【问题标题】:No backing field could be found for property of entity type and the property does not have a getter找不到实体类型的属性的支持字段,并且该属性没有 getter
【发布时间】:2020-05-09 15:37:58
【问题描述】:

我遇到了异常

System.InvalidOperationException : No backing field could be found for property 'ApartmentId' of entity type 'Address' and the property does not have a getter.

这是我的Apartment 班级:

public class Apartment
{
    public Apartment(Address address)
    {
        Address = address;
    }

    private Apartment()
    {
    }

    public int Id { get; private set; }
    public Address Address { get; private set; }
}

这是我的Address 值对象类:

public class Address : IEquatable<Address>
{
    private Address()
    {
    }

    public Address(string streetNumber, string streetName, string city, string state, string zipCode)
    {
        StreetNumber = streetNumber;
        StreetName = streetName;
        City = city;
        State = state;
        ZipCode = zipCode;
    }

    public string StreetNumber { get; private set; }
    public string StreetName { get; private set; }
    public string City { get; private set; }
    public string State { get; private set; }
    public string ZipCode { get; private set; }

    public bool Equals(Address other)
    {
        if (ReferenceEquals(null, other))
        {
            return false;
        }

        if (ReferenceEquals(this, other))
        {
            return true;
        }

        return String.Equals(StreetNumber, other.StreetNumber, StringComparison.OrdinalIgnoreCase) &&
               String.Equals(StreetName, other.StreetName, StringComparison.OrdinalIgnoreCase) &&
               String.Equals(City, other.City, StringComparison.OrdinalIgnoreCase) &&
               String.Equals(State, other.State, StringComparison.OrdinalIgnoreCase) &&
               String.Equals(ZipCode, other.ZipCode, StringComparison.OrdinalIgnoreCase);
    }
}

在我的实体配置中,我使用builder.OwnsOne(a =&gt; a.Address);。在我的存储库中,我进行了以下调用:

    public async Task<Apartment> GetByAddressAsync(Address address)
    {
        return await _context.Apartments.FirstOrDefaultAsync(a => a.Address.Equals(address));
    }

它会产生上述异常。有任何想法吗?我不知道为什么它说我的 Address 值对象中有一个“ApartmentId”。

【问题讨论】:

  • 看一下生成的表结构,Address表中有一个ApartmentId。 EF 需要该字段来记住该地址行属于哪个 Apartment
  • @SirRufo Address 是嵌入在Apartment 表中的owned entity type,因此既没有Address 表也没有AddressId 列。

标签: c# entity-framework-core


【解决方案1】:

异常信息当然是可笑的,和实际问题毫无共同之处,就是表达式

a => a.Address.Equals(address)

它是 IQueryable 表达式树的一部分,因此 EF Core 正在尝试将其翻译为 SQL。

像封装这样的面向对象的特性不能很好地与基于可见性和知识的表达式翻译配合使用。 EF Core 不是反编译器,它看不到您的 Equals 方法的实现。他们通常对未知方法所做的是抛出运行时异常,要求您使用可翻译构造或显式切换到客户端评估。

但是实体类型有特殊处理。 EF Core 正在尝试通过将其隐式转换为 PK(主键)比较来支持相等比较(==、'!=,Equals`)翻译。

这就是您拥有的实体类型的问题。请注意,拥有的实体类型仍然是 entity 类型,但是像 Address 这样的引用拥有的类型没有自己的 PK,因此例外。

当然他们所做的是一个错误,但即使他们“修复”它,修复也只是不同的运行时异常。

解决办法当然是不使用Equals方法,而是显式的成员比较,例如

a => a.Address.StreetNumber.ToUpper() == address.StreetNumber.ToUpper()
    && a.Address.StreetName.ToUpper() == address.StreetName.ToUpper()
    && a.Address.City.ToUpper() == address.City.ToUpper()
    && a.Address.State.ToUpper() == address.State.ToUpper()
    && a.Address.ZipCode.ToUpper() == address.ZipCode.ToUpper()

请注意,字符串比较由数据库控制,因此如果您需要强制执行不区分大小写的比较,则需要显式 ToUpper()

现在我知道这是代码重复并破坏了封装,但这是获得服务器端过滤的唯一方法(除非您使用诸如 Lambda injection by NeinLinq.EntityFrameworkCoreDelegateDecompiler 等的一些 3rd 方库) .

因为客户端在读取整个表格后进行过滤

_context.Apartments.AsEnumerable().FirstOrDefault(a => a.Address.Equals(address))

将成为性能杀手(并且是在 EF Core 3.0+ 中删除隐式客户端评估的原因),而不是缺少自然的 async 支持(需要额外的包,这反过来会导致 EF Core @ 出现问题987654336@s - 见Converting EF Core queries from 2.2 to 3.0 - async await)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-13
    相关资源
    最近更新 更多