我有一个类似于以下内容的数据库。是的,我知道,我继承了一个糟糕的设计,但它现在无法改变。基本上,每个客户都可以拥有主要和次要联系信息:
我希望我的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; }
}
答案 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
分为两个实体ContactInfoPrimary
和ContactInfoSecondary
。 Customer
与ContactInfoPrimary
的关联为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
。不是完全。你只能通过初选获得辅助。