我有一个例外
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 => a.Address);
。在我的存储库中,我进行了以下调用:
public async Task<Apartment> GetByAddressAsync(Address address)
{
return await _context.Apartments.FirstOrDefaultAsync(a => a.Address.Equals(address));
}
并产生上面的异常。有任何想法吗?我不知道为什么它说我的Address
值对象中有一个“ ApartmentId” 。
异常消息当然是荒谬的,与实际问题没有任何共同之处,即表达
a => a.Address.Equals(address)
它是IQueryable
表达式树的一部分,因此EF Core尝试将其转换为SQL。
诸如封装之类的面向对象的功能不能与基于可见性和知识的表达式转换配合使用。EF Core不是反编译器,它看不到您Equals
方法的实现。他们通常使用未知方法执行的操作是抛出运行时异常,要求您使用可翻译构造或显式切换到客户端评估。
但是实体类型有特殊处理。EF Core试图通过将相等隐式转换为PK(主键)比较来支持相等比较(==
,'!= ,
Equals`)转换。
这是您拥有的实体类型的问题。请注意,拥有的实体类型仍然是实体类型,但是像您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注射用NeinLinq.EntityFrameworkCore,DelegateDecompiler来获得服务器端过滤等)。
因为客户端在读取整个表后进行过滤就像
_context.Apartments.AsEnumerable().FirstOrDefault(a => a.Address.Equals(address))
会成为性能杀手(这是在EF Core 3.0+中删除隐式客户端评估的原因),而不是不算缺乏自然async
支持(需要额外的程序包,这反过来会导致EF Core出现问题DbSet
-请参阅转换EF Core查询从2.2到3.0-async await)。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句