简体   繁体   English

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

[英]Entity framework map entity to multiple tables in cascade

I'm facing a problem using EF. 我在使用EF时遇到问题。 I have the following situation: 我有以下情况:

数据库架构

From this database schema i'd like to generate the following entity by merge tables data: 我想从此数据库架构中通过合并表数据生成以下实体:

// 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; }
    }

As you can see, in my context i don't want 3 separated entities for each table. 如您所见,在我的上下文中,我不希望每个表具有3个分离的实体。 I want a single one with the fields related to all tables. 我想要一个与所有表相关的字段。 All fields of Customers and CustomersType tables must be readonly (so i've set the relative setters protected) and the others must be editables so that EF can track changes. 客户表和客户类型表的所有字段都必须是只读的(因此我已将相对设置器设置为受保护的),其他字段必须是可编辑的,以便EF可以跟踪更改。 In particular, i'd like to have the ability to change the "IdCustomer" field and let EF to automatically update "CodCustomer", "CompanyTitle", "DescrType"....and so on by doing cross table select. 特别是,我希望能够更改“ IdCustomer”字段,并让EF通过执行交叉表选择来自动更新“ CodCustomer”,“ CompanyTitle”,“ DescrType” ...等等。

To do that, i wrote this configuration class: 为此,我编写了以下配置类:

 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");
            });

        }
    }

I've tested it but it doesn't work as expected. 我已经对其进行了测试,但是无法正常工作。 I always get this message: 我总是收到此消息:

Properties for type 'Purchase' can only be mapped once. “购买”类型的属性只能映射一次。 The non-key property 'CodCustomer' is mapped more than once. 非关键属性“ CodCustomer”被映射多次。 Ensure the Properties method specifies each non-key property only once. 确保“属性”方法仅指定每个非关键属性一次。

Maybe there's something wrong or i forget something (for example the join fields of Map<> that i don't know where to specify them). 也许出了点问题,或者我忘记了一些东西(例如,我不知道在哪里指定的Map <>的联接字段)。 How can i accomplish in the correct way this task? 我如何正确完成此任务? I don't want to have "Customers" and "CustomersType" DBSets in my context. 我不想在上下文中包含“ Customers”和“ CustomersType” DBSet。 Is there a way to avoid it? 有办法避免吗?

I even thought to add into the "IdCustomer" setter a custom query to update manually "Customers" and "CustomersType" related fields, but i don't want to do that for 2 reasons: 我什至考虑将自定义查询添加到“ IdCustomer” setter中,以手动更新“ Customers”和“ CustomersType”相关字段,但是由于两个原因,我不想这样做:

  1. I don't have any DbConnection avaiable into the "Purchases" class, so i can't create a DbCommand to read data from DB. 我在“购买”类中没有任何DbConnection可用,因此我无法创建DbCommand来从DB读取数据。
  2. I want entity class to be persistent-ignorant 我希望实体类是持久性忽略的
  3. EF seems to be a powerfull tool that can do these sort of things and i don't want to reinvent the wheel by writing custom procedures. EF似乎是一个功能强大的工具,可以执行此类操作,并且我不想通过编写自定义过程来重新发明轮子。

I've uploaded the example C# source and the tables CREATE scripts (MS SQLServer) here . 我在此处上传了示例C#源代码和表CREATE脚本(MS SQLServer)。 All entities are autogenerated by the "EF reverse POCO generator" T4 template (the T4 template is disabled, to activate it set CustomTool = TextTemplatingFileGenerator). 所有实体均由“ EF反向POCO生成器” T4模板自动生成(禁用T4模板,可通过设置CustomTool = TextTemplatingFileGenerator来激活它)。 Do not forget to update the ConnectionString in the app.config. 不要忘记在app.config中更新ConnectionString。

Thanks in advance. 提前致谢。

Not the right mapping 映射不正确

I'm afraid the bad news is that this mapping is not possible with this table structure. 恐怕坏消息是此表结构无法实现此映射。 What you're trying to achieve here is known as entity splitting . 您要在此处实现的目标称为实体拆分 However, entity splitting requires 1:1 associations, because sets of records in the involved tables represent one entity. 但是,实体拆分需要1:1关联,因为所涉及表中的记录集代表一个实体。 With this mapping, you can't have a Customer belonging to more than one Purchase . 使用此映射,您不能拥有一个Customer属于多个Purchase That would mean that you could modify multiple Purchase entities by modifying a Customer property of only one of them. 这意味着您可以通过仅修改其中一个的“ Customer属性来修改多个“ Purchase实体。

Maybe the news isn't that bad, because I think you actually want to have 1-n associations. 也许这个消息并不是那么糟糕,因为我认为您实际上想拥有1-n关联。 But then you can't have these "flattened" properties in Purchase . 但是,那么在Purchase就不能拥有这些“扁平化”的属性。

As an alternative you could create delegated properties like so: 另外,您可以创建委托属性,如下所示:

public string CodCustomer
{
    get { return this.Customer.CodCustomer; }
    set { this.Customer.CodCustomer = value; }
}

You'd have to Include() Customer s and CustomersType s when you fetch Purchase s. 在获取Purchase时,必须Include() CustomerCustomersType

Another alternative is to use a tool like AutoMapper to map Purchase to a DTO type having the flattened properties. 另一种选择是使用诸如AutoMapper的工具将Purchase映射到具有扁平属性的DTO类型。

But what does the exception tell me? 但是异常告诉我什么?

You map the Purchase entity to the Purchases table. 您将Purchase实体映射到Purchases表。 But you don't specify which properties you want to map to this table. 但是,您没有指定要映射到该表的属性。 So EF assumes that all properties should be mapped to it. 因此,EF假定所有属性都应映射到它。 So that's the first (implicit) mapping of CodCustomer . 这就是CodCustomer的第一个(隐式)映射。 The second one is the one in the mc.ToTable statement. 第二个是mc.ToTable语句中的一个。 (EF only reports the first problem.) (EF仅报告第一个问题。)

To fix this, you should add a mapping statement for the left-over Purchase properties: 要解决此问题,您应该为剩余的Purchase属性添加一个映射语句:

Map(mc =>
{
    mc.Properties(n => new
    {
        n.IdPurchase,
        n.CodPurchase,
        n.IdCustomer,
        n.Total,
    });
    mc.ToTable("Purchases");
});

By the way, you should also remove the mapping configuration classes of Customer and CustomersType , they're redundant. 顺便说一句,您还应该删除CustomerCustomersType的映射配置类,它们是多余的。

But, as said, the database schema doesn't match the required structure. 但是,如上所述,数据库架构与所需的结构不匹配。 If you try to save a Purchase you will get a foreign key constraint exception. 如果您尝试保存Purchase ,则将获得外键约束异常。 This is because EF expects the following table structure: 这是因为EF期望以下表结构:

在此处输入图片说明

Where the columns IdPurchase in Customer and CustomersType are both primary key and foreign key to Purchase . 其中列IdPurchaseCustomerCustomersType都是主键和外键Purchase I don't think this is what you had in mind when designing the database. 我认为这不是您在设计数据库时想到的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM