【问题标题】:Entity Framework Association with Filter实体框架与过滤器的关联
【发布时间】:2026-02-07 06:50:01
【问题描述】:

我的模型中有以下模型:

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 代表患者,2 代表供应商, 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 }

但我不想继续这样做。

【问题讨论】:

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


    【解决方案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 只会填充地址类型为“Patient”的地址。

    我认为这是不可能的,原因如下:

    • 如果您没有在 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);
      

    这(令人惊讶)不会引发异常,而是在数据库中创建两个 FK 约束,位于 Patient-AddressVendor-Address 之间,具有相同的 FK 列 EntityKey。对于您的模型,我认为这没有意义,因为如果您的地址包含一些 EntityKey,则需要始终存在具有相同 PK 的 Patient .因此,您必须手动删除 DB 中的这些 FK 约束(这对我来说感觉很 hacky)。

    • 最后一件事是您不能为导航属性的延迟和急切加载指定过滤器。 Addresses 集合将始终使用与 PatientVendor 的 PK 相同的地址填充 EntityKey。您可以通过显式加载来应用过滤器:

      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 解决方案。

    【讨论】:

    • 您的意思是每个相关实体类型都有一个可为空的 FK 列?我将如何强制执行患者地址没有组织地址类型的约束?仅凭业务逻辑?看来我违反了 DRY。
    • @JeffN825:是的,仅使用业务逻辑。基本上我的意思是,使用三个可为空的 FK 列是从 EF 观点 使事情变得容易的唯一方法,因为这是您可以定义从 PatientVendor 等到导航集合的唯一方法EF 将根据需要自动处理的地址。三个可为空的 FK 也有缺点,例如:如何确保始终只有一个不为空?但是你将如何在数据库中强制执行约束?您的 EntityKey 只是一列,而不是 FK。您的模型一致性需要业务逻辑,DB 无能为力,EF 也无济于事。
    • DB 相当简单——可以通过触发器来完成。不幸的是,EF 无法支持这一点。感谢您的意见。
    • 所以您的建议是让所有那些(实际上是 15 个)可为空的 FK 列?还是继续按照我的方式做事?谢谢。
    • @JeffN825:哦,15 很多。我不知道该怎么做。这在很大程度上取决于您的整个应用程序以及在引入导航集合和 FK 时必须更改/测试/重新设计的内容。如果我要开始一个项目,我可能会使用 15 个 FK 列,是的。但我说的是理论,而不是这种模型的经验。如果 15 个 FK 可能是一个问题(性能?只有一个必须是非空的约束?...)或者其他人看到替代方案,也许会问一个额外的问题(在 SQLServer/EF 下)标记。这一决定似乎很重要,可以提出多个问题。