creating anonymous types :)

This commit is contained in:
David Lebée 2018-03-08 23:22:12 -06:00
parent 64d60b6942
commit 7278dea56d
13 changed files with 132 additions and 45 deletions

View File

@ -1,7 +1,4 @@
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using PoweredSoft.DynamicLinq.Dal.Pocos;
using PoweredSoft.DynamicLinq.Fluent;
using PoweredSoft.DynamicLinq.Helpers;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data.SqlClient; using System.Data.SqlClient;
@ -10,6 +7,9 @@ using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using PoweredSoft.DynamicLinq;
using PoweredSoft.DynamicLinq.Dal.Pocos;
using PoweredSoft.DynamicLinq.Fluent;
namespace PoweredSoft.DynamicLinq.Test namespace PoweredSoft.DynamicLinq.Test
{ {
@ -82,7 +82,7 @@ namespace PoweredSoft.DynamicLinq.Test
// the query. // the query.
var query = posts.AsQueryable(); var query = posts.AsQueryable();
var queryBuilder = new QueryBuilder<Post>(query); var queryBuilder = new PoweredSoft.DynamicLinq.Fluent.QueryBuilder<Post>(query);
// add some sorting. // add some sorting.
queryBuilder queryBuilder

View File

@ -35,9 +35,8 @@ namespace PoweredSoft.DynamicLinq.Test
Assert.Fail("Should have thrown an exception"); Assert.Fail("Should have thrown an exception");
} }
catch(Exception ex) catch
{ {
} }
Assert.IsTrue(Posts.AsQueryable().Query(t => t.Equal("Id", 1, QueryConvertStrategy.LeaveAsIs)).Any()); Assert.IsTrue(Posts.AsQueryable().Query(t => t.Equal("Id", 1, QueryConvertStrategy.LeaveAsIs)).Any());

View File

@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using PoweredSoft.DynamicLinq;
namespace PoweredSoft.DynamicLinq.Test namespace PoweredSoft.DynamicLinq.Test
{ {
@ -12,7 +13,7 @@ namespace PoweredSoft.DynamicLinq.Test
{ {
[TestMethod] [TestMethod]
public void WantedSyntax() public void WantedSyntax()
{ {/*
var regularSyntax = TestData.Sales var regularSyntax = TestData.Sales
.GroupBy(t => t.ClientId); .GroupBy(t => t.ClientId);
@ -20,18 +21,18 @@ namespace PoweredSoft.DynamicLinq.Test
.AsQueryable() .AsQueryable()
.GroupBy("ClientId"); .GroupBy("ClientId");
/*
var regularSyntax2 = TestData.Sales var regularSyntax2 = TestData.Sales
.GroupBy(t => new .GroupBy(t => new
{ {
t.ClientId, t.ClientId,
t.NetSales B = t.NetSales
}); });*/
var dynamicSyntax2 = TestData.Sales var dynamicSyntax2 = TestData.Sales
.AsQueryable() .AsQueryable()
.GroupBy("ClientId", "NetSales");*/ .GroupBy(t => t.Path("ClientId").Path("NetSales", "B"));
/* /*
.Select(t => new .Select(t => new

View File

@ -71,16 +71,22 @@ namespace PoweredSoft.DynamicLinq
} }
public static IQueryable GroupBy<T>(this IQueryable<T> query, string path) public static IQueryable GroupBy<T>(this IQueryable<T> query, string path)
where T : class => QueryableHelpers.GroupBy(query, typeof(T), path);
{
var ret = query.GroupBy(typeof(T), path);
return ret as IQueryable;
}
public static IQueryable GroupBy(this IQueryable query, Type type, string path) public static IQueryable GroupBy(this IQueryable query, Type type, string path)
=> QueryableHelpers.GroupBy(query, type, path);
public static IQueryable GroupBy<T>(this IQueryable<T> query, Action<GroupBuilder> callback)
=> query.GroupBy(typeof(T), callback);
public static IQueryable GroupBy(this IQueryable query, Type type, Action<GroupBuilder> callback)
{ {
var ret = QueryableHelpers.GroupBy(query, type, path); var groupBuilder = new GroupBuilder();
return ret; callback(groupBuilder);
if (groupBuilder.Empty)
throw new Exception("No group specified, please specify at least one group");
return QueryableHelpers.GroupBy(query, type, groupBuilder.Parts);
} }
} }
} }

View File

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
namespace PoweredSoft.DynamicLinq.Fluent
{
public class GroupBuilder
{
public List<(string path, string propertyName)> Parts { get; set; } = new List<(string path, string propertyName)>();
public bool Empty => !Parts.Any();
public GroupBuilder Path(string path, string propertyName = null)
{
if (propertyName == null)
{
var name = path;
if (name.Contains("."))
{
var parts = name.Split('.');
name = parts[parts.Length - 1]; // the last one.
}
if (Parts.Any(t => t.propertyName == name))
throw new Exception($"{name} is already taken by another group part, you can specify a property name instead to resolve this issue");
propertyName = name;
}
Parts.Add((path, propertyName));
return this;
}
}
}

View File

@ -6,7 +6,7 @@ using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Reflection.Emit;
namespace PoweredSoft.DynamicLinq.Helpers namespace PoweredSoft.DynamicLinq.Helpers
{ {
@ -77,6 +77,28 @@ namespace PoweredSoft.DynamicLinq.Helpers
return ret; return ret;
} }
internal static IQueryable GroupBy(IQueryable query, Type type, List<(string path, string propertyName)> parts)
{
// EXPRESSION
var parameter = Expression.Parameter(type, "t");
var partExpressions = new List<Expression>();
var fields = new List<(Type type, string propertyName)>();
// resolve part expression and create the fields inside the anonymous type.
parts.ForEach(part =>
{
var partExpression = ResolvePathForExpression(parameter, part.path);
fields.Add((partExpression.Type, part.propertyName));
partExpressions.Add(partExpression);
});
var anonymousType = TypeHelpers.CreateSimpleAnonymousType(fields);
return query;
}
public static IQueryable GroupBy(IQueryable query, Type type, string path) public static IQueryable GroupBy(IQueryable query, Type type, string path)
{ {
var parameter = Expression.Parameter(type, "t"); var parameter = Expression.Parameter(type, "t");
@ -88,31 +110,6 @@ namespace PoweredSoft.DynamicLinq.Helpers
return result; return result;
} }
private static IQueryable GroupByAnonymousObject(IQueryable query, Type type, ParameterExpression parameter, List<Expression> fields)
{
throw new NotSupportedException();
/*
var dynamicAssemblyName = new AssemblyName("PoweredSoft.DynamicLinq.DynamicTypes");
var dynamicAssembly = AssemblyBuilder.DefineDynamicAssembly(dynamicAssemblyName, System.Reflection.Emit.AssemblyBuilderAccess.Run);
System.Reflection.Emit.ModuleBuilder dynamicModule = dynamicAssembly.DefineDynamicModule("TempAssembly");
TypeBuilder dynamicAnonymousType = dynamicModule.DefineType("AnonymousType", TypeAttributes.Public);
int i = 0;
fields.ForEach(field =>
{
var fieldName = $"A_{++i}"; // tODO
dynamicAnonymousType.DefineField(fieldName.
});
dynamicAnonymousType.DefineField(, typeof(TFieldA), FieldAttributes.Public);
dynamicAnonymousType.DefineField(fieldNameB, typeof(TFieldB), FieldAttributes.Public);
return dynamicAnonymousType.CreateType();
throw new NotImplementedException(); */
}
/// <summary> /// <summary>
/// Returns the right expression for a path supplied. /// Returns the right expression for a path supplied.
/// </summary> /// </summary>

View File

@ -3,11 +3,17 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Reflection.Emit;
using System.Reflection;
namespace PoweredSoft.DynamicLinq.Helpers namespace PoweredSoft.DynamicLinq.Helpers
{ {
public static class TypeHelpers public static class TypeHelpers
{ {
internal static Lazy<AssemblyName> DynamicAssemblyName = new Lazy<AssemblyName>(() => new AssemblyName("PoweredSoft.DynamicLinq.DynamicTypes"));
internal static Lazy<AssemblyBuilder> DynamicAssembly = new Lazy<AssemblyBuilder>(() => AssemblyBuilder.DefineDynamicAssembly(DynamicAssemblyName.Value, AssemblyBuilderAccess.Run));
internal static Lazy<ModuleBuilder> DynamicModule = new Lazy<ModuleBuilder>(() => DynamicAssembly.Value.DefineDynamicModule("PoweredSoft.DynamicLinq.DynamicTypes"));
public static object ConvertFrom(Type type, object source) public static object ConvertFrom(Type type, object source)
{ {
object ret = null; object ret = null;
@ -28,5 +34,45 @@ namespace PoweredSoft.DynamicLinq.Helpers
ret = Convert.ChangeType(source, notNullableType); ret = Convert.ChangeType(source, notNullableType);
return ret; return ret;
} }
internal static TypeInfo CreateSimpleAnonymousType(List<(Type type, string name)> fields)
{
// DYNAMIC TYPE CREATION
var typeName = $"PSDLProxy_{Guid.NewGuid().ToString("N")}";
var dynamicType = DynamicModule.Value.DefineType(typeName, TypeAttributes.Class | TypeAttributes.Public);
fields.ForEach(field =>
{
CreatePropertyOnType(dynamicType, field.name, field.type);
});
var ret = dynamicType.CreateTypeInfo();
return ret;
}
internal static void CreatePropertyOnType(TypeBuilder typeBuilder, string propertyName, Type propertyType)
{
// Generate a property called "Name"
var field = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
var attributes = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
// generate property
var propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
// Generate getter method
var getter = typeBuilder.DefineMethod("get_" + propertyName, attributes, propertyType, Type.EmptyTypes);
var il = getter.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // Push "this" on the stack
il.Emit(OpCodes.Ldfld, field); // Load the field "_Name"
il.Emit(OpCodes.Ret); // Return
propertyBuilder.SetGetMethod(getter);
// Generate setter method
var setter = typeBuilder.DefineMethod("set_" + propertyName, attributes, null, new[] { propertyType });
il = setter.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // Push "this" on the stack
il.Emit(OpCodes.Ldarg_1); // Push "value" on the stack
il.Emit(OpCodes.Stfld, field); // Set the field "_Name" to "value"
il.Emit(OpCodes.Ret); // Return
propertyBuilder.SetSetMethod(setter);
}
} }
} }

View File

@ -21,4 +21,8 @@
<Folder Include="Properties\" /> <Folder Include="Properties\" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="System.Reflection.Emit" Version="4.3.0" />
</ItemGroup>
</Project> </Project>