如何使用LINQ中的包含到具有任意属性选择的实体而不评估IQueryable?

问题描述 投票:0回答:1

我正在尝试为具有明确的子母关系的实体创建通用存储库。 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>来完成我想要的事情,但是我不知道如何实现我的目标。可以对表达式更熟悉的人帮我吗?

c# entity-framework linq expression iqueryable
1个回答
0
投票

您正确的事实是,不能将ParentKeySelector转换为SQL的事实。由于无法将其转换为SQL,因此EF不可能将linq转换为SQL以供DB服务器执行,无论您如何操作。

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

© www.soinside.com 2019 - 2024. All rights reserved.