实体框架将实体映射到级联的多个表

时间:2014-06-30 18:15:47

标签: c# entity-framework orm mapping entity

我在使用EF时遇到了问题。 我有以下情况:

DB schema

从这个数据库模式我想通过合并表数据生成以下实体:

// Purchases
    public class Purchase
    {
        //Fields related to Purchases
        public int IdPurchase { get; set; }
        public string CodPurchase { get; set; }
        public int IdCustomer { get; set; }
        public decimal Total { get; set; }

        //Fields related to Customers table
        public string CodCustomer { get; protected set; }
        public string CompanyTitle { get; protected set; }
        public string CodType { get; protected set; }

        //Fields related to CustomersType table
        public string DescrType { get; protected set; }
    }

正如您所看到的,在我的上下文中,我不希望每个表有3个独立的实体。我想要一个与所有表相关的字段。 Customers和CustomersType表的所有字段必须是只读的(因此我设置相对的setter受保护),其他字段必须是可编辑的,以便EF可以跟踪更改。特别是,我希望能够更改“IdCustomer”字段,让EF通过交叉表选择自动更新“CodCustomer”,“CompanyTitle”,“DescrType”....等等。

为此,我编写了这个配置类:

 internal class PurchaseConfiguration : EntityTypeConfiguration<Purchase>
    {
        public PurchaseConfiguration(string schema = "dbo")
        {
            ToTable(schema + ".Purchases");
            HasKey(x => x.IdPurchase);

            Property(x => x.IdPurchase).HasColumnName("IdPurchase").IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
            Property(x => x.IdCustomer).HasColumnName("IdCustomer").IsRequired();
            Property(x => x.Total).HasColumnName("Total").IsRequired().HasPrecision(19, 4);

            Map(mc =>
             {
                 mc.Properties(n => new
                 {
                     n.CodCustomer,
                     n.CompanyTitle,
                     n.CodType
                 });
                 mc.ToTable("Customers");
             });

            Map(mc =>
            {
                mc.Properties(n => new
                {
                    n.DescrType,
                });
                mc.ToTable("CustomersType");
            });

        }
    }

我已经测试了它,但它没有按预期工作。我总是得到这样的信息:

  

“购买”类型的属性只能映射一次。非钥匙   属性'CodCustomer'被映射不止一次。确保   Properties方法仅指定每个非键属性一次。

也许有些错误或者我忘了某些东西(例如Map&lt;&gt;的连接字段,我不知道在哪里指定它们)。 我怎样才能以正确的方式完成这项任务? 我不希望在我的上下文中有“Customers”和“CustomersType”DBSets。 有没有办法避免它?

我甚至想在“IdCustomer”中添加一个自定义查询来手动更新“Customers”和“CustomersType”相关字段,但我不想这样做有两个原因:

  1. 我没有任何DbConnection可用于“Purchases”类,因此我无法创建DbCommand来从DB读取数据。
  2. 我希望实体类是持久无知的
  3. EF似乎是一个强大的工具,可以做这些事情,我不想通过编写自定义程序重新发明轮子。
  4. 我上传了示例C#source和表CREATE脚本(MS SQLServer)here。 所有实体都由“EF反向POCO生成器”T4模板自动生成(禁用T4模板,激活它设置CustomTool = TextTemplatingFileGenerator)。 不要忘记更新app.config中的ConnectionString。

    提前致谢。

1 个答案:

答案 0 :(得分:2)

不正确的映射

我担心坏消息是这种表结构无法实现这种映射。您在此处尝试实现的内容称为实体拆分。但是,实体拆分需要1:1关联,因为所涉及的表中的记录集代表一个实体。使用此映射,您不能拥有属于多个Customer的{​​{1}}。这意味着您可以通过修改其中一个Purchase属性来修改多个Purchase实体。

也许新闻不是那么糟糕,因为我认为你实际上想要有1-n关联。但是你不能在Customer中拥有这些“扁平化”的属性。

作为替代方案,您可以创建委托属性,如下所示:

Purchase

获取public string CodCustomer { get { return this.Customer.CodCustomer; } set { this.Customer.CodCustomer = value; } } 时,您必须Include() CustomerCustomersType

另一种方法是使用AutoMapper之类的工具将Purchase映射到具有展平属性的DTO类型。

但异常告诉我什么?

您将Purchase实体映射到Purchase表。但是,您没有指定要映射到此表的属性。因此,EF假定应将所有属性映射到它。这是Purchases的第一个(隐式)映射。第二个是CodCustomer语句中的一个。 (EF仅报告第一个问题。)

要解决此问题,您应该为剩余的mc.ToTable属性添加映射语句:

Purchase

顺便说一下,您还应该删除Map(mc => { mc.Properties(n => new { n.IdPurchase, n.CodPurchase, n.IdCustomer, n.Total, }); mc.ToTable("Purchases"); }); Customer的映射配置类,它们是多余的。

但是,如上所述,数据库架构与所需的结构不匹配。如果您尝试保存CustomersType,则会收到外键约束异常。这是因为EF期望以下表结构:

enter image description here

PurchaseIdPurchase中的列Customer都是CustomersType的主键和外键。我不认为这是您在设计数据库时的想法。