实体框架与过滤器的关联

时间:2011-10-12 15:29:41

标签: entity-framework entity-framework-4 entity-framework-4.1

我的模型中有以下模型:

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 }

但我不想继续这样做。

1 个答案:

答案 0 :(得分:1)

如果我理解正确,您希望在PatientVendor等地址中收集地址...

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 - AddressVendor - Address之间的数据库中创建两个具有相同FK列的FK约束EntityKey。对于你的模型,我认为,这没有意义,因为如果你有一个地址,它将要求总是Patient 一个Vendor具有相同的PK存在一些EntityKey。因此,您必须手动删除数据库中的这些FK约束(这对我来说非常讨厌)。

  • 最后一件事是您无法为延迟和急切加载导航属性指定过滤器。 Addresses集合将始终使用与EntityKeyPatient的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而没有引用PatientVendor与PK(例如,因为某种方式,患者1和供应商1已被删除)。

仅仅因为这个原因,我更倾向于使用@Akash所示的解决方案 - 除了它可能是EF的唯一可用且干净的解决方案。