EF6:代码优先复杂类型

Posted

技术标签:

【中文标题】EF6:代码优先复杂类型【英文标题】:EF6: Code First Complex Type 【发布时间】:2014-02-23 04:38:32 【问题描述】:

我无法让实体框架将具有值对象(复杂类型)字段的域实体类展平到一个表中。

如果我告诉我的模型构建器忽略我的值对象/复杂类型,一切正常,但这会导致值对象的所有属性在我的表中丢失。一旦我删除忽略语句,我就会得到“在多个位置创建跨实体共享的值”。如果我查看生成的 CE SQL 文件,我会看到一个以我的 Domain 类命名的附加表,附加一个 1 并且仅包含值对象参数。

一些代码:

我的域类:

public User 

    private User()
    public long Id get; private set; // dont ask, inherited legacy database
    public string UserId  get; private set; 
    public string Domain  get; private set; 
    public AuditIformation AuditDetails get ; private set;

    //..domain logic etc


public AuditInformation : IValueObject 
    public long CreatedByUserId  get; private set; 
    public DateTime CreatedDate  get; private set; 
 

我的存储库项目(代码优先)得到了这个:

public partial class myContext : DbContext 

    protected override void OnModelCreating(DbModelBuilder mb) 

        mb.Conventions.Remove<PluralizingTableNameConvention>(); 

        mb.ComplexType<Domain.Model.AuditInformation>();
        mb.ComplexType<Domain.Model.AuditInformation>().Property(a => a.CreatedDate).HasColumnName("Created_On");
        mb.ComplexType<Domain.Model.AuditInformation>().Property(a => a.CreatedByUserId).HasColumnName("Created_By");

        //This line lets everything work but doesn't include my 
        //AuditInformation attributes in my User Table.
        mb.Ignore<Domain.Model.AuditInformation>(); // <== I think I need to remove this

        //..

        mb.Entity<User>().Map(a => 
            a.Property(x => x.Id).HasColumnName("Id");
            a.Property(x => x.UserId).HasColumnName("User_Id");
            a.Property(x => x.Domain).HasColumnName("User_Dmain");
            )
        .HasKey(x => x.Id)
        .ToTable("Tbl_User");   //<==Again, dont ask

  

我想要的是一个看起来像这样的表格:

[TBL_USER] 
ID AS BIGINT,
USER_ID as VARCHAR(MAX),
USER_DMAIN AS VARCHAR(MAX),
CREATED_ON as DATE,
CREATED_BY as BIGINT

但我得到的只是:

[TBL_USER] 
ID AS BIGINT,
USER_ID as VARCHAR(MAX),
USER_DMAIN AS VARCHAR(MAX),

如果我删除忽略行,我会得到这个额外的怪物表

[USER1]  <<==Note, named after the domain class, not the destination table.. 
ID AS BIGINT,
CREATED_ON as DATE,
CREATED_BY as BIGINT

当我尝试使用我的存储库时出现一大堆错误:

----> System.Data.Entity.Infrastructure.DbUpdateException : A value shared across entities or associations is generated in more than one location. Check that mapping does not split an EntityKey to multiple store-generated columns.
----> System.Data.Entity.Core.UpdateException : A value shared across entities or associations is generated in more than one location. Check that mapping does not split an EntityKey to multiple store-generated columns.
----> System.ArgumentException : An item with the same key has already been added.
TearDown : System.NullReferenceException : Object reference not set to an instance of an object.

我做了很多搜索,但我找不到任何将我的值对象属性持久化到为我的域对象创建的表中的具体示例。谁能告诉我哪里出错了?

【问题讨论】:

【参考方案1】:

试试这个:

public class AuditInformation

    public long CreatedByUserId  get; set; 
    public DateTime CreatedDate  get; set; 


public abstract class AuditInfo

    public AuditInformation AuditDetails  get; set; 

    public AuditInfo()
    
        this.AuditDetails = new AuditInformation();
        this.AuditDetails.CreatedByUserId = 0;
        this.AuditDetails.CreatedDate = DateTime.Now;
    


public User : AuditInfo

    private User()
    public long Id get; private set; // dont ask, inherited legacy database
    public string UserId  get; private set; 
    public string Domain  get; private set; 

    //..domain logic etc


public partial class myContext : DbContext 

    public DbSet<User> Users  get; set; 

    protected override void OnModelCreating(DbModelBuilder mb)
    
        mb.Conventions.Remove<PluralizingTableNameConvention>();

        mb.ComplexType<Domain.Model.AuditInformation>();
        mb.ComplexType<Domain.Model.AuditInformation>().Property(a => a.CreatedDate).HasColumnName("Created_On");
        mb.ComplexType<Domain.Model.AuditInformation>().Property(a => a.CreatedByUserId).HasColumnName("Created_By");


        mb.Entity<Cricketer>().Map(a =>
        
            a.Property(x => x.Id).HasColumnName("Id");
            a.Property(x => x.UserId).HasColumnName("User_Id");
            a.Property(x => x.Domain).HasColumnName("User_Dmain");
            a.Property(x => x.AuditDetails.CreatedByUserId).HasColumnName("CreatedByUserId");
            a.Property(x => x.AuditDetails.CreatedDate).HasColumnName("CreatedDate");
        )
        .HasKey(x => x.ID)
        .ToTable("Tbl_User");   //<==Again, dont ask
    

【讨论】:

这成功了。不需要实体映射区域中的 HasColumnName() 作为我想要的复杂类型属性方法中定义的名称。 感谢@Darius 的回答。是的,如果我们用 complex 映射列,那么我们不需要在 enrity 配置中显式映射列。

以上是关于EF6:代码优先复杂类型的主要内容,如果未能解决你的问题,请参考以下文章

在 EF6 中执行复杂的原始 SQL 查询

EF6 使用列默认值创建代码优先表

C# EF6 代码优先实体状态

EF6(代码优先)单个外键属性上的多个导航属性

EF6(代码优先)、MVC、Unity 和没有存储库的服务层

代码优先迁移中列的 EF6 Oracle 默认值