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);
|
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 Email { get; set; }
|
||||||
public string CommentText { get; set; }
|
public string CommentText { get; set; }
|
||||||
public Post Post { 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>
|
<ItemGroup>
|
||||||
<Compile Include="BlogContext.cs" />
|
<Compile Include="BlogContext.cs" />
|
||||||
<Compile Include="Configurations\Configurations.cs" />
|
<Compile Include="Configurations\Configurations.cs" />
|
||||||
|
<Compile Include="Pocos\CommentLike.cs" />
|
||||||
<Compile Include="Pocos\Post.cs" />
|
<Compile Include="Pocos\Post.cs" />
|
||||||
<Compile Include="Pocos\Author.cs" />
|
<Compile Include="Pocos\Author.cs" />
|
||||||
<Compile Include="Pocos\Comment.cs" />
|
<Compile Include="Pocos\Comment.cs" />
|
||||||
|
@ -80,8 +80,20 @@ namespace PoweredSoft.DynamicLinq.Test
|
|||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void SelectNullChecking()
|
public void SelectNullChecking()
|
||||||
{
|
{
|
||||||
// TODO: null checking in select operations :D
|
var query = TestData.Authors.AsQueryable();
|
||||||
throw new Exception("TODO");
|
|
||||||
|
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 Type DestinationType { get; set; }
|
||||||
public bool Empty => Parts?.Count == 0;
|
public bool Empty => Parts?.Count == 0;
|
||||||
public IQueryable Query { get; protected set; }
|
public IQueryable Query { get; protected set; }
|
||||||
|
public bool IsNullCheckingEnabled { get; protected set; }
|
||||||
|
|
||||||
public SelectBuilder(IQueryable query)
|
public SelectBuilder(IQueryable query)
|
||||||
{
|
{
|
||||||
Query = 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))
|
if (string.IsNullOrEmpty(propertyName))
|
||||||
throw new ArgumentNullException($"{propertyName} cannot end up be empty.");
|
throw new ArgumentNullException($"{propertyName} cannot end up be empty.");
|
||||||
@ -40,7 +47,7 @@ namespace PoweredSoft.DynamicLinq.Fluent
|
|||||||
if (propertyName == null)
|
if (propertyName == null)
|
||||||
propertyName = path.Split('.').LastOrDefault();
|
propertyName = path.Split('.').LastOrDefault();
|
||||||
|
|
||||||
throwIfUsedOrEmpty(propertyName);
|
ThrowIfUsedOrEmpty(propertyName);
|
||||||
|
|
||||||
Parts.Add(new SelectPart
|
Parts.Add(new SelectPart
|
||||||
{
|
{
|
||||||
@ -57,7 +64,7 @@ namespace PoweredSoft.DynamicLinq.Fluent
|
|||||||
if (propertyName == null)
|
if (propertyName == null)
|
||||||
propertyName = path.Split('.').LastOrDefault();
|
propertyName = path.Split('.').LastOrDefault();
|
||||||
|
|
||||||
throwIfUsedOrEmpty(propertyName);
|
ThrowIfUsedOrEmpty(propertyName);
|
||||||
|
|
||||||
Parts.Add(new SelectPart
|
Parts.Add(new SelectPart
|
||||||
{
|
{
|
||||||
@ -71,7 +78,7 @@ namespace PoweredSoft.DynamicLinq.Fluent
|
|||||||
|
|
||||||
public SelectBuilder Count(string propertyName)
|
public SelectBuilder Count(string propertyName)
|
||||||
{
|
{
|
||||||
throwIfUsedOrEmpty(propertyName);
|
ThrowIfUsedOrEmpty(propertyName);
|
||||||
Parts.Add(new SelectPart
|
Parts.Add(new SelectPart
|
||||||
{
|
{
|
||||||
PropertyName = propertyName,
|
PropertyName = propertyName,
|
||||||
@ -82,7 +89,7 @@ namespace PoweredSoft.DynamicLinq.Fluent
|
|||||||
|
|
||||||
public SelectBuilder LongCount(string propertyName)
|
public SelectBuilder LongCount(string propertyName)
|
||||||
{
|
{
|
||||||
throwIfUsedOrEmpty(propertyName);
|
ThrowIfUsedOrEmpty(propertyName);
|
||||||
Parts.Add(new SelectPart
|
Parts.Add(new SelectPart
|
||||||
{
|
{
|
||||||
PropertyName = propertyName,
|
PropertyName = propertyName,
|
||||||
@ -96,7 +103,7 @@ namespace PoweredSoft.DynamicLinq.Fluent
|
|||||||
if (propertyName == null)
|
if (propertyName == null)
|
||||||
propertyName = path.Split('.').LastOrDefault();
|
propertyName = path.Split('.').LastOrDefault();
|
||||||
|
|
||||||
throwIfUsedOrEmpty(propertyName);
|
ThrowIfUsedOrEmpty(propertyName);
|
||||||
|
|
||||||
Parts.Add(new SelectPart
|
Parts.Add(new SelectPart
|
||||||
{
|
{
|
||||||
@ -112,7 +119,7 @@ namespace PoweredSoft.DynamicLinq.Fluent
|
|||||||
if (propertyName == null)
|
if (propertyName == null)
|
||||||
propertyName = path.Split('.').LastOrDefault();
|
propertyName = path.Split('.').LastOrDefault();
|
||||||
|
|
||||||
throwIfUsedOrEmpty(propertyName);
|
ThrowIfUsedOrEmpty(propertyName);
|
||||||
|
|
||||||
Parts.Add(new SelectPart
|
Parts.Add(new SelectPart
|
||||||
{
|
{
|
||||||
@ -125,7 +132,7 @@ namespace PoweredSoft.DynamicLinq.Fluent
|
|||||||
|
|
||||||
public SelectBuilder ToList(string propertyName)
|
public SelectBuilder ToList(string propertyName)
|
||||||
{
|
{
|
||||||
throwIfUsedOrEmpty(propertyName);
|
ThrowIfUsedOrEmpty(propertyName);
|
||||||
Parts.Add(new SelectPart
|
Parts.Add(new SelectPart
|
||||||
{
|
{
|
||||||
PropertyName = propertyName,
|
PropertyName = propertyName,
|
||||||
@ -139,7 +146,7 @@ namespace PoweredSoft.DynamicLinq.Fluent
|
|||||||
if (propertyName == null)
|
if (propertyName == null)
|
||||||
propertyName = path.Split('.').LastOrDefault();
|
propertyName = path.Split('.').LastOrDefault();
|
||||||
|
|
||||||
throwIfUsedOrEmpty(propertyName);
|
ThrowIfUsedOrEmpty(propertyName);
|
||||||
|
|
||||||
Parts.Add(new SelectPart
|
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");
|
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();
|
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;
|
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.
|
// create parameter.
|
||||||
var queryType = query.ElementType;
|
var queryType = query.ElementType;
|
||||||
@ -129,7 +129,7 @@ namespace PoweredSoft.DynamicLinq.Helpers
|
|||||||
var partExpressions = new List<(Expression expression, string propertyName)>();
|
var partExpressions = new List<(Expression expression, string propertyName)>();
|
||||||
parts.ForEach(part =>
|
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));
|
fields.Add((partBodyExpression.Type, part.propertyName));
|
||||||
partExpressions.Add((partBodyExpression, part.propertyName));
|
partExpressions.Add((partBodyExpression, part.propertyName));
|
||||||
});
|
});
|
||||||
@ -146,7 +146,7 @@ namespace PoweredSoft.DynamicLinq.Helpers
|
|||||||
return result;
|
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)
|
if (selectType == SelectTypes.Key)
|
||||||
{
|
{
|
||||||
@ -193,23 +193,23 @@ namespace PoweredSoft.DynamicLinq.Helpers
|
|||||||
throw new NotSupportedException($"unkown select type {selectType}");
|
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))
|
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)
|
if (selectType == SelectTypes.Path)
|
||||||
{
|
{
|
||||||
return ResolvePathForExpression(parameter, path, selectCollectionHandling);
|
return ResolvePathForExpression(parameter, path, selectCollectionHandling, nullChecking);
|
||||||
}
|
}
|
||||||
else if (selectType == SelectTypes.PathToList)
|
else if (selectType == SelectTypes.PathToList)
|
||||||
{
|
{
|
||||||
var expr = ResolvePathForExpression(parameter, path, selectCollectionHandling);
|
var expr = ResolvePathForExpression(parameter, path, selectCollectionHandling, nullChecking);
|
||||||
var notGroupedType = expr.Type.GenericTypeArguments.FirstOrDefault();
|
var notGroupedType = expr.Type.GenericTypeArguments.FirstOrDefault();
|
||||||
if (notGroupedType == null)
|
if (notGroupedType == null)
|
||||||
throw new Exception($"Path must be a Enumerable<T> but its a {expr.Type}");
|
throw new Exception($"Path must be a Enumerable<T> but its a {expr.Type}");
|
||||||
@ -241,7 +241,7 @@ namespace PoweredSoft.DynamicLinq.Helpers
|
|||||||
return result;
|
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 isLast = parts.Count == 1;
|
||||||
var currentPart = parts.First();
|
var currentPart = parts.First();
|
||||||
@ -251,21 +251,41 @@ namespace PoweredSoft.DynamicLinq.Helpers
|
|||||||
if (isLast)
|
if (isLast)
|
||||||
return memberExpression;
|
return memberExpression;
|
||||||
|
|
||||||
|
Expression ret = null;
|
||||||
|
|
||||||
if (!IsEnumerable(memberExpression))
|
if (!IsEnumerable(memberExpression))
|
||||||
return InternalResolvePathExpression(param, parts.Skip(1).ToList(), selectCollectionHandling);
|
{
|
||||||
|
// TODO: null checking here too.
|
||||||
|
// should be easier then collection :=|
|
||||||
|
|
||||||
// enumerable.
|
ret = InternalResolvePathExpression(step + 1, param, parts.Skip(1).ToList(), selectCollectionHandling, nullChecking);
|
||||||
var listGenericArgumentType = memberExpression.Type.GetGenericArguments().First();
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// enumerable.
|
||||||
|
var listGenericArgumentType = memberExpression.Type.GetGenericArguments().First();
|
||||||
|
|
||||||
// sub param.
|
// sub param.
|
||||||
var innerParam = Expression.Parameter(listGenericArgumentType);
|
var innerParam = Expression.Parameter(listGenericArgumentType);
|
||||||
var innerExpression = InternalResolvePathExpression(innerParam, parts.Skip(1).ToList(), selectCollectionHandling);
|
var innerExpression = InternalResolvePathExpression(step + 1, innerParam, parts.Skip(1).ToList(), selectCollectionHandling, nullChecking);
|
||||||
var lambda = Expression.Lambda(innerExpression, innerParam);
|
var lambda = Expression.Lambda(innerExpression, innerParam);
|
||||||
|
|
||||||
if (selectCollectionHandling == SelectCollectionHandling.Select)
|
if (selectCollectionHandling == SelectCollectionHandling.Select)
|
||||||
return Expression.Call(typeof(Enumerable), "Select", new Type[] { listGenericArgumentType, innerExpression.Type }, memberExpression, lambda);
|
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>
|
/// <summary>
|
||||||
@ -274,10 +294,10 @@ namespace PoweredSoft.DynamicLinq.Helpers
|
|||||||
/// <param name="param">Expression.Parameter(typeOfClassOrInterface)</param>
|
/// <param name="param">Expression.Parameter(typeOfClassOrInterface)</param>
|
||||||
/// <param name="path">the path you wish to resolve example Contact.Profile.FirstName</param>
|
/// <param name="path">the path you wish to resolve example Contact.Profile.FirstName</param>
|
||||||
/// <returns></returns>
|
/// <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();
|
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)
|
public static ConstantExpression GetConstantSameAsLeftOperator(Expression member, object value)
|
||||||
|
Loading…
Reference in New Issue
Block a user