【问题标题】:Entity Framework, Many-to-Optional, Without a Foreign Key?实体框架,多对可选,没有外键?
【发布时间】:2016-04-23 17:42:53
【问题描述】:

我有两个表,它们来自旧系统。这些表从两个独立的外部来源定期更新,并且仅用作我的应用程序中查找数据的“只读”表:

交付站点

public partial class DeliverySite
{
    public string CustomerID { get; set; } // PK
    public string CustomerName { get; set; }
    public string DeliveryAddress { get; set; }
    public string BillingAddress { get; set; }
    //... fields removed for clarity.

    // Navigational Properties.
    public virtual ICollection<Item> Items { get; set; }
}

public class DeliverySiteMap : EntityTypeConfiguration<DeliverySite>
{
    public DeliverySiteMap()
    {
        // Primary Key
        this.HasKey(t => t.CustomerID);

        // Properties
        this.Property(t => t.CustomerID)
            .IsRequired()
            .HasMaxLength(50);

        this.Property(t => t.CustomerName)
            .IsRequired()
            .HasMaxLength(50);

        this.Property(t => t.DeliveryAddress)
            .IsRequired();

        this.Property(t => t.BillingAddress)
            .IsRequired();

        // Table & Column Mappings
        this.ToTable("DeliverySites");
        this.Property(t => t.CustomerID).HasColumnName("CustomerID");
        this.Property(t => t.CustomerName).HasColumnName("CustomerName");
        this.Property(t => t.DeliveryAddress).HasColumnName("DeliveryAddress");
        this.Property(t => t.BillingAddress).HasColumnName("BillingAddress");   
    }
}

项目

public partial class Item
{
    public string Item { get; set; }  // PK
    public string ItemDescription { get; set; }
    public decimal Brand { get; set; }
    public decimal Price { get; set; }
    public string CustomerID { get; set; }  // PK + FK
    //... fields removed for clarity.

    // Navigational Properties.
    public virtual DeliverySite DeliverySite { get; set; }
}

public class ItemMap : EntityTypeConfiguration<Item>
{
    public ItemMap()
    {
        // Primary Key
        this.HasKey(t => new { t.Item, t.CustomerID });

        // Properties
        this.Property(t => t.UserItem)
            .HasMaxLength(50);

        this.Property(t => t.UserItemDescription)
            .HasMaxLength(255);

        this.Property(t => t.CCItem)
            .IsRequired()
            .HasMaxLength(50);

        this.Property(t => t.CCItemDescription)
            .IsRequired()
            .HasMaxLength(255);

        this.Property(t => t.CustomerID)
            .HasMaxLength(50);

        // Table & Column Mappings
        this.ToTable("Items");
        this.Property(t => t.Item).HasColumnName("Item");
        this.Property(t => t.ItemDescription).HasColumnName("ItemDescription");
        this.Property(t => t.Brand).HasColumnName("Brand");
        this.Property(t => t.Price).HasColumnName("Price");
        this.Property(t => t.CustomerID).HasColumnName("CustomerID");
    }
}

鉴于这些表格是独立更新的,可能为“DeliverySites”输入了不存在的“Items”。

因此,我想建立一个可选关系。 (所以我可以在我的应用程序中使用导航属性,但这样我就不会阻止表格被独立更新。)

在我的ItemMap : EntityTypeConfiguration&lt;Item&gt; 中,我尝试了以下方法:

this.HasOptional(x => x.DeliverySite)
    .WithMany(x => x.Items)
    .HasForeignKey(x => x.CustomerID)
    .WillCascadeOnDelete(false);

但我收到此错误:

System.Data.Entity.Edm.EdmAssociationType: : 多重冲突 具有角色“Item_DeliverySite_Target”中的引用约束 关系“Item_DeliverySite”。因为所有的属性在 依赖角色是不可为空的,主要角色的多重性 必须是“1”。

我应该如何实现这种关系?

另外,如果我能做到这一点而不在数据库中添加任何 FK 约束,那将是理想的。这可能吗?

【问题讨论】:

    标签: c# entity-framework-5


    【解决方案1】:

    您很可能遇到此错误,因为Item.CustomerID 是必需的。尽管您没有在映射中调用 IsRequired(),但它是必需的,因为它是复合主键的一部分...

    this.HasKey(t => new { t.Item, t.CustomerID });
    

    ...并且因为复合键的每一列都不允许在数据库中使用 NULL。因此EF想要映射

    this.HasRequired(x => x.DeliverySite)
        .WithMany(x => x.Items)
        .HasForeignKey(x => x.CustomerID)
        .WillCascadeOnDelete(false);
    

    对我来说,真正的问题似乎是您试图使用 EF 创建关系和导航属性,而数据库中没有真正的关系和外键约束。

    如果Item.CustomerID 可以在数据库中具有不作为表DeliverySites 中的行存在的值,则不能使用HasRequired,因为如果您尝试使用Include 加载Item对于DeliverySite,EF 将创建一个INNER JOINItems,它们不会通过CustomerID 引用现有的DeliverySite,根本不会加载。所以,基本上你会得到错误的查询结果。另一方面,您不能使用可选映射(这将导致LEFT OUTER JOIN),因为CustomerID 是PK 的一部分,它会导致您的异常。

    老实说,对于您的旧数据库架构,我不会尝试在您的 EF 模型中引入导航属性和人为关系。如果您在一个查询中需要“相关”DeliverySites 和 Items,我可能更愿意使用带有 CustomerID 的手动 LINQ 连接作为普通标量属性来连接数据并投影加载的 DeliverySite s 和 Items 到一个专门的类(或匿名对象)中。

    【讨论】:

    • 我选择使用的解决方案(实际上是在您的帖子之前)确实是放弃尝试使用导航属性的概念。遗产就是遗产...... :)
    【解决方案2】:

    我相信如果您删除外键并制作它,这会起作用:

    this.HasOptional(x => x.DeliverySite)
        .WithMany(x => x.Items);
    

    【讨论】:

    • 我认为更大的问题是您为什么要首先对遗留系统中的表使用代码?为什么不直接从数据库中生成表?
    • 我已经对数据库中的表进行了逆向工程。我正在尝试使用/添加导航属性。
    • 它们是数据库中的外键吗?
    • 不,表格没有约束——除了 PK 的
    • 您是如何生成表格的?为什么不直接使用模型浏览器?
    【解决方案3】:

    我知道这不是您想要的答案,但恕我直言,您最好花时间制定一个确保旧数据库完整性的解决方案。在 ORM 中寻找非规范化数据库的变通方法不仅是不好的做法,而且会给您的同行带来麻烦并使您的产品贬值。不确定数据是如何持久保存的,但您可以将项目保存在临时表中,并且仅在存在相应的交付站点时才添加到规范化表中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多