简体   繁体   中英

Entity framework map entity to multiple tables in cascade

I'm facing a problem using 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. 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. 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.

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. 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). How can i accomplish in the correct way this task? I don't want to have "Customers" and "CustomersType" DBSets in my context. 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:

  1. I don't have any DbConnection avaiable into the "Purchases" class, so i can't create a DbCommand to read data from 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.

I've uploaded the example C# source and the tables CREATE scripts (MS SQLServer) here . All entities are autogenerated by the "EF reverse POCO generator" T4 template (the T4 template is disabled, to activate it set CustomTool = TextTemplatingFileGenerator). Do not forget to update the ConnectionString in the app.config.

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. With this mapping, you can't have a Customer belonging to more than one Purchase . That would mean that you could modify multiple Purchase entities by modifying a Customer property of only one of them.

Maybe the news isn't that bad, because I think you actually want to have 1-n associations. But then you can't have these "flattened" properties in 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.

Another alternative is to use a tool like AutoMapper to map Purchase to a DTO type having the flattened properties.

But what does the exception tell me?

You map the Purchase entity to the Purchases table. 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. So that's the first (implicit) mapping of CodCustomer . The second one is the one in the mc.ToTable statement. (EF only reports the first problem.)

To fix this, you should add a mapping statement for the left-over Purchase properties:

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.

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. This is because EF expects the following table structure:

在此处输入图片说明

Where the columns IdPurchase in Customer and CustomersType are both primary key and foreign key to Purchase . I don't think this is what you had in mind when designing the database.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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