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

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.

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.

IndexAnnotation to EF Core

I have this code in EF 6.2
public class ClientNotificationMap : EntityTypeConfiguration<ClientNotification>
{
public ClientNotificationMap()
{
HasKey(x => x.RelationalId);
Property(x => x.SubscriptionId)
.IsRequired()
.HasMaxLength(400)
.HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute()));
}
}
which I want to migrate to Core 2.2
public class ClientNotificationMap : IEntityTypeConfiguration<ClientNotification>
{
public ClientNotificationMap()
{
}
public void Configure(EntityTypeBuilder<ClientNotification> builder)
{
builder.HasKey(x => x.RelationalId);
builder.Property(x => x.SubscriptionId)
.IsRequired()
.HasMaxLength(400)
.HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute()));
}
}
Does anyone know how to change the
.HasColumnAnnotation(IndexAnnotation.AnnotationName, new IndexAnnotation(new IndexAttribute()));
In Core it doesn't seem to be supported. Also I don't find anything relative to the web.
In the end I wrote the code
builder.HasIndex(x => x.SubscriptionId)
.IsUnique();
builder.Property(x => x.SubscriptionId)
.IsRequired()
.HasMaxLength(400);
it compiles at least
It should be something like this:
builder.HasIndex(x => new { x.SubscriptionId})
.HasDatabaseName("IX_SubscriptionId")
.HasFilter(null)
.IsUnique(true);

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 ?

Can a mapping class be created in Entity Framework similar to this one in Fluent NHibernate?

I want to create a mapping class similar to the one mentioned below. I want to convert this Fluent NHibernate mapping class to Entity Framework.
Fluent NHibernate
using FluentNHibernate.Mapping;
using MyBlog.Core.Objects;
public class PostMap: ClassMap<Post>
{
public PostMap()
{
Id(x => x.Id);
Map(x => x.Title)
.Length(500)
.Not.Nullable();
Map(x => x.ShortDescription)
.Length(5000)
.Not.Nullable();
Map(x => x.Description)
.Length(5000)
.Not.Nullable();
Map(x => x.Meta)
.Length(1000)
.Not.Nullable();
Map(x => x.UrlSlug)
.Length(200)
.Not.Nullable();
Map(x => x.Published)
.Not.Nullable();
Map(x => x.PostedOn)
.Not.Nullable();
Map(x => x.Modified);
References(x => x.Category)
.Column("Category")
.Not.Nullable();
HasManyToMany(x => x.Tags)
.Table("PostTagMap");
}
}
Is NHibernate support available with Hosting Services? Is it easily available with any ASP.NET Hosting or only selected services use it?
Yes, Entity Framework has the similar mappings.
NHibernate:
public PostMap()
{
Map(x => x.Title)
.Length(500)
.Not.Nullable();
}
Entity Framework:
public class YourDomainModelContext : DbContext
{
public YourDomainModelContext() { }
...
public DbSet<Post> Posts { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Post>()
.Property(u => u.Title)
.HasMaxLength(500);
}
}
You can get more information in these blog-posts:
Configuring/Mapping Properties and Types with the Fluent API
Entity Framework Mapping Scenarios
Fluent API vs Data Annotations
For EF4 and EF5 there are Fluent API generators, but for EF6 not. I'm also searching for one that works.

EF 4.3 fluent mapping Intermediate table TPT

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.