Reputation: 1402
I have this ASP MVC Project I got from a colleague and It had 2 contexts. One from the template which is needed for the login and the other one was selfmade. So I want to combine the two but I get this error all the time:
ApplicationUser is not part of the model for the current context
The model is Database First. I made my SQL tables first then I made a ADO.NET Entity Data model which gives my this connectionString:
<add name="DairyCowBarnEntities" connectionString="metadata=res://*/Models.DairyCowBarnModel.csdl|res://*/Models.DairyCowBarnModel.ssdl|res://*/Models.DairyCowBarnModel.msl;provider=System.Data.SqlClient;provider connection string="data source=server;initial catalog=DairyCowBarn;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework"" providerName="System.Data.EntityClient" />
In this database the required ASP.NET tables are added and also in the data model. I added the ASP.NET tables via Migration.
This is my DbContext Class:
public partial class DairyCowBarnEntities : IdentityDbContext<ApplicationUser>
{
public DairyCowBarnEntities()
: base("name=DairyCowBarnEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public static DairyCowBarnEntities Create()
{
return new DairyCowBarnEntities();
}
public virtual DbSet<C__MigrationHistory> C__MigrationHistory { get; set; }
public virtual DbSet<C__RefactorLog> C__RefactorLog { get; set; }
public virtual DbSet<AspNetRole> AspNetRoles { get; set; }
public virtual DbSet<AspNetUserClaim> AspNetUserClaims { get; set; }
public virtual DbSet<AspNetUserLogin> AspNetUserLogins { get; set; }
public virtual DbSet<AspNetUser> AspNetUsers { get; set; }
public virtual DbSet<jntConcentrateMixture> jntConcentrateMixtures { get; set; }
public virtual DbSet<jntConsistingParcel> jntConsistingParcels { get; set; }
}
I have read that ApplicationUser needs a normal connectionString like this:
<add name="DairyCowBarnEntities" connectionString="Data Source=server;Initial Catalog=DairyCowBarn;Integrated Security=True" providerName="System.Data.SqlClient" />
But then I'm getting this error:
The context is being used in Code First mode with code that was generated from an EDMX file for either Database First or Model First development. This will not work correctly. To fix this problem do not remove the line of code that throws this exception. If you wish to use Database First or Model First, then make sure that the Entity Framework connection string is included in the app.config or web.config of the start-up project. If you are creating your own DbConnection, then make sure that it is an EntityConnection and not some other type of DbConnection, and that you pass it to one of the base DbContext constructors that take a DbConnection.
I have no idea how I can combine the two. I'm out of ideas. Any help is much appreciated and really needed. Hopefully I explained it clearly. Otherwise just ask if need some more info.
Upvotes: 3
Views: 2858
Reputation: 1815
When you do reverse engineering on your database, it will pull in the AspNet tables, which you do not need. The only model you need regarding your authentication is
public class ApplicationUser : IdentityUser
{
public DateTimeOffset CreatedOn { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
I've added a CreatedOn column to my AspNetUsers table, which is why it is in the above model. The next step is make sure your DbContext derives from IdentityDbContext<ApplicationUser>
. This base class will handle mapping to your AspNet tables. So your DbContext should look like:
public partial class DairyCowBarnEntities : IdentityDbContext<ApplicationUser>
{
public DairyCowBarnEntities()
: base("name=DairyCowBarnEntities")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public static DairyCowBarnEntities Create()
{
return new DairyCowBarnEntities();
}
public virtual DbSet<jntConcentrateMixture> jntConcentrateMixtures { get; set; }
public virtual DbSet<jntConsistingParcel> jntConsistingParcels { get; set; }
}
So do this, remove all models regarding your AspNet and all references to those models in your code. Add the ApplicationUser model but don't create a DbSet for it and pass it into your derived class IdentityDbContext<ApplicationUser>
. The IdentityDbContext will handle everything about authentication and authorization so you can use the UserManager and Identity as normal.
A note, my CreatedOn is database generated with a default (sysdatetimeoffset()) but that never worked. I had to explicitly set the CreatedOn in code or it populated to the database as DateTimeOffset.MinValue.
Updated to answer a comment:
The IdentityDbContext<ApplicationUser>
that your db context derives from handles that mapping for you. It provides a layer of abstraction so you don't have to worry about it. If you need to query those tables beyond what the UserManager will do for you, ie; getting all users email addresses that are not confirmed, you can always write your own select statement and pass it into your dbContext like dbContext.Database.SqlQuery<T>(sqlQuery, false);
T in this case will be a model that you create which should have the same property names and types as the column names you are querying. Example below.
public Users
{
public string UserName { get; set; }
public bool EmailConfirmed { get; set; }
}
var sqlQuery = "SELECT Users.UserName, Users.EmailConfirmed FROM AspNetUsers WHERE Users.EmailConfirmed = {0}"
var unconfirmedUsers = dbContext.Database.SqlQuery<Users>(sqlQuery, false).ToList();
Using {0}
and passing in the value as an argument will automatically scrub that value so no sql injection scripts will work.
Upvotes: 1