我的模型中有以下模型:
Patient
Vendor
Organization
这些实体中的每一个都需要地址。
地址基本上如下所示
Address
AddressTypeId // with Navigation Property/Association to AddressType
EntityKey // indicates the PK Id of the entity this address is for
AddressType
EntityId // indicates the entity type this address type corresponds to (Patient or Vendor)
// This should be on the AddressType, not the Address, since we need a way of knowing what kind of AddressTypes are available to create for new addresses for Patients, Vendors, and Organizations
//...that is Patients support AddressType X, Vendors support AddressType Y, etc.
我想在Address上的EntityKey属性上创建Patient,Vendor和Organization的关联 - 每个都有一个过滤器约束,Address的AddressType.EntityId是该实体的匹配EntityId(1代表Patient,2代表Vendor, 3代表地址)。
这样做的最佳方式是什么?市场上大多数ORM都支持这种情况......而且它肯定是一种非常常见的情况。
注意:我不想创建PatientAddress / PatientAddressType,VendorAddress / VendorAddressType和OrganizationAddress / OrganizationAddress类型派生实体。它严重混乱了模型,使其基本上不可理解。
现在我通过在LINQ查询中进行显式连接来解决这个问题:
const int patientTypeEntityId = 1;
var query = from p in repository.Patients
let addresses = repository.Addresses.Where(a =>
a.EntityKey == p.Id & a.AddressType.EntityId == patientTypeEntityId)
select new { Patient = p, Addresses = a }
但我不想继续这样做。
答案 0 :(得分:1)
如果我理解正确,您希望在Patient
,Vendor
等地址中收集地址...
public class Patient
{
public int Id { get; set; }
public ICollection<Address> Addresses { get; set; }
}
public class Vendor
{
public int Id { get; set; }
public ICollection<Address> Addresses { get; set; }
}
public class Address
{
public int Id { get; set; }
//public int EntityKey { get; set; }
public AddressType AddressType { get; set; }
}
...并以某种方式告诉EF,Patient.Addresses
仅填充地址类型为“患者”的地址。
我认为这有几个原因是不可能的:
如果你没有在Address
(那里没有EntityKey
属性)中公开外键,你必须告诉EF映射中的键(否则它会创建/假设两个不同的FK专栏):
modelBuilder.Entity<Patient>()
.HasMany(p => p.PVAddresses)
.WithRequired()
.Map(a => a.MapKey("EntityKey"));
modelBuilder.Entity<Vendor>()
.HasMany(p => p.PVAddresses)
.WithRequired()
.Map(a => a.MapKey("EntityKey"));
由于两个不同关系的重复“EntityKey”列,这会引发异常。
接下来我们可以尝试将外键公开为Address
中的属性(EntityKey
属性存在),然后使用此映射:
modelBuilder.Entity<Patient>()
.HasMany(p => p.PVAddresses)
.WithRequired()
.HasForeignKey(a => a.EntityKey);
modelBuilder.Entity<Vendor>()
.HasMany(p => p.PVAddresses)
.WithRequired()
.HasForeignKey(a => a.EntityKey);
这(令人惊讶)不会抛出异常,但会在Patient
- Address
和Vendor
- Address
之间的数据库中创建两个具有相同FK列的FK约束EntityKey
。对于你的模型,我认为,这没有意义,因为如果你有一个地址,它将要求总是Patient
和一个Vendor
具有相同的PK存在一些EntityKey
。因此,您必须手动删除数据库中的这些FK约束(这对我来说非常讨厌)。
最后一件事是您无法为延迟和急切加载导航属性指定过滤器。 Addresses
集合将始终使用与EntityKey
或Patient
的PK分别具有相同Vendor
的地址填充。您可以使用明确的加载来应用过滤器:
var patient = context.Patients.Single(p => p.Id == 1);
context.Entry(patient).Collection(p => p.Addresses).Query()
.Where(a => a.Addresstype.EntityId == patientTypeEntityId)
.Load();
但您必须确保永远不会对Addresses
集合使用延迟或急切加载。所以,这不是一个真正的解决方案,我们应该立即忘记它。
对我来说,最丑陋的一点是你不能在EntityKey
上拥有FK约束。换句话说:数据库允许EntityKey = 1而没有引用Patient
或Vendor
与PK(例如,因为某种方式,患者1和供应商1已被删除)。
仅仅因为这个原因,我更倾向于使用@Akash所示的解决方案 - 除了它可能是EF的唯一可用且干净的解决方案。