Advancing well next we need to check null before calling toList()
This commit is contained in:
		
							parent
							
								
									be22a2a0dd
								
							
						
					
					
						commit
						190ac86bc4
					
				| @ -68,4 +68,22 @@ namespace PoweredSoft.DynamicLinq.Dal.Configurations | ||||
|             HasRequired(t => t.Post).WithMany(t => t.Comments).HasForeignKey(t => t.PostId).WillCascadeOnDelete(false); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public class CommentLikeConfiguration : EntityTypeConfiguration<CommentLike> | ||||
|     { | ||||
|         public CommentLikeConfiguration() : this("dbo") | ||||
|         { | ||||
|         } | ||||
| 
 | ||||
|         public CommentLikeConfiguration(string schema) | ||||
|         { | ||||
|             ToTable("CommentLike", schema); | ||||
|             HasKey(t => t.Id); | ||||
|             Property(t => t.Id).HasColumnName("Id").HasColumnType("bigint").IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); | ||||
|             Property(t => t.CommentId).HasColumnName("CommentId").HasColumnType("bigint").IsRequired(); | ||||
|             Property(t => t.CreateTime).HasColumnName("CreateTime").HasColumnType("datetimeoffset").IsRequired(); | ||||
| 
 | ||||
|             HasRequired(t => t.Comment).WithMany(t => t.CommentLikes).HasForeignKey(t => t.CommentId).WillCascadeOnDelete(false); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -14,5 +14,6 @@ namespace PoweredSoft.DynamicLinq.Dal.Pocos | ||||
|         public string Email { get; set; } | ||||
|         public string CommentText { get; set; } | ||||
|         public Post Post { get; set; } | ||||
|         public ICollection<CommentLike> CommentLikes { get; set; } | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										17
									
								
								PoweredSoft.DynamicLinq.Dal/Pocos/CommentLike.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								PoweredSoft.DynamicLinq.Dal/Pocos/CommentLike.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Text; | ||||
| using System.Threading.Tasks; | ||||
| 
 | ||||
| namespace PoweredSoft.DynamicLinq.Dal.Pocos | ||||
| { | ||||
|     public class CommentLike | ||||
|     { | ||||
|         public long Id { get; set; } | ||||
|         public long CommentId { get; set; } | ||||
|         public DateTimeOffset CreateTime { get; set; } | ||||
| 
 | ||||
|         public virtual Comment Comment { get; set; } | ||||
|     } | ||||
| } | ||||
| @ -50,6 +50,7 @@ | ||||
|   <ItemGroup> | ||||
|     <Compile Include="BlogContext.cs" /> | ||||
|     <Compile Include="Configurations\Configurations.cs" /> | ||||
|     <Compile Include="Pocos\CommentLike.cs" /> | ||||
|     <Compile Include="Pocos\Post.cs" /> | ||||
|     <Compile Include="Pocos\Author.cs" /> | ||||
|     <Compile Include="Pocos\Comment.cs" /> | ||||
|  | ||||
| @ -80,8 +80,20 @@ namespace PoweredSoft.DynamicLinq.Test | ||||
|         [TestMethod] | ||||
|         public void SelectNullChecking() | ||||
|         { | ||||
|             // TODO: null checking in select operations :D | ||||
|             throw new Exception("TODO"); | ||||
|             var query = TestData.Authors.AsQueryable(); | ||||
| 
 | ||||
|             query.Select(t => new | ||||
|             { | ||||
|                 Comments = t.Posts == null ? null : t.Posts.SelectMany(t2 => t2.Comments).ToList() | ||||
|             }); | ||||
| 
 | ||||
|             var querySelect = query.Select(t => | ||||
|             { | ||||
|                 t.NullChecking(true); | ||||
|                 t.PathToList("Posts.Comments", selectCollectionHandling: SelectCollectionHandling.SelectMany); | ||||
|             }); | ||||
| 
 | ||||
|             var list = querySelect.ToDynamicClassList(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -20,13 +20,20 @@ namespace PoweredSoft.DynamicLinq.Fluent | ||||
|         public Type DestinationType { get; set; } | ||||
|         public bool Empty => Parts?.Count == 0; | ||||
|         public IQueryable Query { get; protected set; } | ||||
|         public bool IsNullCheckingEnabled { get; protected set; } | ||||
| 
 | ||||
|         public SelectBuilder(IQueryable query) | ||||
|         { | ||||
|             Query = query; | ||||
|         } | ||||
| 
 | ||||
|         protected void throwIfUsedOrEmpty(string propertyName) | ||||
|         public SelectBuilder NullChecking(bool check = true) | ||||
|         { | ||||
|             IsNullCheckingEnabled = check; | ||||
|             return this; | ||||
|         } | ||||
| 
 | ||||
|         protected void ThrowIfUsedOrEmpty(string propertyName) | ||||
|         { | ||||
|             if (string.IsNullOrEmpty(propertyName)) | ||||
|                 throw new ArgumentNullException($"{propertyName} cannot end up be empty."); | ||||
| @ -40,7 +47,7 @@ namespace PoweredSoft.DynamicLinq.Fluent | ||||
|             if (propertyName == null) | ||||
|                 propertyName = path.Split('.').LastOrDefault(); | ||||
| 
 | ||||
|             throwIfUsedOrEmpty(propertyName); | ||||
|             ThrowIfUsedOrEmpty(propertyName); | ||||
| 
 | ||||
|             Parts.Add(new SelectPart | ||||
|             { | ||||
| @ -57,7 +64,7 @@ namespace PoweredSoft.DynamicLinq.Fluent | ||||
|             if (propertyName == null) | ||||
|                 propertyName = path.Split('.').LastOrDefault(); | ||||
| 
 | ||||
|             throwIfUsedOrEmpty(propertyName); | ||||
|             ThrowIfUsedOrEmpty(propertyName); | ||||
| 
 | ||||
|             Parts.Add(new SelectPart | ||||
|             { | ||||
| @ -71,7 +78,7 @@ namespace PoweredSoft.DynamicLinq.Fluent | ||||
| 
 | ||||
|         public SelectBuilder Count(string propertyName) | ||||
|         { | ||||
|             throwIfUsedOrEmpty(propertyName); | ||||
|             ThrowIfUsedOrEmpty(propertyName); | ||||
|             Parts.Add(new SelectPart | ||||
|             { | ||||
|                 PropertyName = propertyName, | ||||
| @ -82,7 +89,7 @@ namespace PoweredSoft.DynamicLinq.Fluent | ||||
| 
 | ||||
|         public SelectBuilder LongCount(string propertyName) | ||||
|         { | ||||
|             throwIfUsedOrEmpty(propertyName); | ||||
|             ThrowIfUsedOrEmpty(propertyName); | ||||
|             Parts.Add(new SelectPart | ||||
|             { | ||||
|                 PropertyName = propertyName, | ||||
| @ -96,7 +103,7 @@ namespace PoweredSoft.DynamicLinq.Fluent | ||||
|             if (propertyName == null) | ||||
|                 propertyName = path.Split('.').LastOrDefault(); | ||||
| 
 | ||||
|             throwIfUsedOrEmpty(propertyName); | ||||
|             ThrowIfUsedOrEmpty(propertyName); | ||||
| 
 | ||||
|             Parts.Add(new SelectPart | ||||
|             { | ||||
| @ -112,7 +119,7 @@ namespace PoweredSoft.DynamicLinq.Fluent | ||||
|             if (propertyName == null) | ||||
|                 propertyName = path.Split('.').LastOrDefault(); | ||||
| 
 | ||||
|             throwIfUsedOrEmpty(propertyName); | ||||
|             ThrowIfUsedOrEmpty(propertyName); | ||||
| 
 | ||||
|             Parts.Add(new SelectPart | ||||
|             { | ||||
| @ -125,7 +132,7 @@ namespace PoweredSoft.DynamicLinq.Fluent | ||||
| 
 | ||||
|         public SelectBuilder ToList(string propertyName) | ||||
|         { | ||||
|             throwIfUsedOrEmpty(propertyName); | ||||
|             ThrowIfUsedOrEmpty(propertyName); | ||||
|             Parts.Add(new SelectPart | ||||
|             { | ||||
|                 PropertyName = propertyName, | ||||
| @ -139,7 +146,7 @@ namespace PoweredSoft.DynamicLinq.Fluent | ||||
|             if (propertyName == null) | ||||
|                 propertyName = path.Split('.').LastOrDefault(); | ||||
| 
 | ||||
|             throwIfUsedOrEmpty(propertyName); | ||||
|             ThrowIfUsedOrEmpty(propertyName); | ||||
| 
 | ||||
|             Parts.Add(new SelectPart | ||||
|             { | ||||
| @ -158,7 +165,7 @@ namespace PoweredSoft.DynamicLinq.Fluent | ||||
|                 throw new Exception("No select specified, please specify at least one select path"); | ||||
| 
 | ||||
|             var partsTuple = Parts.Select(t => (selectType: t.SelectType, propertyName: t.PropertyName, path: t.Path, selectCollectionHandling: t.SelectCollectionHandling)).ToList(); | ||||
|             return QueryableHelpers.Select(Query, partsTuple, DestinationType); | ||||
|             return QueryableHelpers.Select(Query, partsTuple, DestinationType, nullChecking: IsNullCheckingEnabled); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -118,7 +118,7 @@ namespace PoweredSoft.DynamicLinq.Helpers | ||||
|             return result; | ||||
|         } | ||||
| 
 | ||||
|         public static IQueryable Select(IQueryable query, List<(SelectTypes selectType, string propertyName, string path, SelectCollectionHandling selectCollectionHandling)> parts, Type destinationType = null) | ||||
|         public static IQueryable Select(IQueryable query, List<(SelectTypes selectType, string propertyName, string path, SelectCollectionHandling selectCollectionHandling)> parts, Type destinationType = null, bool nullChecking = false) | ||||
|         { | ||||
|             // create parameter. | ||||
|             var queryType = query.ElementType; | ||||
| @ -129,7 +129,7 @@ namespace PoweredSoft.DynamicLinq.Helpers | ||||
|             var partExpressions = new List<(Expression expression, string propertyName)>(); | ||||
|             parts.ForEach(part => | ||||
|             { | ||||
|                 var partBodyExpression = CreateSelectExpression(query, parameter, part.selectType, part.path, part.selectCollectionHandling); | ||||
|                 var partBodyExpression = CreateSelectExpression(query, parameter, part.selectType, part.path, part.selectCollectionHandling, nullChecking); | ||||
|                 fields.Add((partBodyExpression.Type, part.propertyName)); | ||||
|                 partExpressions.Add((partBodyExpression, part.propertyName)); | ||||
|             }); | ||||
| @ -146,7 +146,7 @@ namespace PoweredSoft.DynamicLinq.Helpers | ||||
|             return result; | ||||
|         } | ||||
| 
 | ||||
|         private static Expression CreateSelectExpressionForGrouping(IQueryable query, ParameterExpression parameter, SelectTypes selectType, string path, SelectCollectionHandling selectCollectionHandling) | ||||
|         private static Expression CreateSelectExpressionForGrouping(IQueryable query, ParameterExpression parameter, SelectTypes selectType, string path, SelectCollectionHandling selectCollectionHandling, bool nullChecking) | ||||
|         { | ||||
|             if (selectType == SelectTypes.Key) | ||||
|             { | ||||
| @ -193,23 +193,23 @@ namespace PoweredSoft.DynamicLinq.Helpers | ||||
|             throw new NotSupportedException($"unkown select type {selectType}"); | ||||
|         } | ||||
| 
 | ||||
|         private static Expression CreateSelectExpression(IQueryable query, ParameterExpression parameter, SelectTypes selectType, string path, SelectCollectionHandling selectCollectionHandling) | ||||
|         private static Expression CreateSelectExpression(IQueryable query, ParameterExpression parameter, SelectTypes selectType, string path, SelectCollectionHandling selectCollectionHandling, bool nullChecking) | ||||
|         { | ||||
|             if (!IsGrouping(query)) | ||||
|                 return CreateSelectExpressionRegular(query, parameter, selectType, path, selectCollectionHandling); | ||||
|                 return CreateSelectExpressionRegular(query, parameter, selectType, path, selectCollectionHandling, nullChecking); | ||||
| 
 | ||||
|             return CreateSelectExpressionForGrouping(query, parameter, selectType, path, selectCollectionHandling);             | ||||
|             return CreateSelectExpressionForGrouping(query, parameter, selectType, path, selectCollectionHandling, nullChecking);             | ||||
|         } | ||||
| 
 | ||||
|         private static Expression CreateSelectExpressionRegular(IQueryable query, ParameterExpression parameter, SelectTypes selectType, string path, SelectCollectionHandling selectCollectionHandling) | ||||
|         private static Expression CreateSelectExpressionRegular(IQueryable query, ParameterExpression parameter, SelectTypes selectType, string path, SelectCollectionHandling selectCollectionHandling, bool nullChecking) | ||||
|         { | ||||
|             if (selectType == SelectTypes.Path) | ||||
|             { | ||||
|                 return ResolvePathForExpression(parameter, path, selectCollectionHandling); | ||||
|                 return ResolvePathForExpression(parameter, path, selectCollectionHandling, nullChecking); | ||||
|             } | ||||
|             else if (selectType == SelectTypes.PathToList) | ||||
|             { | ||||
|                 var expr = ResolvePathForExpression(parameter, path, selectCollectionHandling); | ||||
|                 var expr = ResolvePathForExpression(parameter, path, selectCollectionHandling, nullChecking); | ||||
|                 var notGroupedType = expr.Type.GenericTypeArguments.FirstOrDefault(); | ||||
|                 if (notGroupedType == null) | ||||
|                     throw new Exception($"Path must be a Enumerable<T> but its a {expr.Type}"); | ||||
| @ -241,7 +241,7 @@ namespace PoweredSoft.DynamicLinq.Helpers | ||||
|             return result; | ||||
|         } | ||||
| 
 | ||||
|         internal static Expression InternalResolvePathExpression(ParameterExpression param, List<string> parts, SelectCollectionHandling selectCollectionHandling) | ||||
|         internal static Expression InternalResolvePathExpression(int step, ParameterExpression param, List<string> parts, SelectCollectionHandling selectCollectionHandling, bool nullChecking) | ||||
|         { | ||||
|             var isLast = parts.Count == 1; | ||||
|             var currentPart = parts.First(); | ||||
| @ -251,21 +251,41 @@ namespace PoweredSoft.DynamicLinq.Helpers | ||||
|             if (isLast) | ||||
|                 return memberExpression; | ||||
| 
 | ||||
|             Expression ret = null; | ||||
| 
 | ||||
|             if (!IsEnumerable(memberExpression)) | ||||
|                 return InternalResolvePathExpression(param, parts.Skip(1).ToList(), selectCollectionHandling); | ||||
|             { | ||||
|                 // TODO: null checking here too. | ||||
|                 // should be easier then collection :=| | ||||
| 
 | ||||
|             // enumerable. | ||||
|             var listGenericArgumentType = memberExpression.Type.GetGenericArguments().First(); | ||||
|                 ret = InternalResolvePathExpression(step + 1, param, parts.Skip(1).ToList(), selectCollectionHandling, nullChecking); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 // enumerable. | ||||
|                 var listGenericArgumentType = memberExpression.Type.GetGenericArguments().First(); | ||||
| 
 | ||||
|             // sub param. | ||||
|             var innerParam = Expression.Parameter(listGenericArgumentType); | ||||
|             var innerExpression = InternalResolvePathExpression(innerParam, parts.Skip(1).ToList(), selectCollectionHandling); | ||||
|             var lambda = Expression.Lambda(innerExpression, innerParam); | ||||
|                 // sub param. | ||||
|                 var innerParam = Expression.Parameter(listGenericArgumentType); | ||||
|                 var innerExpression = InternalResolvePathExpression(step + 1, innerParam, parts.Skip(1).ToList(), selectCollectionHandling, nullChecking); | ||||
|                 var lambda = Expression.Lambda(innerExpression, innerParam); | ||||
| 
 | ||||
|             if (selectCollectionHandling == SelectCollectionHandling.Select) | ||||
|                 return Expression.Call(typeof(Enumerable), "Select", new Type[] { listGenericArgumentType, innerExpression.Type }, memberExpression, lambda); | ||||
|                 if (selectCollectionHandling == SelectCollectionHandling.Select) | ||||
|                     ret = Expression.Call(typeof(Enumerable), "Select", new Type[] { listGenericArgumentType, innerExpression.Type }, memberExpression, lambda); | ||||
|                 else | ||||
|                     ret = Expression.Call(typeof(Enumerable), "SelectMany", new Type[] { listGenericArgumentType, innerExpression.Type.GenericTypeArguments.First() }, memberExpression, lambda); | ||||
|             } | ||||
| 
 | ||||
|             return Expression.Call(typeof(Enumerable), "SelectMany", new Type[] { listGenericArgumentType, innerExpression.Type.GenericTypeArguments.First() }, memberExpression, lambda); | ||||
|             if (nullChecking) | ||||
|             { | ||||
|                 ret = Expression.Condition( | ||||
|                     Expression.Equal(memberExpression, Expression.Constant(null)), | ||||
|                     Expression.Constant(null, ret.Type), | ||||
|                     ret | ||||
|                 ); | ||||
|             } | ||||
| 
 | ||||
|             return ret;             | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
| @ -274,10 +294,10 @@ namespace PoweredSoft.DynamicLinq.Helpers | ||||
|         /// <param name="param">Expression.Parameter(typeOfClassOrInterface)</param> | ||||
|         /// <param name="path">the path you wish to resolve example Contact.Profile.FirstName</param> | ||||
|         /// <returns></returns> | ||||
|         public static Expression ResolvePathForExpression(ParameterExpression param, string path, SelectCollectionHandling selectCollectionHandling = SelectCollectionHandling.Select) | ||||
|         public static Expression ResolvePathForExpression(ParameterExpression param, string path, SelectCollectionHandling selectCollectionHandling = SelectCollectionHandling.Select, bool nullChecking = false) | ||||
|         { | ||||
|             var parts = path.Split('.').ToList(); | ||||
|             return InternalResolvePathExpression(param, parts, selectCollectionHandling); | ||||
|             return InternalResolvePathExpression(1, param, parts, selectCollectionHandling, nullChecking); | ||||
|         } | ||||
| 
 | ||||
|         public static ConstantExpression GetConstantSameAsLeftOperator(Expression member, object value) | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user