【问题标题】:Entity Framework Core throwing exception on queryingEntity Framework Core 在查询时抛出异常
【发布时间】:2018-04-11 22:34:44
【问题描述】:

我在我的项目中使用了一个通用的存储库类,所以一个实际的具体存储库是通过依赖注入来实例化的:

services.AddTransient<IRepository<Passenger>, EntityFrameworkRepository<Passenger>>();

下面是通用存储库本身,请注意我正在使用规范模式进行查询过滤。规范本身只返回一个Expression&lt;TEntity, bool&gt; 表达式对象。

public class EntityFrameworkRepository<TEntity> : IRepository<TEntity>
    where TEntity : class
{
    public async Task<IEnumerable<TEntity>> Find(Specification<TEntity> specification)
    {
        return await _context.Set<TEntity>()
            .Where(specification.ToExpression())
            .AsNoTracking()
            .ToListAsync();;
    }

    public async Task<TEntity> FindOne(Specification<TEntity> specification)
    {
        return await _context.Set<TEntity>()
            .AsNoTracking()
            .FirstOrDefaultAsync(specification.ToExpression());
    }

    public async Task<TEntity> GetById(object id)
    {
        return await _context.Set<TEntity>().FindAsync(id);
    }
}

仅返回确认电子邮件地址的乘客的规范的实现:

public class PermanentPassengerSpecification : Specification<Passenger>
{
    public override Expression<Func<Passenger, bool>> ToExpression()
    {
        return passenger => passenger.EmailConfirmed == true;
    }
}

另一个通过电话号码匹配乘客

public class PassengerByPhoneSpecification : Specification<Passenger>
{
    private readonly PhoneNumber _phoneNumber;

    public PassengerByPhoneSpecification(PhoneNumber phoneNumber)
    {
        if (phoneNumber == null)
            throw new ArgumentNullException();
        _phoneNumber = phoneNumber;
    }

    public override Expression<Func<Passenger, bool>> ToExpression()
    {
        return passenger => passenger.PhoneNumber == _phoneNumber;
    }
}

所以,基本上当我使用 FindOne 方法从存储库中查询数据时,EF Core 会抛出这个:

InvalidOperationException: The EF.Property<T> method may only be used within LINQ queries.
Microsoft.EntityFrameworkCore.EF.Property<TProperty>(object entity, string propertyName)
lambda_method(Closure , TransparentIdentifier<Passenger, PhoneNumber> )
System.Linq.AsyncEnumerable+WhereSelectEnumerableAsyncIterator+<MoveNextCore>d__8.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
System.Runtime.CompilerServices.ConfiguredTaskAwaitable+ConfiguredTaskAwaiter.GetResult()
System.Linq.AsyncEnumerable+AsyncIterator+<MoveNext>d__10.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
System.Runtime.CompilerServices.ConfiguredTaskAwaitable+ConfiguredTaskAwaiter.GetResult()
System.Linq.AsyncEnumerable+<FirstOrDefault_>d__165.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
System.Runtime.CompilerServices.TaskAwaiter.GetResult()
Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider+TaskResultAsyncEnumerable+Enumerator+<MoveNext>d__3.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
System.Runtime.CompilerServices.TaskAwaiter.GetResult()
Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider+ExceptionInterceptor+EnumeratorExceptionInterceptor+<MoveNext>d__5.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
System.Runtime.CompilerServices.TaskAwaiter.GetResult()
Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler+<ExecuteSingletonAsyncQuery>d__23.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
System.Runtime.CompilerServices.TaskAwaiter.GetResult()
Taksapp.Infrastructure.Repositories.EntityFrameworkRepository+<FindOne>d__3.MoveNext() in EntityFrameworkRepository.cs

有什么方法可以解决这个问题吗?

【问题讨论】:

  • 什么是Specification&lt;TEntity&gt;?生成的任何表达式都不符合 EF 的预期
  • 这没有意义,因为这些规范只包含与数据库实体相关的表达式。 @Nkosi
  • 显示使用示例
  • 完成!!我用一个例子编辑了这篇文章
  • 显示导致该特定错误的那个。堆栈跟踪有&lt;Passenger, PhoneNumber&gt;。我正在尝试缩小错误的真实情况

标签: c# asp.net-core entity-framework-core


【解决方案1】:

这是因为表达式将实体成员 Passenger.EmailConfirmed 与常量值 true 进行比较。

public class PermanentPassengerSpecification : Specification<Passenger> {
    public override Expression<Func<Passenger, bool>> ToExpression() {
        return passenger => passenger.EmailConfirmed == true;
    }
}

但是在这种情况下

public class PassengerByPhoneSpecification : Specification<Passenger> {
    private readonly PhoneNumber _phoneNumber;

    public PassengerByPhoneSpecification(PhoneNumber phoneNumber) {
        if (phoneNumber == null)
            throw new ArgumentNullException();
        _phoneNumber = phoneNumber;
    }

    public override Expression<Func<Passenger, bool>> ToExpression() {
        return passenger => passenger.PhoneNumber == _phoneNumber; //<--THIS WONT WORK
    }
}

表达式将实体成员Passenger.PhoneNumber(我假设它是一个string)与局部变量_phoneNumber 进行比较,后者是一个ValueOject 派生的PhoneNumber

Entity Framework 无法将表达式中的值对象转换为有效的 SQL。

PhoneNumber 值对象没有隐式或显式转换,因此在生成查询时很可能只调用与任何电话号码都不匹配的 ToString

我的建议是允许值对象能够转换为最有可能与之比较的对象,或者至少修改规范以进行有效比较。

让我们说类似

public override Expression<Func<Passenger, bool>> ToExpression() {
    string formattedNumber = 
        string.Format("{0}{1}",_phoneNumber.RegionCode, _phoneNumber.Number);
    return passenger => passenger.PhoneNumber == formattedNumber;
}

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2020-06-07
  • 2021-02-27
  • 1970-01-01
  • 1970-01-01
  • 2019-02-19
  • 1970-01-01
  • 2022-01-25
  • 2020-09-11
相关资源
最近更新 更多