Error on running migration in MVC Core application - c#

I'm trying to run the following command to create a new migration for a context that I created myself. I run this command.
dotnet ef migrations add MusicContextCreation --context MusicContext
Here is my music context class. This is what I'm trying to add a migration for.
using Microsoft.EntityFrameworkCore;
namespace MusicianProject.Models.Contexts
{
public class MusicContext : DbContext
{
public MusicContext(DbContextOptions<MusicContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
//builder.Entity<Band>().HasMany<BandMember>();
//builder.Entity<Band>().HasMany<Album>(band => band.Albums);
builder.Entity<Band>().ToTable("Band");
//builder.Entity<BandMember>().HasOne<User>(bandmember => bandmember.User);
builder.Entity<BandMember>().ToTable("BandMember");
builder.Entity<Album>().ToTable("Album");
builder.Entity<Genre>().ToTable("Genre");
}
public DbSet<Band> Bands { get; set; }
public DbSet<Genre> Genres { get; set; }
public DbSet<BandMember> BandMembers { get; set; }
public DbSet<Album> Albums { get; set; }
}
}
Here is the error message I'm getting in the command prompt when I run that command.
The entity type Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUserLogin requires a primary key to be defined.
What I don't understand is I have called the base classes OnModelCreating which I've seen as the answer in other similar questions. What I don't understand is that another context I have for Identity users, does not give me this issue, and it actually sets up the identity tables.
I can add the classes that are being used in this context if need be, I did not add them yet as I didn't want to clutter. Let me know in the comments if it is needed to see the classes being defined in dbset collections.

You should inherit from IdentityDbContext (instead of DbContext), which has proper OnModelCreating content with fluent configuration of Identity classes.
If you want to have two different DbContexts, one for users and other for other classes - you should not have any references between classes in different contexts, read my answer here

Related

How to define nested model relationship with ValueObjects

I have a parent child relationship where the parent has a ValueObject and I cannot determine how to correctly define the relationship.
Adding a migration for the Child/Parent relationship fails with the error...
The entity type 'Address' requires a primary key to be defined.
The following is the current code structure.
public class Address
{
[Required]
public string BuildingNumber { get; private set; }
// other address properties...
}
public class Parent
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; protected set; }
[Required]
public Address PrimaryAddress { get; private set; }
}
public class ParentContext : DbContext
{
public ParentContext(DbContextOptions<ParentContext> options) :
base(options)
{
}
public DbSet<Parent> Parents { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Parent>().OwnsOne(p => p.PrimaryAddress);
// Flatten the ValueObject fields into table
modelBuilder.Entity<Parent>().OwnsOne(p => p.PrimaryAddress).
Property(b => b.BuildingNumber).IsRequired().
HasColumnName("Primary_BuildingName");
}
}
public class Child
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; protected set; }
[Required]
public int ParentId { get; private set; }
[ForeignKey("ParentId")]
public Parent Parent { get; private set; }
}
public class ChildContext : DbContext
{
public ChildContext(DbContextOptions<ChildContext> options) : base(options)
{
}
public DbSet<Child> Children { get; set; }
}
Using the above code example I can run separate commands to create migrations for Parent and Child and the tables look correct.
add-migration create-parent -c parentcontext
add-migration create-child -c childcontext
Adding in the relationship to the entities and adding the final migration fails.
add-migration add-parent-child-fk -c childcontext
The problem only occurs where I have Child and Parent in a different Context.
I have tried defining the relationship different ways in both the parent and child to map the address fields so that the child 'understands' the mapping but I cannot avoid EF errors with anything I have tried.
Example Project is here
https://github.com/cimatt55/ef-parent-valueobject
The main problem are the separate contexts. Value object (owned entity type) is just a consequence - if there wasn't value object, then you would have another issues.
You seem to base your design on a wrong assumption that only entity classes from publicly exposed DbSet. But that's not true. Referenced entities by navigation properties are also included, as well as referenced entities by them etc.
This is logical because EF Core context represents a database with tables and relationships. EF Core needs to know all the related entities in order to correctly support loading related data, querying (joining), cascade delete, tables, columns, primary and foreign key property/columns and their mappings etc.
This is explained in the Including & Excluding Types section of the EF Core documentation:
By convention, types that are exposed in DbSet properties on your context are included in your model. In addition, types that are mentioned in the OnModelCreating method are also included. Finally, any types that are found by recursively exploring the navigation properties of discovered types are also included in the model.
Adjusting their example for your ChildContext, the following types are discovered:
Child because it is exposed in a DbSet property on the context
Parent because it is discovered via the Child.Parent navigation property
Address because it is discovered via the Parent.PrimaryAddress navigation property
Since ChildContext has no Parent entity configuration, EF assumes everything related to Parent (and Address) to be by convention, hence the exception.
Shorty, using separate contexts containing related entities is not a good idea. The solution is to put and maintain all related entities in a single context.
Looking at the terminology used, you've probably are after DDD and bounded contexts, but these do not fit in EF Core (and generally in relational database) model.

Repeated Update causes tracking error

Im using VisualStudio 2017 with .Net Core 2 and EntityFrameworkCore (v2.0.1). The application is a console application which starts an MQTT Client and then processes the received messages, storing them into a database.
Each time the application starts and on the first update it works as expected. However on each subsequent update with the same data (none, one or more fields are changed), it throws an System.InvalidOperationException with the following message:
The instance of entity type 'Entity1' cannot be tracked because
another instance with the key value '[SomeKeyValue]' is already being
tracked. When attaching existing entities, ensure that only one entity
instance with a given key value is attached.
The entities are fairly simple, only using a One-To-Many relationship.
I also have the same repository used in a WebApi writing to the same database, and here the same update code works as expected without errors. I hooked up the Console App to use the WebApi instead, and this works, even though it is exactly the same Repository and Database.
I tried various recommendations I found on the internet, which is for example to explicitly detach the entity, but none of that worked.
The setup is the same as for the Asp.Net WebApi, using dependency injection
services.AddDbContext<MyContext>(options => options.UseSqlServer(connectionString));
The One-To-Many Relations is configured like this:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Entity1>()
.HasOne<Entity2>(di => di.Entity1)
.WithMany(d => d.Entity2)
.HasForeignKey(d => d.Entity1Id);
}
The Entities are:
public class Entity1: ClientChangeTracker
{
[Key]
public string Id{ get; set; }
public ICollection<Entity2> Entity2{ get; set; }
...
}
public class Entity2: ClientChangeTracker
{
[Key]
public string Id{ get; set; }
public Entity1 Entity1{get; set; }
public string Entity1Id{ get; set; }
...
}
The repository code for adding entity:
public void AddEntity(Entity1 entity1)
{
if (_context.Entity1s.Any(x => x.Id== entity1.Id))
{
_context.Entity1s.Update(entity1).Entity;
}
else
{
_context.Entity1s.Add(entity1).Entity;
}
_context.SaveChanges();
}
Anybody any idea why this is happening and how it can be fixed?
It appears that configuring DbContext in the IoC container requires an extra step inside of a Console Application. Instead of
services.AddDbContext<MyContext>(options => options.UseSqlServer(connectionString));
it requires an addition parameter to specify the ServiceLifetime as Transient:
services.AddDbContext<MyContext>(options => options.UseSqlServer(connectionString), ServiceLifetime.Transient);
This seems to fix the problem.

EF Core, Loads navigation property without Include() or Load() [duplicate]

I have some models like those below:
public class Mutant
{
public long Id { get; set; }
...
// Relations
public long OriginalCodeId { get; set; }
public virtual OriginalCode OriginalCode { get; set; }
public int DifficultyLevelId { get; set; }
public virtual DifficultyLevel DifficultyLevel { get; set; }
}
and
public class OriginalCode
{
public long Id { get; set; }
...
// Relations
public virtual List<Mutant> Mutants { get; set; }
public virtual List<OriginalCodeInputParameter> OriginalCodeInputParameters { get; set; }
}
and in the OnModelCreating of DBContext I made the relations like these:
modelBuilder.Entity<Mutant>()
.HasOne(m => m.OriginalCode)
.WithMany(oc => oc.Mutants)
.HasForeignKey(m => m.OriginalCodeId)
.OnDelete(Microsoft.EntityFrameworkCore.Metadata.DeleteBehavior.Restrict);
modelBuilder.Entity<Mutant>()
.HasOne(m => m.DifficultyLevel)
.WithMany(dl => dl.Mutants)
.HasForeignKey(m => m.DifficultyLevelId)
.OnDelete(Microsoft.EntityFrameworkCore.Metadata.DeleteBehavior.Restrict);
now when I request for Mutants, the OriginalCode is null:
but as soon as I request for OriginalCodes like below:
then the OriginalCode field of the mutants will be not null:
What is the reason and how could I fix it?
The reason is explained in the Loading Related Data section of the EF Core documentation.
The first behavior is because EF Core currently does not support lazy loading, so normally you'll get null for navigation properties until you specifically load them via eager or explicit loading. However, the Eager loading section contains the following:
Tip
Entity Framework Core will automatically fix-up navigation properties to any other entities that were previously loaded into the context instance. So even if you don't explicitly include the data for a navigation property, the property may still be populated if some or all of the related entities were previously loaded.
which explains why the navigation property is not null in the second case.
Now, I'm not sure which of the two behaviors do you want to fix, so will try to address both.
The first behavior can be "fixed" by using one of the currently available methods for loading related data, for instance eager loading:
var mutants = db.Mutants.Include(m => m.OriginalCode).ToList();
The second behavior is "by design" and cannot be controlled. If you want to avoid it, make sure to use fresh new DbContext instance just for executing a single query to retrieve the data needed, or use no tracking query.
Update: Starting with v2.1, EF Core supports Lazy Loading. However it's not enabled by default, so in order to utilize it one should mark all navigation properties virtual, install Microsoft.EntityFrameworkCore.Proxies and enable it via UseLazyLoadingProxies call, or utilize Lazy-loading without proxies - both explained with examples in the EF Core documentation.
Using Package Manager Console install Microsoft.EntityFrameworkCore.Proxies
install-package Microsoft.EntityFrameworkCore.Proxies
And then in your Context class add .UseLazyLoadingProxies():
namespace SomeAPI.EFModels
{
public partial class SomeContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder
.UseLazyLoadingProxies()
.UseSqlServer(connectionString);
}
}
}
}

The entity type 'IdentityUserLogin<string>' requires a primary key to be defined [duplicate]

This question already has answers here:
The entity type 'Microsoft.AspNet.Identity.EntityFramework.IdentityUserLogin<string>' requires a key to be defined
(4 answers)
Closed 4 years ago.
i am using dotnet core 1.1 on linux, and i am having issues when i want to split up the identityContext from my regular dbContext, whenever i run the following line in my startup.cs --> configure:
//... some other services
using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
serviceScope.ServiceProvider.GetService<ApplicationDbContext>().Database.Migrate();
//running other database.migrations here + seeding data. But it is the line above that causes problems
So this line throws the exception: The entity type 'IdentityUserLogin' requires a primary key to be defined
I simply don't understand this, why is it my job to give the IdentityUserLogin a primary key??, it is a 3rd party class and i haven't even touched it. I have the following simple setup:
namespace EsportshubApi.Models
{
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
public ApplicationDbContext()
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
}
And the applicationUser:
namespace EsportshubApi.Models.Entities
{
public class ApplicationUser : IdentityUser
{
public ApplicationUser() { }
public static ApplicationUserBuilder Builder()
{
return new ApplicationUserBuilder(new ApplicationUser());
}
public int AccountId { get; set; }
public Guid AccountGuid { get; set; }
public string Salt { get; set; }
public bool Verified { get; set; }
public string Checksum { get; set; }
public string Password { get; set; }
public DateTime Created { get; set; }
public DateTime Updated { get; set; }
}
}
In my startup i am configuring the identity framework the following way:
configureServices:
services.AddEntityFrameworkSqlServer().AddMySQL().AddDbContext<ApplicationDbContext>(options =>
options.UseMySQL(config["ConnectionStrings:DefaultConnection"]));
And
Configure:
app.UseIdentity();
My project is opensourced at : my github repo
if that helps.
I have tried a lot of things. The two most promising was deriving all of the classes used in this generic show, and passing them in explicitly, tried to change all of their keys to ints etc. And that gave the exact same error just with int instead of string. The other way i tried was to do the following inside of OnModelCreating to give IdentityUserLogin a primary key by e.g :
modelBuilder.Entity<IdentityUserLogin<int>>()
.Property(login => login.UserId)
.ForMySQLHasColumnType("PK")
.UseSqlServerIdentityColumn()
.UseMySQLAutoIncrementColumn("AI");
As you can see, this was back when i had UserId as a integer, but i am not even sure if the UserId should be its primaryKey. I can get other errors instead of this one, that says
IdentityUserLogin is part of a hierarchy, and it has no discriminator values
But if I had discriminator values it eventually just goes back to this error. The weirdest part i think is that i have the EXACT same implementation as the UnicornStore github example, that uses a bit of the identity framework as well .... So i really need your help guys. Can reproduce this error by downloading the project, copying the default.appsettings.json into appsettings.json, put in a valid connectionstring, dotnet restore, run with dotnet run --environment Development.
I even tried to change out the implementation to use a MSSQL database instead of MySQL, but that gave the exact same error.
keys of Identity tables are mapped in OnModelCreating method of IdentityDbContext and if this method is not called, you will end up getting the error that you got.
All you need to do is call.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
The original answer (just copied for other's reference just in case) here
Okay. So i will try to answer my own question, because i did get past it. Its still possible to follow the github link in the OP, and see the project i got the error in.
Mainly what was wrong, was that i thouth i got this error trying to migrate the ApplicationDbContext : IDentityContext but actually the error was thrown based on my other dbContext in the application, when i tried to run the migration on that one. I am still a little unaware as to why the other DbContext picked up on these Identity entities which i had not referred to anywhere, i think it's odd that a dbcontext seems to know something about entities not mentioned in the OnModelCreating method. Anyway - when i found out that it wasn't the IdentityDbContext that something was wrong with, i simply added the following in the OnModelCreating of the context that threw the error:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Ignore <IdentityUserLogin<string>>();
modelBuilder.Ignore <IdentityUserRole<string>>();
modelBuilder.Ignore<IdentityUserClaim<string>>();
modelBuilder.Ignore<IdentityUserToken<string>>();
modelBuilder.Ignore<IdentityUser<string>>();
modelBuilder.Ignore<ApplicationUser>();
So ... i am still wondering why my context is picking up on these entities without having anything to do with them, and i am pretty worried that each time i add a context i have to exclude models in contrary to including them.
I had a similar issue relating to your initial problem where
The entity type 'IdentityUserLogin' requires a primary key to
be defined.
And while I think your solution to ignore entities seems to work, I just wanted to provide another solution for those who actually do want to assign a primary key to each entity. The problem is that whenever you create an entity, the DbContext will want keep track of the primary key for each - thus, you'd have to assign it.
See my example below, where Project and Target are my entities, and I've assigned it each a property I would like to use as the primary key for each entity.
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Project>().HasKey(m => m.ProjectPath);
builder.Entity<Target>().HasKey(m => m.Guid);
base.OnModelCreating(builder);
}
After you've identified and assigned which property should be designated as the primary key for that entity, the error will go away.

Table Per Concrete Type (TPC) Inheritance in Entity Framework 6 (EF6)

In an effort to avoid the use of Table Per Hierarchy (TPH) I have been looking at examples of how best to implement Table-Per-Concrete Class (TPC) inheritance in my database model. I came across the official documentation and this article.
Below are some mock-up classes with some simple inheritance.
public class BaseEntity
{
public BaseEntity()
{
ModifiedDateTime = DateTime.Now;
}
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public DateTime ModifiedDateTime { get; set; }
}
public class Person : BaseEntity
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Business : BaseEntity
{
public string Name { get; set; }
public string Location { get; set; }
}
The DbModelBuilder configurations used per the examples in both articles.
modelBuilder.Entity<BaseEntity>()
.Property(c => c.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<Person>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Person");
});
modelBuilder.Entity<Business>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Business");
});
The application runs successfully but when I go back to the database I find three (3) tables instead of the two (2) I expected to find. After a bit of testing it would appear the "BaseEntity" table is created but is never used. Everything seems to work just fine with the exception of this empty orphaned table.
I mess around with the DbModelBuilder configurations, eventually removing the "BaseEntity" configurations which provides the expected result; Two (2) tables, each of them having the correct properties and functioning correctly.
I do one last test, rip out all the DbModelBuilder configurations, only include the two (2) DbSet properties for "Person" and "Business" and test again.
public DbSet<Person> People { get; set; }
public DbSet<Business> Businesses { get; set; }
To my surprise the project builds, goes out to the database, creates only the two tables with all the class properties including the inherited ones from the "BaseEntity" class. I can do CRUD operations without issue.
After running many tests I can't find any issues with the final test and I have not been able to reproduce the duplicate key error both articles warned about.
The changes to the database were committed successfully, but an error
occurred while updating the object context. The ObjectContext might be
in an inconsistent state. Inner exception message: AcceptChanges
cannot continue because the object's key values conflict with another
object in the ObjectStateManager. Make sure that the key values are
unique before calling AcceptChanges.
I am curious why the examples use the MapInheritedProperties property; is this an outdated method?
Why do both examples say to include configuration properties for the "BaseEntity" yet including either the DbSet property or any DbModelBuilder configurations for the "BaseEntity" class causes an unused table to be created.
In reference to the unique key error the articles warned of; I am unable to reproduce the error and I have tested many times with the primary key as either an int generated by the database and a guid generated by the database. Is the information about this error also obsolete or is there a test I can run to produce said error?
Just to make this all simpler, I've moved the code necessary to force TablePerConcrete to open source. Its purpose is to allow features normally only available in the Fluent Interface (where you have to scatter a lot of code into your Db class' OnModelCreating method) to migrate over to Attribute-based features.
It allows you to do things like this:
[TablePerConcrete]
public class MySubclassTable : MyParentClassEntity
Forcing TPC regardless of what EF might decide to infer from your parent class/subclass relationship.
One interesting challenge here is that sometimes EF will screw up an inherited Id property, setting it to be filled with an explicit value rather than being database-generated. You can ensure it doesn't do that by having the parent class implement interface IId (which just says: This has an Id property), then marking the subclasses with [ForcePKId].
public class MyParentClassEntity : IId
{
public int Id { get; set; }
. . .
[TablePerConcrete]
[ForcePKId]
public class MySubclassTable : MyParentClassEntity
{
// No need for PK/Id property here, it was inherited and will work as
// you intended.
Kicking off the code that handles all this for you is pretty simple - just add a couple lines to your Db class:
public class Db : DbContext
{
. . .
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var modelsProject = Assembly.GetExecutingAssembly();
B9DbExtender.New().Extend(modelBuilder, modelsProject);
You can access it one of 2 ways:
Via a single gist with all the relevant classes copy-pasted into a single file, here: https://gist.github.com/b9chris/8efd30687d554d1ceeb3fee359c179f9
Via a library, our Brass9.Data, which we're releasing open source. It has a lot of other EF6 tools in it, like Data Migrations. It's also more organized, with classes broken out into separate files as you'd normally expect: https://github.com/b9chris/Brass9.Data
I use mapping classes, but never-mind. I solve it like this:
public class PersonMap : EntityTypeConfiguration<Person>
{
public PersonMap()
{
Map(m => { m.ToTable("Person"); m.MapInheritedProperties(); });
HasKey(p => p.Id);
Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
}
}
Remember - base class must be abstract.

Categories

Resources