creating anonymous types :)
This commit is contained in:
parent
64d60b6942
commit
7278dea56d
@ -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
|
||||||
|
@ -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());
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
34
PoweredSoft.DynamicLinq/Fluent/Group/GroupBuilder.cs
Normal file
34
PoweredSoft.DynamicLinq/Fluent/Group/GroupBuilder.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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>
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user