【发布时间】:2020-07-29 07:34:17
【问题描述】:
我有以下(通用)存储库方法:
public async Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> predicate, DateTime? asOf = null, IEnumerable<string> includeProperties = null,
bool trackChanges = false)
{
var query = Set.Include(includeProperties)
.AsOf(asOf);
if (!trackChanges)
{
query = query.AsNoTracking();
}
return await query.SingleAsync(predicate);
}
执行此测试时,它工作正常
[Fact]
public static async Task GetStoffByStoffId_ReturnsEntity_OK_Test()
{
// Arrange
var dbContext = new SaiTestContext();
IRepository<StoffEntity> repository = new Repository<StoffEntity>(dbContext);
const string stoffId = "645106EF59801EE59EB22F1F93673380";
// Act
var entity = await repository.GetAsync(stoff => stoff.StoffId == stoffId);
// Assert
Assert.NotNull(entity);
}
当执行这个时
[Fact]
public static async Task GetStoffByStoffIdPredicate_ReturnsEntity_OK_Test()
{
// Arrange
var dbContext = new SaiTestContext();
IRepository<StoffEntity> repository = new Repository<StoffEntity>(dbContext);
var stoffUpdate = new StoffUpdateEntity
{
StoffId = "645106EF59801EE59EB22F1F93673380"
};
Func<StoffEntity, StoffUpdateEntity, bool> stoffSeletor =
(stoffEntity, updateEntity) => stoffEntity.StoffId == stoffUpdate.StoffId;
// Act
var entity = await repository.GetAsync(stoff => stoffSeletor(stoff, stoffUpdate));
// Assert
Assert.NotNull(entity);
}
这个异常被抛出
System.InvalidOperationException : The LINQ expression 'DbSet<StoffEntity>
.Where(s => Invoke(__stoffSeletor_0, s[StoffEntity], __stoffUpdate_1)
)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.<VisitMethodCall>g__CheckTranslated|8_0(ShapedQueryExpression translated, <>c__DisplayClass8_0& )
at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at EntityFrameworkCore.TemporalTables.Query.AsOfQueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at EntityFrameworkCore.TemporalTables.Query.AsOfQueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)
at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](Expression query, Boolean async)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](IDatabase database, Expression query, IModel model, Boolean async)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass12_0`1.<ExecuteAsync>b__0()
at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore[TFunc](Object cacheKey, Func`1 compiler)
at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[TResult](Expression expression, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable`1 source, Expression expression, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable`1 source, LambdaExpression expression, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.SingleAsync[TSource](IQueryable`1 source, Expression`1 predicate, CancellationToken cancellationToken)
at Refdata.SAI.Data.Repositories.Repository`1.GetAsync(Expression`1 predicate, Nullable`1 asOf, IEnumerable`1 includeProperties, Boolean trackChanges) in C:\dev\Refdata.SAI\Source\Refdata.SAI.Data\Repositories\Repository.cs:line 75
at Refdata.SAI.Data.Tests.Integration.RepositoryGetByPredicateTests.GetStoffByStoffIdPredicate_ReturnsEntity_OK_Test() in C:\dev\Refdata.SAI\Source\Refdata.SAI.Data.Tests.Integration\RepositoryGetByPredicateTests.cs:line 61
--- End of stack trace from previous location where exception was thrown ---
将函数作为参数传递时,是否可以修复第二个测试?
根本原因是我想从一个泛型类中使用这个函数,该函数是由protected abstract Func<TEntity, TUpdateEntity, bool> EntitySelector { get; } 在专用类中实现的。
【问题讨论】:
-
我不是 LINQ 专家,但我看到的是您的仓库想要获取 Func
而您正在创建的 Func 不是相同。所以我想说你必须扩展你的回购方法。 -
@user743414 不,如果是这种情况,那么会出现编译错误,而不是异常。
stoff => stoffSeletor(stoff, stoffUpdate)可转换为Expression<Func<TEntity, bool>>。 -
你想做什么? EF Core 类已经是通用的,DbSet 已经是一个存储库。你已经可以使用
DbSet.Find或 LINQ-to-Entities 的FindFirst来做你想做的事,传递一个 lambda。您是否尝试在 EF Core 的存储库类之上创建一个“通用”存储库? -
通用存储库在 EF Core 等高级 ORM 之上使用时是一种反模式。 Oren Eini 早在 2009 年就在 Repository is the new Singleton 中解释了这一点,甚至解释了为什么大多数人最终构建的是 DAO,而不是存储库。 Gunnar Peipman 在No need for repositories and unit of work with Entity Framework Core 中解释了通用存储库的真正问题 - INSERT 最终会无缘无故地执行 4 次更新和 10 次删除
标签: c# generics lambda ef-core-3.1