EF6: Code First Complex Type

c# domain-driven-design entity-framework sql-server-ce

Question

I'm having trouble getting entity framework to flatten my domain entity classes with Value Objects (complex type) fields to one table.

Everything works if I tell my model builder to ignore my value objects/complex type, but that results in all the attributes of the value object being missed in my tables. As soon as I remove the ignore statement i get "A value shared across entities is created in more than one location". If I look in the resulting CE SQL file I see an additional table named after my Domain class appended with a 1 and containing only the Value Object parameters.

Some Code:

My domain Classes:

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

My repository project (going code first) has got this:

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

  }
}

What I want to get is a table looking like:

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

But what im getting is only:

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

and if I remove the ignore line i get this bonus freak table

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

and a whole bunch of error when I try to use my repository:

----> 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.

Ive done a lot of searching but I just cant find any concrete examples of persisting my value object attributes into the tables created for my domain objects. Can someone show me where I'm going wrong?

1
10
2/23/2014 4:38:32 AM

Accepted Answer

Try this:

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
    }
}
9
2/23/2014 6:07:31 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow