creating anonymous types :)
This commit is contained in:
		
							parent
							
								
									64d60b6942
								
							
						
					
					
						commit
						7278dea56d
					
				| @ -1,7 +1,4 @@ | ||||
| using Microsoft.VisualStudio.TestTools.UnitTesting; | ||||
| using PoweredSoft.DynamicLinq.Dal.Pocos; | ||||
| using PoweredSoft.DynamicLinq.Fluent; | ||||
| using PoweredSoft.DynamicLinq.Helpers; | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Data.SqlClient; | ||||
| @ -10,6 +7,9 @@ using System.Linq.Expressions; | ||||
| using System.Reflection; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
| using PoweredSoft.DynamicLinq; | ||||
| using PoweredSoft.DynamicLinq.Dal.Pocos; | ||||
| using PoweredSoft.DynamicLinq.Fluent; | ||||
| 
 | ||||
| namespace PoweredSoft.DynamicLinq.Test | ||||
| { | ||||
| @ -82,7 +82,7 @@ namespace PoweredSoft.DynamicLinq.Test | ||||
| 
 | ||||
|             // the query. | ||||
|             var query = posts.AsQueryable(); | ||||
|             var queryBuilder = new QueryBuilder<Post>(query); | ||||
|             var queryBuilder = new PoweredSoft.DynamicLinq.Fluent.QueryBuilder<Post>(query); | ||||
| 
 | ||||
|             // add some sorting. | ||||
|             queryBuilder | ||||
|  | ||||
| @ -35,9 +35,8 @@ namespace PoweredSoft.DynamicLinq.Test | ||||
| 
 | ||||
|                 Assert.Fail("Should have thrown an exception"); | ||||
|             } | ||||
|             catch(Exception ex) | ||||
|             catch | ||||
|             { | ||||
|                  | ||||
|             } | ||||
| 
 | ||||
|             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.Text; | ||||
| using System.Threading.Tasks; | ||||
| using PoweredSoft.DynamicLinq; | ||||
| 
 | ||||
| namespace PoweredSoft.DynamicLinq.Test | ||||
| { | ||||
| @ -12,7 +13,7 @@ namespace PoweredSoft.DynamicLinq.Test | ||||
|     { | ||||
|         [TestMethod] | ||||
|         public void WantedSyntax() | ||||
|         { | ||||
|         {/* | ||||
|             var regularSyntax = TestData.Sales | ||||
|                 .GroupBy(t => t.ClientId); | ||||
| 
 | ||||
| @ -20,18 +21,18 @@ namespace PoweredSoft.DynamicLinq.Test | ||||
|                 .AsQueryable() | ||||
|                 .GroupBy("ClientId"); | ||||
| 
 | ||||
|             /* | ||||
|              | ||||
| 
 | ||||
|             var regularSyntax2 = TestData.Sales | ||||
|                 .GroupBy(t => new | ||||
|                 { | ||||
|                     t.ClientId, | ||||
|                     t.NetSales | ||||
|                 }); | ||||
|                     B = t.NetSales | ||||
|                 });*/ | ||||
| 
 | ||||
|             var dynamicSyntax2 = TestData.Sales | ||||
|                 .AsQueryable() | ||||
|                 .GroupBy("ClientId", "NetSales");*/ | ||||
|                 .GroupBy(t => t.Path("ClientId").Path("NetSales", "B")); | ||||
| 
 | ||||
|             /* | ||||
|             .Select(t => new | ||||
|  | ||||
| @ -69,18 +69,24 @@ namespace PoweredSoft.DynamicLinq | ||||
|             var ret = qb.Build(); | ||||
|             return ret; | ||||
|         } | ||||
|    | ||||
| 
 | ||||
|         public static IQueryable GroupBy<T>(this IQueryable<T> query, string path) | ||||
|             where T : class | ||||
|         { | ||||
|             var ret = query.GroupBy(typeof(T), path);  | ||||
|             return ret as IQueryable; | ||||
|         } | ||||
|             => QueryableHelpers.GroupBy(query, typeof(T), 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); | ||||
|             return ret; | ||||
|             var groupBuilder = new GroupBuilder(); | ||||
|             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.Text; | ||||
| using System.Threading.Tasks; | ||||
| using System.Reflection.Emit; | ||||
| 
 | ||||
| 
 | ||||
| namespace PoweredSoft.DynamicLinq.Helpers | ||||
| { | ||||
| @ -77,6 +77,28 @@ namespace PoweredSoft.DynamicLinq.Helpers | ||||
|             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) | ||||
|         { | ||||
|             var parameter = Expression.Parameter(type, "t"); | ||||
| @ -88,31 +110,6 @@ namespace PoweredSoft.DynamicLinq.Helpers | ||||
|             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> | ||||
|         /// Returns the right expression for a path supplied. | ||||
|         /// </summary> | ||||
|  | ||||
| @ -3,11 +3,17 @@ using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
| using System.Reflection.Emit; | ||||
| using System.Reflection; | ||||
| 
 | ||||
| namespace PoweredSoft.DynamicLinq.Helpers | ||||
| { | ||||
|     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) | ||||
|         { | ||||
|             object ret = null; | ||||
| @ -28,5 +34,45 @@ namespace PoweredSoft.DynamicLinq.Helpers | ||||
|             ret = Convert.ChangeType(source, notNullableType); | ||||
|             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\" /> | ||||
|   </ItemGroup> | ||||
| 
 | ||||
|   <ItemGroup> | ||||
|     <PackageReference Include="System.Reflection.Emit" Version="4.3.0" /> | ||||
|   </ItemGroup> | ||||
| 
 | ||||
| </Project> | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user