我正在尝试为具有明确的子母关系的实体创建通用存储库。 F.e.带AuditLogs的AuditLogEntries。
public class BaseWithParentRepository<TEntity, TKey, TParentKey> : BaseRepository<TEntity, TKey>, IRepositoryWithParent<TEntity, TKey, TParentKey> where TEntity : class, IDbEntityWithKey<TKey>, new()
{
public Func<TEntity, TParentKey> ParentKeySelector { get; set; }
public BaseWithParentRepository(IDbContext context, Func<TEntity, TParentKey> parentKeySelector) : base(context)
{
ParentKeySelector = parentKeySelector;
}
public IQueryable<TEntity> QueryByParentKey(TParentKey parentKey)
{
IEnumerable<TParentKey> parentIds = new TParentKey[] { parentKey };
return Query.Where(e => parentIds.Contains(ParentKeySelector(e)));
}
}
AuditLogDetail类为:
public class AuditLogDetail : BaseDbEntity<Guid>
{
public Guid AuditLogId { get; set; }
public virtual AuditLog AuditLog { get; set; }
public string Field { get; set; }
public string OldValue { get; set; }
public string NewValue { get; set; }
}
AuditLogDetails存储库为:
public class AuditLogDetailRepository : BaseWithParentRepository<AuditLogDetail, Guid, Guid>, IAuditLogDetailRepository
{
public AuditLogDetailRepository(IDbContext context) : base(context, ald => ald.AuditLogId)
{
}
}
如您所见,BaseWithParentRepository
的构造函数的第二个参数是属性选择器,方法QueryByParentKey
尝试获取父实体(AuditLog)的所有AuditLogDetails。
但是,当调用QueryByParentKey
方法时,将引发以下异常:
Error: System.NotSupportedException: The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.NotSupportedTranslator.Translate(ExpressionConverter parent, Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.ContainsTranslator.TranslateContains(ExpressionConverter parent, Expression sourceExpression, Expression valueExpression)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.ContainsTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input, DbExpressionBinding& binding)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, DbExpression& source, DbExpressionBinding& sourceBinding, DbExpression& lambda)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.Convert()
at System.Data.Entity.Core.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable`1 forMergeOption)
at System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClass41_0.<GetResults>b__1()
at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func`1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)
at System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClass41_0.<GetResults>b__0()
at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1 operation)
at System.Data.Entity.Core.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption)
at System.Data.Entity.Core.Objects.ObjectQuery`1.<System.Collections.Generic.IEnumerable<T>.GetEnumerator>b__31_0()
at System.Data.Entity.Internal.LazyEnumerator`1.MoveNext()
at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at Manu.Data.BaseWithParentRepository`3.GetAllByParentKey(TParentKey parentKey)
我了解在ParentKeySelector
中调用Contains
不能转换为SQL。因此,似乎我应该使用Expression<Func<TEntity, TParentKey>>
而不是Func<TEntity, TParentKey>
来完成我想要的事情,但是我不知道如何实现我的目标。可以对表达式更熟悉的人帮我吗?
您正确的事实是,不能将ParentKeySelector转换为SQL的事实。由于无法将其转换为SQL,因此EF不可能将linq转换为SQL以供DB服务器执行,无论您如何操作。
尚不清楚您要完成什么。但是,如果父/子关系是显式的,则您应该能够定义外键引用并使EF自动自动解析它们,请参见EF tutorial。