将具有重复列的映射表映射到同一类

时间:2015-12-31 18:19:48

标签: entity-framework entity-framework-6

我有一个类似于以下内容的数据库。是的,我知道,我继承了一个糟糕的设计,但它现在无法改变。基本上,每个客户都可以拥有主要和次要联系信息:

客户

  • ID
  • CUSTOMER_NUMBER
  • [其他东西]

的ContactInfo

  • CUSTOMER_ID
  • PRIMARY_NAME
  • PRIMARY_EMAIL
  • 主电话
  • secondary_name
  • SECONDARY_EMAIL
  • secondary_phone
  • 的updated_at

我希望我的Entity Framework 6域模型看起来像这样:

public class Customer
{
    public int Id { get; set; }
    public string CustomerNumber { get; set; }

    public ContactInfo PrimaryContactInfo { get; set; }
    public ContactInfo SecondaryContactInfo { get; set; }
}

public class ContactInfo
{
    public int CustomerId { get; set; }

    public string Name { get; set; }
    public string Email { get; set; }
    public string Phone { get; set; }
    public DateTime? UpdatedAt { get; set; }

    public virtual Customer Customer { get; set; }
}

有没有办法设置映射,以便这可能?实质上,映射列" primary_ "对于ContactInfo的一个实例,列" secondary _ "到另一个实例,并将一些列映射到两个实例?

或者,可以将字段映射到复杂类型的两个实例:

public class ContactInfo
{
    public int CustomerId { get; set; }

    public SingleContactInfo Primary { get; set; }
    public SingleContactInfo Secondary { get; set; }

    public virtual Customer Customer { get; set; }
}

public class SingleContactInfo
{
    public string Name { get; set; }
    public string Email { get; set; }
    public string Phone { get; set; }
}

2 个答案:

答案 0 :(得分:1)

如果您可以从ContactInfo类中删除CustomerID和Customer引用,那么您可以尝试以下操作:

将ContactInfo注册为ComplexType:

modelBuilder.ComplexType<ContactInfo>();

根据Customer对象上的每个实例定义列的特定名称(我只是展示了如何为name属性执行此操作):

modelBuilder.Entity<Customer>().Property(p => p.PrimaryContactInfo.Name).HasColumnName("primary_name");
modelBuilder.Entity<Customer>().Property(p => p.SecondaryContactInfo.Name).HasColumnName("secondary_name");

答案 1 :(得分:1)

第一个解决方案(继承)无法完成,因为无法将继承方案中的两个实体保存到一个数据库记录中。

第二个解决方案(复杂类型)确实有效,但您必须牺牲将ContactInfo作为独立实体进行处理并在模型中包含updated_at的能力。 (因为updated_at无法映射两次到一个复杂类型属性。)

我看到将实体映射到数据模型同时将ContactInfo保持为独立实体集(几乎)以及读/写updated_at的能力的唯一方法是通过表拆分

在此映射中,表ContactInfo分为两个实体ContactInfoPrimaryContactInfoSecondaryCustomerContactInfoPrimary的关联为1:1,其中包含 ContactInfoSecondary

这就是它的样子:

课程:

public partial class Customer
{
    public int Id { get; set; }
    public string CustomerNumber { get; set; }
    public virtual ContactInfoPrimary ContactInfoPrimary { get; set; }
}

public abstract class ContactInfo
{
    public int CustomerId { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public string Phone { get; set; }
}

public class ContactInfoPrimary : ContactInfo
{
    public DateTime UpdatedAt { get; set; }
    public virtual Customer Customer { get; set; }
    public virtual ContactInfoSecondary ContactInfoSecondary { get; set; }
}

public class ContactInfoSecondary : ContactInfo
{ }

映射:

class CustomerMap : EntityTypeConfiguration<Customer>
{
    public CustomerMap()
    {
        ToTable("Customer");
        HasKey(c => c.Id);
        Property(c => c.Id)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity)
            .HasColumnName("id");
        Property(c => c.CustomerNumber)
            .HasColumnName("customer_number");
        HasOptional(c => c.ContactInfoPrimary).WithRequired(ci => ci.Customer);
    }    
}

class ContactInfoPrimaryMap : EntityTypeConfiguration<ContactInfoPrimary>
{
    public ContactInfoPrimaryMap()
    {
        ToTable("ContactInfo");
        HasKey(ci => ci.CustomerId);
        Property(ci => ci.CustomerId)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None)
            .HasColumnName("customer_id");
        Property(ci => ci.UpdatedAt).HasColumnName("updated_at");
        Property(ci => ci.Name).HasColumnName("primary_name");
        Property(ci => ci.Email).HasColumnName("primary_email");
        Property(ci => ci.Phone).HasColumnName("primary_phone");

        // This, and the ToTable statements, define the table splitting.
        HasRequired(ci => ci.ContactInfoSecondary).WithRequiredPrincipal();
    }
}

class ContactInfoSecondaryMap : EntityTypeConfiguration<ContactInfoSecondary>
{
    public ContactInfoSecondaryMap()
    {
        ToTable("ContactInfo");
        HasKey(ci => ci.CustomerId);
        Property(ci => ci.CustomerId)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None)
            .HasColumnName("customer_id");
        Property(ci => ci.Name).HasColumnName("secondary_name");
        Property(ci => ci.Email).HasColumnName("secondary_email");
        Property(ci => ci.Phone).HasColumnName("secondary_phone");
    }
}

上下文:

public partial class TestSO : DbContext
{
    public TestSO() : base("name=TestSO")
    { }

    public virtual DbSet<ContactInfoPrimary> ContactInfoes { get; set; }
    public virtual DbSet<Customer> Customers { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new CustomerMap());
        modelBuilder.Configurations.Add(new ContactInfoPrimaryMap());
        modelBuilder.Configurations.Add(new ContactInfoSecondaryMap());
    }
}

现在,您可以使用以下联系信息创建Customer

var cis = new ContactInfoSecondary
            {
                Name = "Name2",
                Email = "email2",
                Phone = "phone2"
            };
var cip = new ContactInfoPrimary
            {
                Name = "Name1",
                Email = "email1",
                Phone = "phone1",
                UpdatedAt = DateTime.Today,
                ContactInfoSecondary = cis
            };
var cst = new Customer
            {
                ContactInfoPrimary = cip,
                CustomerNumber = "number-nine"
            };
context.Customers.Add(cst);
context.SaveChanges();

这就是如何使用所有信息获取客户:

context.Customers.Include(c => c.ContactInfoPrimary.ContactInfoSecondary)

或单独联系信息:

context.ContactInfoes.Include(c => c.ContactInfoSecondary)

你知道为什么我说你几乎独立获得ContactInfo。不是完全。你只能通过初选获得辅助。