如何在不计算 IQueryable 的情况下将 LINQ 中的包含用于具有任意属性选择的实体?



我正在尝试为具有明确子父关系的实体创建一个通用存储库。 F.e. AuditLogEntry with AuditLogs.

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)));
}
}

类是:

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)

我知道在Contains中调用ParentKeySelector不能转换为SQL。所以似乎我应该用Expression<Func<TEntity, TParentKey>>而不是Func<TEntity, TParentKey>来做我想做的事,但我不知道如何实现我的目标。更熟悉表达式的人可以帮我吗?

您是正确的,ParentKeySelector无法转换为SQL是问题的原因。由于它无法转换为 SQL,因此 EF 无法将 linq 转换为 SQL 以供数据库服务器执行,无论您如何执行此操作。

目前尚不清楚您要完成什么。但是,如果父/子关系是显式的,则应该能够定义外键引用,并让 EF 自动解析它们,请参阅 EF 教程。

最新更新