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