I want to create 3 tables based on 3 entities using code-first and fluent API. I am using Entity Framework version 6. The join table needs a 3-column primary key and additional columns.
My question: how can I use code-first with C# Fluent API to create/map the 3-column primary key for the PatientTreatment table? Thank you.
Details of the 3-column primary key for the join table { PatentId, TreatmentId , TreatmentDate }. The values of PatentId and TreatmentId are fetched from the other 2 entities (tables) while the value of TreatmentDate is entered manually (e.g. C# code or T-SQL script like calling getdate() function).
Details of the 3 entities:
public class Patient {
public long PatentId {get; set;} // database created using Identity
...
}
public class Treatment {
public long TreatmentId {get; set;} // database created using Identity
...
}
And the join table (entity)
public class PatientTreatment
{
public long PatentId {get; set;} // part of the primary key from the Patient entity
public long TreatmentId {get; set;} // part of the primary key from the Treatment entity
public DateTime TreatmentDate {get; set;} // part of the primary key but its value is from C# code or from T-SQL script, not from other entity (table)
// other fields ...
}
You can't model this as a many-to-many association in which the PatientTreatment class is hidden, which is what is usually referred to as many-to-many in Entity Framework mapping.
But you didn't intend to do that, as is apparent from the explicit PatientTreatment class you show. So it's just a matter of modeling it correctly.
In the OnModelCreating override of your DbContext subclass, setup the mapping like so:
protected override void OnModelCreating(DbModelBuilder mb)
{
mb.Entity<PatientTreatment>()
.HasKey(x => new { x.PatientId, x.TreatmentId, x.TreatmentDate });
mb.Entity<Patient>().HasMany(p => p.PatientTreatments)
.WithRequired().HasForeignKey(x => x.PatientId);
mb.Entity<Treatment>().HasMany(t => t.PatientTreatments)
.WithRequired().HasForeignKey(x => x.TreatmentId);
base.OnModelCreating(mb);
}
I think this line HasKey(x => new { x.PatientId, x.TreatmentId, x.TreatmentDate }) is what you were looking for mainly.
I Just Serch it in stackoverflow
It's not possible to create a many-to-many relationship with a
customized join table. In a many-to-many relationship EF manages the
join table internally and hidden. It's a table without an Entity class
in your model. To work with such a join table with additional
properties you will have to create actually two one-to-many
relationships
check this many to many with extra column
Related
I have a central table to handle translations, many entities could have a collection of translations. I'm using EF 6.0 code first.
For each entity EF generate a foreign key in Translation table, to avoid this, I decide to add 3 columns :TableName, ColumnName and EntityKey to point to the given table.
public class CurrencyModel
{
// more staff here
public ICollection<TranslationModel> Translations {get; set; }
}
public class CityModel
{
// more staff here
public ICollection<TranslationModel> Translations {get; set; }
}
public class CountryModel
{
// more staff here
public ICollection<TranslationModel> Translations {get; set; }
}
public class CategoryModel
{
// more staff here
public ICollection<TranslationModel> Translations {get; set; }
}
How can I force EF to do not generate a foreign key for each table ?
Thank you in advance for your help
After you create and add migration and before updating database you should use fluent api, Go to your DbContext class and check OnModelCreating method, In this method you can see .HasForeignKey(e => e....) on some of your entities ..
OnModelCreating method is where you can override EF conventions and force it to for example not generate a foreign key for each table ..
Is it possible to flatten a two-table relationships into a single entity in Entity Framework?
Specifically, (simplified for example) given the following two tables that define a 1-1 relationship
create table Foo
(
Id int not null identity (1, 1)
constraint PK_Foo_Id primary key (Id),
Name nvarchar(64) not null,
BarId int not null
constraint FK_Bar_Foo foreign key (BarId) references Bar (Id)
)
create table Bar
(
Id int not null identity (1, 1)
constraint PK_Bar_Id primary key (Id),
Value nvarchar(max) not null
)
I can easily map this to entities like this
public class Foo
{
public int Id { get; set;}
public string Name { get; set;}
public Bar Bar { get; set;}
}
public class Bar
{
public int Id { get; set;}
public string Value { get; set;}
}
But what I would like to map to a single flattened entity
public class FlatFoo
{
public int Id { get; set;}
public string Name { get; set;}
public string Value { get; set;}
}
Notice that only one field from table Bar is mapped to FlatFoo
Notes
The actual tables are larger.
Since the text value in Bar can get large it would fill index pages quickly, so there are two tables for quicker index searches against Foo.Id and Foo.Name.
I have looked into Split Entities, but it required both tables have the same primary key.
I have looked at Complex Types but it works in the opposite manner taking a flat table and splitting into composite entities.
I am looking to use the Fluent API to perform the mapping.
Can you provide any help in flattening the mapping between two tables and a single entity?
Update
Yes, views will work to get a flat entity, but then I am not mapping from tables to entity. Likewise, from the other side, I know it is possible to map to non-public composition and expose the property that way. But, I am more interested in learning if EF fluent API is flexible enough to handle the mapping directly than I am in solving a particular issue.
Unfortunately, there is considerable push-back here (at work) to any suggestion of adding anything other than tables to a database (something as basic as views included). It is typically pointed out that doing so adds additional point of maintenance, increases training for support, adds complexity for basic CRUD and other excuses for not learning the tools available. It is silly at best, but it is something I have to deal with. :(
So, as a point of learning for me, is it possible to do this seemingly basic task of directly mapping fields from two arbitrary tables into one entity using EF, fluent API preferred?
Entity Framework doesn't provide a way to map one entity to two tables and then cherry pick from the columns in the way you describe unless the tables share a common key. So as mentioned in the comments, the simplest solution is to create a View and map the entity to that.
public class FlatFooMap : EntityTypeConfiguration<FlatFoo>
{
public FlatFooMap ()
{
ToTable("vwFlatFoo");
HasKey(t => t.Id);
}
}
I have two tables Group and 'User'. User can join to many groups. So I created this two objects and join them in fluent api:
public class Group
{...
public virtual ICollection<ApplicationUser> Members { get; set; }
and:
public class ApplicationUser
{...
public virtual ICollection<Group> MemberInGroups { get; set; }
And I mapped them in fluent api:
modelBuilder.Entity<Group>()
.HasMany(c => c.Members)
.WithMany(x => x.MemberInGroups)
.Map(a =>
{
a.ToTable("UsersInGroups");
a.MapLeftKey("GroupId");
a.MapRightKey("UserId");
});
How I can add here one more column in junction table like JoinDate?
EF doesn't support it. If you need a junction table with extra columns, that table must be mapped to an entity itself, and you lose the direct many-to-many navigation.
You'll have two many-to-one and one-to-many relationships instead, and you'll need to give two hops two navigate between then, i.e from an enttity to the junction table entity (which will be its child) and from the junction table entity to the collection of related entities of the other side of the relationship.
See this SO Q&A:
Many to many mapping with extra fields in Fluent API
Below is my model:
public class TMUrl
{
//many other properties
//only property with type Keyword
public List<Keyword> Keywords{get;set;}
}
public class Keyword
{
//many other properties
//only property with type TMUrl
public List<TMUrl> Urls{get;set;}
}
So clearly, both the entities have many-to-many relationship.
I chose fluent api to tell the entity-framework about this relationship i.e.
modelBuilder.Entity<TMUrl>
.HasMany(s => s.Keywords)
.WithMany(s => s.URLs).Map(s =>
{
s.MapLeftKey("KeywordId");
s.MapRightKey("UrlId");
s.ToTable("KeywordUrlMapping");
});
but when I do
url.Keywords.Add(dbKey); //where url is object of TMUrl,
//dbKey is an existing/new object of Keyword
db.SaveChanges();
I get exception
An error occurred while saving entities that do not expose foreign key
properties for their relationships....
InnerException:
The INSERT statement conflicted with the FOREIGN KEY constraint
"KeywordMaster_Keyword". The conflict occurred in database "DbName",
table "dbo.KeywordMaster", column 'Id'.The statement has been terminated.
but when I add Configuration from the otherside aswell, everything works fine.
i.e.
modelBuilder.Entity<KeyWord>
.HasMany(s => s.URLs)
.WithMany(s => s.Keywords)
.Map(s =>
{
s.MapLeftKey("KeywordId");
s.MapRightKey("UrlId");
s.ToTable("KeywordUrlMapping");
});
Why?. Why I've to add configuration from both the entities, where I've read here and many other places, configuration for one of the entities should do.
What is the case, when I should add configuration for both of the entities involved in the relationship?
I need to understand this. Why. Please help.
The terms Left and Right in MapLeftKey and MapRightKey in the many-to-many mapping with Fluent API can be misunderstood and I guess your problem is caused by this misunderstanding.
One might think that it means they describe the columns that are "left" and "right" in the many-to-many join table. That's actually the case if you let EF Code-First create the database and join table based on your Fluent mapping.
But it's not necessarily the case when you create a mapping to an existing database.
To illustrate this with the prototypic many-to-many example of a User-Role model assume you have an existing database with a Users, Roles and RoleUsers table:
Now, you want to map this table schema to a simple model:
public class User
{
public User()
{
Roles = new List<Role>();
}
public int UserId { get; set; }
public string UserName { get; set; }
public ICollection<Role> Roles { get; set; }
}
public class Role
{
public int RoleId { get; set; }
public string RoleName { get; set; }
}
And you add the Fluent mapping for the Users entity (you must do it this way, because by convention the model above would be one-to-many and you can't start from the Role entity side because it has no Users collection):
modelBuilder.Entity<User>()
.HasMany(u => u.Roles)
.WithMany()
.Map(m =>
{
m.MapLeftKey("RoleId"); // because it is the "left" column, isn't it?
m.MapRightKey("UserId"); // because it is the "right" column, isn't it?
m.ToTable("RoleUsers");
});
This mapping is wrong and if you try to put "Anna" into role "Marketing"...
var anna = ctx.Users.Find(1);
var marketing = ctx.Roles.Find(2);
anna.Roles.Add(marketing);
ctx.SaveChanges();
...SaveChanges will throw exactly the exception you are having. The reason becomes clear when you capture the SQL command that is sent with SaveChanges:
exec sp_executesql N'insert [dbo].[RoleUsers]([RoleId], [UserId])
values (#0, #1)
',N'#0 int,#1 int',#0=1,#1=2
So, EF wants to insert here a row into the join table RoleUsers with a RoleId of 1 and a UserId of 2 which is causing the foreign key constraint violation because there is no user with UserId 2 in the Users table.
In other words, the mapping above has configured the column RoleId as the foreign key to table Users and the column UserId as the foreign key to table Roles. In order to correct the mapping we have to use the "left" column name in the join table in MapRightKey and the "right" column in MapLeftKey:
m.MapLeftKey("UserId");
m.MapRightKey("RoleId");
Actually looking at Intellisense the description makes it clearer what "Left" and "Right" really mean:
MapLeftKey
Configures the name of the column(s) for the left foreign key. The
left foreign key represents the navigation property specified in the
HasMany call.
MapRightKey
Configures the name of the column(s) for the right foreign key. The
right foreign key represents the navigation property specified in the
WithMany call.
So, "Left" and "Right" refer to the order in which the entities appear in the Fluent mapping, not to the column order in the join table. The order in the table actually doesn't matter, you can change it without breaking anything because the INSERT sent by EF is an "extended" INSERT that also contains the column names and not only the values.
Perhaps MapFirstEntityKey and MapSecondEntityKey would have been a less misleading choice of those method names - or maybe MapSourceEntityKey and MapTargetEntityKey.
This was a long post about two words.
If my guess is right that it has anything to do with your problem at all then I would say that your first mapping is incorrect and that you only need the second and correct mapping.
I have a question related to the Fluent NHibernate. I can not describe the schema mapping one entity to multiple tables. There is the following structure of the database:
Create table CeTypes (Id int not null PRIMARY KEY, Name nvarchar(100) not null)
Create table CeValues (Id int not null PRIMARY KEY, Name nvarchar(100) not null)
Create table Ces (Id int not null PRIMARY KEY, CeType_id int not null FOREIGN KEY REFERENCES CeTypes(Id), CeValue_id int not null FOREIGN KEY REFERENCES CeTypes(Id))
there is the following entity:
public class Ce
{
public virtual int Id { get; set; }
public virtual string Type { get; set; }
public virtual string Value { get; set; }
}
CeType, CeValue entities in the domain and there is no. I do not know how to describe the mapping Ce entity.
Tried to describe:
public class CeMap : ClassMap<Ce>
{
public CeMap()
{
Table("Ces");
Id(c => c.Id);
Join("CeTypes", m => m.Map(ce => ce.Type).Column("Name"));
Join("CeValues", m => m.Map(ce => ce.Value).Column("Name"));
}
}
But with such a scheme CeType, CeValue tables should have a field Ce_id. How can I describe scheme mapping under the current structure of the database?
I tried doing the same thing when I first started using nHibernate and couldn't find a way to do it. I actually don't believe that you can map multiple tables to a single object. Usually you would have one entity per table. Each entity will be mapped to their table and would have references/hasmany links between them.
You'll probably find that having one entity per table is better in the long run as well because it allows for simpler mapping to the database.