EF 4.3 fluent mapping Intermediate table TPT - entity-framework

I have the following legacy table structure (simplified for this post)
The following is my feeble attempt at configuring the Entity:
public class EntityConfiguration : EntityTypeConfiguration<Entity> {
public EntityConfiguration() {
ToTable("Entity");
HasKey(x => x.Id);
Property(x => x.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasMany(x => x.TypeOneUpdateBlacklist)
.WithMany()
.Map(x => {
x.ToTable("UpdateBlacklist");
x.MapLeftKey("EntityId");
x.MapRightKey("UpdateId");
});
HasMany(x => x.TypeTwoUpdateBlacklist)
.WithMany()
.Map(x => {
x.ToTable("UpdateBlacklist");
x.MapLeftKey("EntityId");
x.MapRightKey("UpdateId");
});
}
The configuration renders this error:
The EntitySet 'EntityBlacklistUpdate' with schema 'dbo' and table 'UpdateBlacklist' was already defined. Each EntitySet must refer to a unique schema and table.
Is there away to configure this? Thanks in advance

You should be able to create the many-to-many mapping with the base type Update:
public class EntityConfiguration : EntityTypeConfiguration<Entity> {
public EntityConfiguration() {
ToTable("Entity");
HasKey(x => x.Id);
Property(x => x.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasMany(x => x.Updates)
.WithMany()
.Map(x => {
x.ToTable("UpdateBlacklist");
x.MapLeftKey("EntityId");
x.MapRightKey("UpdateId");
});
}
However, it would require that your class Entity does only have a navigation collection Updates of the base type instead of two navigation collections for the two derived types. And it is only possible if the database schema really represents an inheritance model, i.e. a given Update row can either have a related TypeOneUpdate or a TypeTwoUpdate row, both never both. If it can have both, you cannot map this with TPT but you would have to create one-to-one relationships.

Related

Delete entity when it has a optional foreign key

I have a model containing these classes:
Curriculum class has several SessionTimes and other classes use these entities.
The configuration classes for them are:
public class SessionAttendanceConfiguration : EntityTypeConfiguration<SessionAttendance>
{
public SessionAttendanceConfiguration()
{
HasOptional(x => x.Session)
.WithMany(x => x.SessionAttendance)
.WillCascadeOnDelete(false);
}
}
public class SessionTimeConfiguration : EntityTypeConfiguration<SessionTime>
{
public SessionTimeConfiguration()
{
HasMany(x => x.ClassSessions)
.WithOptional(x => x.SessionTime)
.WillCascadeOnDelete(false);
HasMany(x => x.SessionAttendance)
.WithOptional(x => x.Session)
.WillCascadeOnDelete(false);
}
}
public class StudentAttendanceConfiguration : EntityTypeConfiguration<StudentAttendance>
{
public StudentAttendanceConfiguration()
{
HasMany(x => x.SessionAttendances)
.WithRequired()
.WillCascadeOnDelete();
}
}
public class ClassSessionConfiguration : EntityTypeConfiguration<ClassSession>
{
public ClassSessionConfiguration()
{
HasRequired(x => x.EducationDay)
.WithMany(x => x.Sessions)
.WillCascadeOnDelete(true);
HasOptional(x => x.SessionTime)
.WithMany(x => x.ClassSessions)
.WillCascadeOnDelete(false);
}
}
public class CurriculumConfiguration : EntityTypeConfiguration<Curriculum>
{
public CurriculumConfiguration()
{
HasRequired(x => x.Course)
.WithOptional(x => x.Curriculum);
}
}
When I want to delete a Course object, at first I load all the children and mark them as Deleted, and at the end remove that course from the context.
Something like this:
if (course.StudentAttendances.IsNullOrEmpty())
context.Entry(course).Collection(x => x.StudentAttendances);
// Load other children
// Mark list children as deleted
if (!course.Curriculum.Sessions.IsNullOrEmpty())
{
for (int i = course.Curriculum.Sessions.Count - 1; i >= 0; i--)
{
var session = course.Curriculum.Sessions[i];
context.Entry(session).State = EntityState.Deleted;
}
}
// Remove course
context.Courses.Remove(course);
The delete operation ends with an error related to SessionTime's foreign key to the SessionAttendance class.
The DELETE statement conflicted with the REFERENCE constraint "FK_dbo.SessionAttendances_dbo.SessionTime_Session_Id". The conflict occurred in database "MyDb", table "dbo.SessionAttendances", column 'Session_Id
Since the SessionAttendance has a optional SessionTime, I'm confused.
What's the problem? Thanks
The problem is you have to delete associated records in the foreign key tables first before removing the record.
From the logic you were writing, you need to add business scenarios to remove SessionAttendances rows that contain the Session_Id before removing the Session record.

Common entity in multiple db contexts Entity Framework Core

I'm creating a game and divided my domain in three modules. My first idea was to create multiple database context as it should suit quite well my purpose the problem is that some of entities are common for all modules but in different modules they have different behaviours. Example of this is Player entity I want to have different Player domain objects but both should map to one database table so inheritance is not an option for me as I don't want to inherit behaviour from other modules.
In Economy context Player looks
public override void Configure(EntityTypeBuilder<Player> entity)
{
entity.HasKey(c => c.Id);
entity.HasOne(x => x.PlayerBuildings)
.WithOne(x => x.Player)
.HasForeignKey<Player>(x => x.Id);
entity.HasOne(x => x.Store)
.WithOne(x => x.Player)
.HasForeignKey<Player>(x => x.Id);
entity.HasOne(x => x.ConstructionQueue)
.WithOne(x => x.Player)
.HasForeignKey<Player>(x => x.Id);
entity.ToTable("Player");
}
In World context it looks
public override void Configure(EntityTypeBuilder<Player> entity)
{
entity.HasKey(c => c.Id);
entity.ToTable("Player");
}
public override void Configure(EntityTypeBuilder<Realm> entity)
{
entity.HasKey(c => c.Id);
entity.HasMany(x => x.Players)
.WithOne(x => x.Realm)
.HasForeignKey(x => x.RealmId);
entity.ToTable("Realm");
}
When I run "add-migration name" it adds migration correctly but in both modules it generates create table sql. But it should be created only once and extend later. Do you have any hints ?

EF4.3 many to many table as entity

I have there entities two are normal the other is mapping between each other any time I put navigation mapping on I get invalid object name.
If I try and map I get table already in schema.
EntityA EntityB EntityC
Id Id EntityA has FK} PrimaryKey
Name description EntityB has FK}
The mapping classes
I have taken all navigation on EntityB that caused my problems and entity after trying that
I tried this id in entity c but still got invalid table name. the name it was trying to create was dbo.BA where the actual table is dbo.tblAB
//entity.HasRequired(p => p.A).WithMany(p => p.C).HasForeignKey(p => p.EntityA);
//entity.HasRequired(p => p.B).WithMany(p => p.C).HasForeignKey(p => p.EntityB)
I cannot use the .map because I need to use the all three entities with in the DAL
Mapping classes
Entity C
entity.HasKey(t => new {t.EntityA, t.EntityB});
entity.Property(qa => qa.EntityA).IsRequired();
entity.Property(qa => qa.EntityB.).IsRequired();
entity.toTable("tblC)
this was exiting table and because we need to delete these records we need to do through the entity.
Entity B and Entity A have ICollection Properties to Entity C and mapping of
In Entity A mapping
entity.HasMany(g => g.C).WithRequired().HasForeignKey(p => p.EntityA).WillCascadeOnDelete(false);
In entity B mapping
entity.HasMany(g => g.C).WithRequired().HasForeignKey(p => p.EntityB).WillCascadeOnDelete(false);
In entity C mapping
entity.Property(t => t.EntityA).HasColumnName("EntityA");
entity.Property(t => t.EntityB).HasColumnName("EntityB");
entity.Map(c => c.ToTable("tblC")).HasKey(t => new { t.EntityA, t.EntityB });*
I was getting a multiplicity error but now I am getting Invalid column name 'EntityC_EntityA'. Invalid column name 'EntityC_EntityB'.
UPDATE
I have now fixed the schema with
In EntityC
[Key, Column(Order = 0), ForeignKey("EntityA")]
[Key, Column(Order = 1), ForeignKey("EntityB"))
Navigaton properties
[ForeignKey("Id")]
public virtual EntityA EntityA { get; set; }
[ForeignKey("Id")]
public virtual EntityB EntityB { get; set; }
In the Entity A and b mapping
entity.HasMany(g => g.Cs).WithRequired(p=>p.EntityA).HasForeignKey(p => p.EntityA).WillCascadeOnDelete(false);
entity.HasMany(g => g.Cs).WithRequired(p=>p.EntityB).HasForeignKey(p => p.EntityB).WillCascadeOnDelete(false);
The problem know that the query is trying to create the db.BA table.
The query I have tried is
ctx.EntityAs.Include(p => p.Cs.Select(pr => pr.EntityB))
.Include(p => p.EntityBs.Select(pr => pr.Cs.Select(ps => ps.EntityA)))
I would like to thank Gert Arnold for putting in the write direction. The answer for the schema is in the question
The Linq to solve the many is to include EntityC with a select on B or A
entityA.Include(p => p.Cs.Select(pr => pr.EntityA))
or
entityB.Include(p => p.Cs.Select(pr => pr.EntityB))

Delete child entity when removed from parent collection in one to many relationship Entity Framework

I have the following database setup:
The tables are mapped as follow:
public class OrderMapping : EntityTypeConfiguration<Order>
{
public OrderMapping()
{
this.ToTable("Orders", "prf");
this.HasKey(o => o.OrderId);
this.HasMany(o => o.OrderItems)
.WithRequired(oi => oi.Order)
.HasForeignKey(oi => oi.OrderId);
this.HasRequired(o => o.Institution)
.WithMany()
.HasForeignKey(o => o.InstitutionId);
this.Property(o => o.IsConfirmed)
.IsRequired();
this.Ignore(o => o.Id);
}
}
public class OrderItemMapping : EntityTypeConfiguration<OrderItem>
{
public OrderItemMapping()
{
this.ToTable("OrderItems", "prf");
this.HasKey(oi => oi.OrderItemId);
this.HasRequired(oi => oi.Order)
.WithMany(oi => oi.OrderItems)
.HasForeignKey(oi => oi.OrderId);
this.HasRequired(oi => oi.Proficiency)
.WithMany()
.HasForeignKey(oi => oi.ProficiencyId);
this.HasOptional(oi => oi.Enrolment)
.WithMany()
.HasForeignKey(oi => oi.EnrolmentId);
this.HasMany(oi => oi.OrderItemSets)
.WithRequired(ois => ois.OrderItem)
.HasForeignKey(ois => ois.OrderItemId);
this.Property(oi => oi.DateCreated);
this.Ignore(oi => oi.Id);
}
}
public class OrderItemSetMapping : EntityTypeConfiguration<OrderItemSet>
{
public OrderItemSetMapping()
{
this.ToTable("OrderItemSets", "prf");
this.HasKey(ois => ois.OrderItemSetId);
this.HasRequired(ois => ois.OrderItem)
.WithMany(ois => ois.OrderItemSets)
.HasForeignKey(ois => ois.OrderItemId);
this.Property(ois => ois.NumberOfSets);
this.Property(ois => ois.Month);
}
}
When I try to remove an OrderItemSet from the OrderItem's collection Entity Framework is trying to set the foreignkey in OrderItemSet as null instead of deleting the row even though the foreignkey is not nullable and therefore throwing an exception stating the foreignkey cannot be set to null.
this.OrderItemSets.Remove(orderItemSet);
I don't know what is wrong with my mapping to have Entity Framework think it should set the foreignkey to null instead of deleting the row.
What you need is an identifying relationship between OderItem and OrderItemSet. From the section Considerations for Identifying and Non-identifying Relationships in the link provided above:
Removing the relationship deletes the dependent object. Calling the Remove method on the EntityCollection marks both the relationship and the dependent object for deletion.
You should consider the same type of relation ship for Order and OrderItem.
The basic idea is that, for the model of OrderItemSet, you make the foreign key to OrderItem part of the OrderItemSet's primary key, thus creating a composite key. Inside the mapping for OrderItemSet try to map the primary key like so:
public OrderItemSetMapping()
{
...
this.HasKey(ois => new { ois.OrderItemSetId, ois.OrderItemId });
...
}
In case this doesn't work with the fluent API, then try to create the mapping with attributes:
public class OrderItemSet
{
[Key, ForeignKey("OrderItem"), Column(Order = 1)]
public <YourKeyType> OrderItemId { get; set; }
[Key, Column(Order = 2)]
public <YourKeyType> OrderItemSetId { get; set; }
...
}
Why don't you just remove the child directly:
context.OrderItemSets.Remove(orderItemSet);
context.SaveChanges();
When you remove a child from a parent, you may want to add it to another parent, so it would be inappropriate for Entity Framework to delete the child automatically. You should do it yourself.
I have resolved the issue as per Abbondanza's suggestion. First off create the key of the entity to contain the foreign key (this will force entity framework to delete the child item as it cannot exist without the foreign key):
public OrderItemSetMapping()
{
...
this.HasKey(ois => new { ois.OrderItemSetId, ois.OrderItemId });
...
}
Entity Framework will now delete the entity if removed from the collection of the parent, however since OrderItemSetId is an Identity column this creates another issue where Entity Framework now wants to insert a value in that column when adding a new item to the parent collection (which will throw an exception). By specifying a DatabaseGenerationOption on this column the problem will be resolved:
public OrderItemSetMapping()
{
...
this.Property(r => r.OrderItemSetId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
...
}

ASP.NET Identity 2: trouble creating Fluent Code First relationships

I'm trying to create an ASP.NET Identity DbContext from scratch (getting rid of the depedency on IdentityDbContext<...>)
Nearly there...but my EF Code First fluent relationships are not yet right.
Table structure should come out as:
but getting more like the following:
That's even after I defined the HasForeignKey explicitly.
My question is...where did I go wrong on the relationships?
Thanks!
The mapping I'm using is the following:
public class ApplicationDbContext : DbContext
{
...
public ApplicationDbContext()
: base("DefaultConnection") //, throwIfV1Schema: false
{
Database.SetInitializer<ApplicationDbContext>(new ApplicationDbInitializer());
}
...
protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new IdentityRoleMap());
modelBuilder.Configurations.Add(new IdentityUserClaimMap());
modelBuilder.Configurations.Add(new IdentityUserLoginMap());
modelBuilder.Configurations.Add(new IdentityUserMap());
modelBuilder.Configurations.Add(new IdentityUserRoleMap());
}
...
}
public class IdentityUserClaimMap : EntityTypeConfiguration<IdentityUserClaim>
{
public IdentityUserClaimMap()
{
this.HasKey(t => t.Id);
this.Property(t => t.UserId).IsRequired().HasMaxLength(128);
this.ToTable("Custom_UserClaims");
}
}
public class IdentityUserLoginMap : EntityTypeConfiguration<IdentityUserLogin>
{
public IdentityUserLoginMap()
{
this.HasKey(t => new { t.UserId, t.LoginProvider, t.ProviderKey });
this.Property(t => t.LoginProvider).IsRequired().HasMaxLength(128);
this.Property(t => t.ProviderKey).IsRequired().HasMaxLength(128);
this.Property(t => t.UserId).IsRequired().HasMaxLength(128);
this.ToTable("Custom_UserLogins");
}
}
public class IdentityRoleMap : EntityTypeConfiguration<IdentityRole>
{
public IdentityRoleMap()
{
this.HasKey(t => t.Id);
this.Property(t => t.Id).IsRequired().HasMaxLength(128);
this.Property(t => t.Name).IsRequired().HasMaxLength(256);
this.ToTable("Custom_Roles");
this
.HasMany(x => x.Users)
.WithOptional()
.HasForeignKey(x => x.RoleId);
}
}
public class IdentityUserMap : EntityTypeConfiguration<ApplicationUser>
{
public IdentityUserMap()
{
this.HasKey(t => t.Id);
this.Property(t => t.Id).IsRequired().HasMaxLength(128);
this.Property(t => t.Email).HasMaxLength(256);
this.Property(t => t.UserName).IsRequired().HasMaxLength(256);
this.ToTable("Custom_Users");
//Nav:Claims: User can have 1-(0-*) claims
this
.HasMany<IdentityUserClaim>(x => x.Claims)
.WithRequired()
.HasForeignKey(x => x.UserId)
.WillCascadeOnDelete();
//Nav:Logins: User can have 1-(0-*) Logins
this
.HasMany<IdentityUserLogin>( x => x.Logins)
.WithRequired()
.HasForeignKey(x => x.UserId)
.WillCascadeOnDelete();
//Nav:Roles:
//Interestingly, appears not to be using a *-* Map statement:
this
.HasMany<IdentityUserRole>(x => x.Roles)
.WithRequired()
.HasForeignKey(x => x.UserId)
.WillCascadeOnDelete(false);
}
}
public class IdentityUserRoleMap : EntityTypeConfiguration<IdentityUserRole>
{
public IdentityUserRoleMap()
{
this
.ToTable("Custom_UserRoles")
.HasKey(x => new {x.UserId, x.RoleId});
}
}
Oh, that's annoying.
Turns out that it works fine.
I don't use Sql Server's diagramming tool all that much, and didn't realise that at some point I got it right... but the diagrams kept on being wrong, simply because I didn't Refresh the tables before i selected them. What a waste of time!
Regarding whether to use one or two DbContexts as Erik suggested in the comments, I'll consider it further. For now, I'll play around with using a single DbContext, as all functionality seems to be working fine.
Night.