EF6: Code First Complex Type - c#

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

Related

Map table row to DDD value object woith EF core

scenario
I have two aggregates in my microservice Domain model: User and UserNotification.
public class User : Aggreagete
{
public int Id { get; private set; }
}
public class UserNotification : Aggreagete
{
public int Id { get; private set; }
public int UserId { get; private set; }
public bool EnableSms { get; private set; }
public bool EnableEmail { get; private set; }
public NotificationKind NotificationKind { get; private set; }
public UserModel User { get;private set }
}
UserValueObject needs to be mapped from the same table as User Aggregate. I use UserValueObject because from Notification bounded Context it needs to be immutable.
I have following EF mappings:
public void Configure(EntityTypeBuilder<User> builder)
{
builder.ToTable("Users", SCHEME);
builder.HasKey(o => o.Id);
builder.Property(o => o.Id);
builder.HasMany<UserNotification>()
.WithOne()
.HasForeignKey(entry => entry.UserId)
.OnDelete(DeleteBehavior.Cascade);
}
public void Configure(EntityTypeBuilder<UserNotification> builder)
{
builder.ToTable("UserNotifications", SCHEME);
builder.HasKey(o => o.Id);
builder.Property(o => o.Id);
builder.Property(e => e.UserId).IsRequired();
builder.Property(e => e.EnableSms).IsRequired();
builder.Property(e => e.EnableEmail).IsRequired();
builder.Property(e => e.NotificationKind).IsRequired()
.HasConversion(
v => v.ToString(),
strValue => Enum.Parse<NotificationKind>(strValue)
);
builder.OwnsOne(e => e.User).ToTable("Users");
}
problem
I receive error during "Add Migration"
Cannot use table 'dbo.Users' for entity type 'User' since it is being used for entity type 'UserModel' and there is no relationship between their primary keys.
UserModel is:
public class UserModel : ValueObject
{
public int Id { get; private set; }
public string Email { get; private set; }
public string Phone { get; private set; }
public string Name { get; private set; }
}
question
I would like to map UserModel.Id from database table "Users", but Email, Phone and Name should be injected in repository with using HTTP request to Identity
Service. Is is possible to do such operation with EF core?
I see that it is not possible to achieve this with owned entity, because Users table is used also to mapped other aggreagate.
You are getting the correct exception due to the name conflict.
For the User entity you ask EF to use the Users table:
builder.ToTable("Users", SCHEME);
but you also ask it to use the same table to store value objects of the UserNotification entity:
builder.OwnsOne(e => e.User).ToTable("Users");
If your plan is to "reuse" the Users aggregate persistence to magically map the value project to it than I can definitely tell that it's not a good idea.
Value objects are typed pieces of state. They belong to entities and managed by entities. You have to update the content of the VO when needed.
You also don't need to keep the user information in the aggregate, unless you use it in any of the methods of your notification aggregate. If you need to be able to query and join - just do that in the query. Don't use EF for queries and write a plain SQL with inner join to get the information you need. Look up CQRS, Jimmy Bogard advocated using EF for the write side and plain SQL queries for the read side some time ago, it makes total sense.

EntityFreamwork full entity and lite entity

i have table users
user table :
Id, Name , firstName , password , email , address , dateofBrith
i want to create two entity for user table one lite and other full
[Table("user")]
public class LiteUser
{
public int ID {get;set;}
public string Name {get;set;}
public int firstName{get;set;}
}
second entity
public class fullUser : LiteUser
{
public date dateofBrith {get;set;}
public string password {get;set;}
public string email {get;set;}
public string address {get;set;}
}
but not I get error about no column discriminator
is possible to do somthing like my entity are same but one have more filed then the other entity
thank you in advance for help
Unfortunately, no. You can only define one entity to one table. Instead, you'd have to do a manual .Select off of the full entity to return a custom "Lite" entry because EF needs to know all the columns that tie to a specific table from the start.
Edit: The only way around this would be to create a view and map to that instead.
You can do something like this
[Table("user")]
public class LiteUser
{
public string Name {get;set;}
public int firstName{get;set;}
}
public class fullUser : LiteUser
{
public int ID {get;set;}
public date dateofBrith {get;set;}
public string password {get;set;}
public string email {get;set;}
public string address {get;set;}
}
Use primary key public int ID {get;set;} value in the derived class
As Daniel points out, a table can be associated to a single entity definition, outside of Table Per Hierarchy inheritance, which isn't what you are looking for.
This was an old trick I used with NHibernate which isn't supported in EF.
With EF you can utilize Linq and ViewModels to avoid the need of Lite vs. Full models.
Given:
//Entity
public class User
{
public int ID {get;set;}
public string Name {get;set;}
public int firstName{get;set;}
public date dateofBrith {get;set;}
public string password {get;set;}
public string email {get;set;}
public string address {get;set;}
}
// View Models...
public class LiteUserViewModel
{
public int ID {get;set;}
public string Name {get;set;}
public int firstName{get;set;}
}
public class FullUserViewModel : LiteUserViewModel
{
public date dateofBrith {get;set;}
public string password {get;set;}
public string email {get;set;}
public string address {get;set;}
}
Querying..
//Give me a list of lite data..
var viewModels = context.Users
.Where(x => x.DateOfBirth < startDate)
.Select(x => new LiteUserViewModel
{
UserId = x.UserId,
Name = x.Name,
FirstName = x.FirstName
}).ToList();
// Give me a full user.
var viewModel = context.Users
.Where(x => x.UserId = userId)
.Select(x => new FullUserViewModel
{
UserId = x.UserId,
// ... etc ...
}).SingleOrDefault();
You can leverage libraries like AutoMapper to handle mapping entity to view model. In cases where you just want to inspect data you don't need to define a view model / DTO, just use an anonymous type. The end result is the same in that EF will execute an optimized query to just return back the data you want rather than entire entities. You can optimize view models to flatten down hierarchical data using this technique. You do need to ensure that any methods or transformations in the .Select() are pure and EF compatible because EF will attempt to translate and pass those to SQL. More complex transformations should be done in the view model itself, or utilize an anonymous type select of the raw data, followed by a ToList/Single/etc. then .Select() into the view model with appropriate transformations via Linq2Object.
One option is to use table splitting which is when you map a single table to two or more entities. The difference with your requested solution is that the "additional" properties in the "full" configuration will be represented by another entity type. Example (for EF Core; EF6 will be very similar):
public class SplitTablePrincipal
{
[Key]
public int Id { get; set; }
public string PrincipalProperty { get; set; }
// principal entity has a nav property to the dependent entity
public virtual SplitTableDependent Dependent { get; set; }
}
public class SplitTableDependent
{
[Key]
public int Id { get; set; }
public string DependentProperty { get; set; }
}
public class SplitTablePricipalConfiguration : IEntityTypeConfiguration<SplitTablePrincipal>
{
public void Configure( EntityTypeBuilder<SplitTablePrincipal> builder )
{
//builder.HasKey( pe => pe.Id );
// establish 1:? relationship w/ shared primary key
builder.HasOne( pe => pe.Dependent )
.WithOne()
.HasForeignKey<SplitTableDependent>( de => de.Id ); // FK is PK
builder.ToTable( "YourTableName" );
}
}
public class SplitTableDependentConfiguration : IEntityTypeConfiguration<SplitTableDependent>
{
public void Configure( EntityTypeBuilder<SplitTableDependent> builder )
{
//builder.HasKey( de => de.Id );
// map dependent entity to same table as principal
builder.ToTable( "YourTableName" ); // same table name
}
}
You only need to include a DbSet for the SplitTablePrincipal entity type in your DbContext. When querying, the Dependent property will not be populated by default (your "lite" configuration); you would need to eager load the property for the "full" data configuration via .Include( stp => stp.Dependent ). You could also lazy load or explicitly load the Dependent property further down the line should you so choose. For example:
dbContext.Entry( principalEntity ).Reference( p => p.Dependent ).Load();

Many entities to one junction table NHibernate modelling

I would like to be able to add a collection of Notes to any of my main entities in my NHibernate application. I can see how you could do this with a seperate junction table per entity. However, I would like to be able to avoid this and only have one junction table - if this is possible.
Below is the code so far, however this will result in all Notes being loaded for every Entity and I only want to load the notes for that particular entity. What are the alternative approaches I need to take?
public class Entity
{
public virtual int Id { get; set; }
}
public class EntityType1 : Entity
{
public EntityType1()
{
Notes = new List<Note>();
}
public virtual string EntityTypeName { get; set; }
public virtual IList<Note> Notes {get;set;}
}
public class EntityType2 : Entity
{
public EntityType2()
{
Notes = new List<Note>();
}
public virtual string EntityType2Name { get; set; }
public virtual IList<Note> Notes { get; set; }
}
public class Note
{
public virtual int Id { get; set; }
public virtual IList<Entity> Entities { get; set; }
public virtual string NoteText { get; set; }
}
}
namespace FluentNHib.Mappings
{
public class EntityMap : ClassMap<Entity>
{
public EntityMap()
{
Id(m => m.Id);
}
}
public class EntityType1Map : ClassMap<EntityType1>
{
public EntityType1Map()
{
Id(m => m.Id);
Map(m => m.EntityTypeName1);
HasManyToMany(m => m.Notes).Table("EntityToNotes")
.ParentKeyColumn("EntityId")
.ChildKeyColumn("NoteId")
.LazyLoad()
.Cascade.SaveUpdate();
}
}
public class EntityType2Map : ClassMap<EntityType2>
{
public EntityType2Map()
{
Id(m => m.Id);
Map(m => m.EntityType2ame);
HasManyToMany(m => m.Notes).Table("EntityToNotes")
.ParentKeyColumn("EntityId")
.ChildKeyColumn("NoteId")
.LazyLoad()
.Cascade.SaveUpdate();
}
}
public class NoteMap : ClassMap<Note>
{
public NoteMap()
{
Id(m => m.Id);
Map(m => m.NoteText);
}
}
I am not sure what the real issue is:
...however this will result in all Notes being loaded for every Entity and I only want to load the notes for that particular entity...
Is the issue in lazy loading? or in fact that Entity1 and Entity2 can have same ID, therefore the references are mixed? (I expect that and this should be part of the answer below)
Anyhow, I would say that we can achieve what you need: map the Note with just one table EntityToNotes. And that is good.
But, in general, I would descourage you from using the many-to-many. It is just my own feeling, experience. Below are some links with more explanation:
Am I doing many to many incorrectly when using fluent nhibernate?
NHibernate how do you map a crossreference table to a bag?
Nhibernate: How to represent Many-To-Many relationships with One-to-Many relationships?
Draft of the SOLUTION:
So, firstly we have to extend the table "EntityToNotes" with two columns
EntityToNoteId column - we need a primary key for new pairing object
Discriminator column
The Discriminator column will be used for (almost like a standard inheritance)
inserting Discriminator value during creation
filtering te IList<Notes> per Entity
These could be the pairing Entity (with an abstract base gathering the common stuff)
public abstract class EntityToNote<TEntity>
{
public abstract string Discriminator { get; set; }
public virtual TEntity Entity {get;set;}
public virtual Note Note {get;set;}
}
// the pairing objects
public class EntityType1ToNote : EntityToNote<EntityType1>
{
string _discriminator = "EntityType1"; // here we set the discriminator
public virtual string Discriminator
{
get { return _discriminator; }
set { _discriminator = value; }
}
...
// Similar for other pairing objects
The Entities will now be referencing lists of pairing objects
public class EntityType1 : Entity
{
public virtual IList<EntityType1ToNote> Notes {get;set;}
...
public class EntityType2 : Entity
{
public virtual IList<EntityType2ToNote> Notes { get; set; }
...
Here is snippet of the mapping (all other Entities will have usual mapping, including ClassMaps for EntityType1ToNote, EntityType2ToNote...)
public class EntityType1Map : ClassMap<EntityType1>
{
public EntityType1Map()
{
Id(m => m.Id);
Map(m => m.EntityTypeName1);
HasMany(m => m.Notes)
// this "table" setting is redundant, it will come from EntityType1ToNote
//.Table("EntityToNotes")
.KeyColumn("EntityId")
// here is the trick, that only related rows will be selected
.Where("Discriminator = 'EntityType1'")
.Cascade.AllDeleteOrphan();
}
}
As I tried to explain in the links provided, we gained this way a lot. Mostly the ability to use more columns on the pairing table - e.g. Discriminator (later we can have more columns like SortBy...) and we are able to use powerful searching with subqueries - see Query on HasMany reference
Also, in fact, the pairing could be mapped via the real inheritance... But the main point here is: Instead of many-to-many we introduced the pairing object and gained a lot

Difficulty Concerning EF Code First Fluent API, TPH, and Foreign Keys

I have two tables in my database. One is called Users, and the other is called Widgets. The Widgets table represents 3 entities in my code model. One of the entities, Widget, is a parent class for the other two entities, WidgetTypeA and WidgetTypeB. Both WidgetTypeA and WidgetTypeB have navigation properties to the User entity, which is persisted to the Users table in the database. I'm having trouble getting Code First to use the same foreign key for both the WidgetTypeA and WidgetTypeB entities (UserId). Does anyone know how to do this? It seems like it should be a common problem with Table Per Hierarchy mapping.
My entity classes are as follows:
public class Widget
{
public int Id { get; set; }
public string Name { get; set; }
}
class WidgetMap : EntityTypeConfiguration<Widget>
{
public WidgetMap()
{
ToTable("Widgets");
HasKey(w => w.Id);
Property(w => w.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(w => w.Name)
.IsRequired()
.HasMaxLength(75)
.IsUnicode(true);
}
}
public class WidgetTypeA : Widget
{
public int UserId { get; set; }
public virtual User User { get; set; }
public string Color { get; set; }
public int DepthLevel { get; set; }
}
class WidgetTypeAMap : EntityTypeConfiguration<WidgetTypeA>
{
public WidgetTypeAMap()
{
Map(w => w.Requires("WidgetTypeId").HasValue(1));
HasRequired(w => w.User)
.WithMany(u => u.WidgetTypeAs)
.HasForeignKey(w => w.UserId)
.WillCascadeOnDelete(false);
Property(w => w.Color)
.IsOptional()
.IsUnicode(true)
.HasMaxLength(75);
Property(w => w.DepthLevel)
.IsOptional();
}
}
public class WidgetTypeB : Widget
{
public int UserId { get; set; }
public virtual User User { get; set; }
}
class WidgetTypeBMap : EntityTypeConfiguration<WidgetTypeB>
{
public WidgetTypeBMap()
{
Map(w => w.Requires("WidgetTypeId").HasValue(2));
HasRequired(w => w.User)
.WithMany(u => u.WidgetTypeBs)
.HasForeignKey(w => w.UserId)
.WillCascadeOnDelete(false);
}
}
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public int Age { get; set; }
public virtual ICollection<WidgetTypeA> WidgetTypeAs { get; set; }
public virtual ICollection<WidgetTypeB> WidgetTypeBs { get; set; }
}
class UserMap : EntityTypeConfiguration<User>
{
public UserMap()
{
ToTable("Users");
HasKey(u => u.Id);
Property(u => u.Username)
.IsRequired()
.HasMaxLength(75)
.IsUnicode(true);
Property(u => u.Age)
.IsRequired();
}
}
At any rate, I keep getting the error
Invalid column name 'UserId1'
when I try to perform the following operations:
using (var entities = new MyEntities())
{
User u = new User
{
Username = "Frank",
Age = 14
};
entities.Users.Add(u);
entities.SaveChanges();
WidgetTypeA wa1 = new WidgetTypeA
{
Name = "0SDF81",
UserId = u.Id,
DepthLevel = 6
};
entities.WidgetTypeAs.Add(wa1);
entities.SaveChanges();
}
Not sure if this can be fixed or not. I can always specify a second UserId foreign key for the Widgets table, but that seems pointless. Perhaps there's a way to do this using Fluent API?
You cannot map properties defined in different derived entities to the same column. That is limitation in EF. If your WidgetTypeA has UserId property and your WidgetTypeB has UserId property they must be different columns in the database. It should work if you move both UserId and User properties from derived types to the parent Widget type.
I know its a long way late, but hopefully may help other readers.
Although Ladislav was correct that using a mapped Foreign Key is not supported in EF6, I did find a useful workaround.
It is possible to define a computed column specification whose expression simply refers to the original column. Userid in the description above. This can be used as the discriminator for the TPH mapping. With this approach, the column need not be persisted, but can be used for TPH, with the original column being available for use as a foreign key.

Mapping a single field to multiple tables with Fluent NHibernate

Question: is there a way to map a single foreign key to a number of mutually exclusive tables, based on a context?
Background...
In my specific example, I have the following domain graph, representing an insurance claim which can be against a vehicle or property:
public enum InvolvedPartyContext
{
Vehicle = 1,
Property = 2
}
public class Claim
{
public virtual Guid Id { get; set; }
public virtual InvolvedPartyContext InvolvedPartyContext { get; set; }
public virtual Vehicle Vehicle { get; set; } // set if Context = Vehicle
public virtual Property Property { get; set; } // set if Context = Property
}
public class Vehicle { //... }
public class Property { //... }
The SQL looks like this (notice the single foreign key InvolvedPartyId):
CREATE TABLE Claims (
Id uniqueidentifier NOT NULL,
InvolvedPartyContext int NOT NULL,
InvolvedPartyId uniqueidentifier NOT NULL
)
CREATE TABLE Vehicles (
Id uniqueidentifier NOT NULL,
Registration varchar(20) NOT NULL
)
CREATE TABLE Properties (
Id uniqueidentifier NOT NULL,
PostCode varchar(20) NOT NULL
)
The Fluent NHibernate mapping file for Claim:
public ClaimMap()
{
Id(x => x.Id);
Map(x => x.InvolvedPartyContext).CustomTypeIs(typeof(InvolvedPartyContext));
References(x => x.Vehicle, "InvolvedPartyId");
References(x => x.Property, "InvolvedPartyId");
}
This throws an "Invalid index {n} for this SqlParameterCollection with Count {m}" exception, since the same field (InvolvedPartyId) is mapped twice. A simple fix would be to create VehicleId and PropertyId fields, but in the real world there are many more contexts, so this isn't very flexible.
Personally, I wouldn't go with the design you have. Instead I'd create subclasses of your Claim class, VehicleClaim and PropertyClaim respectively.
public class VehicleClaim : Claim
{
public virtual Vehicle Vehicle { get; set; }
}
Then change your mappings to use your InvolvedPartyContext column as a discriminator (the column which NHibernate uses to determine which class the row represents), and create subclass mappings for each subclass.
public class ClaimMap : ClassMap<Claim>
{
public ClaimMap()
{
Id(x => x.Id);
DiscriminateSubClassesOnColumn("InvolvedPartyContext");
}
}
public class VehicleClaimMap : SubclassMap<VehicleClaim>
{
public VehicleClaimMap()
{
DiscriminatorValue(1);
References(x => x.Vehicle);
}
}
If you really do want to run with what you've got, you should look into the any mappings; there isn't a lot of documentation on them, but you use the ReferencesAny method.

Categories