I'm trying to do the following... When a user submits a new article, i want to display the authors (users) username in my web application. That's why i need to "connect" these two and i want to have only 1 DbContext.
Now i'm doing it this way:
public class ApplicationDbContext : IdentityDbContext
{
public DbSet<ApplicationUser> ApplicationUsers { get; set; }
private DbSet<Article> Articles { get; set; } // This line is all i have added/changed. Everything else was auto-generated when i created a new ASP.net MVC project with individual authentication in visual studio.
public ApplicationDbContext()
: base("DefaultConnection")
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
But i'm not sure if this is the right way to do it. How should this be done properly?
UPDATE (explanation of my comment to DavidG's answer)
First i was retrieving a list of users like this:
class ApplicationUserService
{
public List<ApplicationUser> GetUsers()
{
using (var dbContext = new ApplicationDbContext())
{
return dbContext.ApplicationUsers.ToList();
}
}
}
instead of:
class ApplicationUserService
{
public List<IdentityUser> GetUsers()
{
using (var dbContext = new ApplicationDbContext())
{
return dbContext.Users.ToList();
}
}
}
The problem was that i couldn't get the UserName or any other property because of this mistake. I watched some tutorials online, no one ever mentioned that the DbSet is Users. Anyways... now i know why i don't need this property:
public DbSet<ApplicationUser> ApplicationUsers { get; set; }
This message from VS helped me (intended for other people which should stumble upon the same problem i had):
'ApplicationDbContext.Users' hides inherited member 'IdentityDbContext<IdentityUser, IdentityRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>.Users'.
This is fine if you want to use one context, and the Identity tables are in the same database as your "article" table. I normally like to override OnModelCreating so I can add mapping/configurations for tables I create.
Depending on the size of the application, I leave the Identity context alone, and I create one for my application (I may include the Users table to make it easier to retrieve users). A matter of choice.
Your application just needs to inherit from the correct version of the IdentityDbContext class. You need to use the generic one so you can customise the user class with your own IdentityUser. So you context should be like this. Note the new inheritance and removal of the ApplicationUsers property. This is removed as the Identity class already has this done for you.
public class ApplicationDbContext : IdentityDbContext<IdentityUser>
{
private DbSet<Article> Articles { get; set; }
public ApplicationDbContext()
: base("DefaultConnection")
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
Related
I got an error using ASP.NET Identity in my app.
Multiple object sets per type are not supported. The object sets
'Identity Users' and 'Users' can both contain instances of type
'Recommendation Platform.Models.ApplicationUser'.
I saw a few questions about this error in StackOverflow. All indicate on two DbSet objects of the same type. But in my DbContext there aren't the same types of DbSets. Exception is thrown on FindAsync() method during logging in.
if (ModelState.IsValid)
var user = await UserManager.FindAsync(model.UserName, model.Password);
if (user != null && user.IsConfirmed)
{
The problem is I don't have two DbSets of the same type. My Contexts look like this:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
public System.Data.Entity.DbSet<RecommendationPlatform.Models.ApplicationUser> IdentityUsers { get; set; }
}
and
public class RecContext : DbContext
{
public RecContext()
: base("RecConnection")
{
Database.SetInitializer<RecContext>(new DropCreateDatabaseIfModelChanges<RecContext>());
}
public DbSet<Recommendation> Recommendations { get; set; }
public DbSet<Geolocation> Geolocations { get; set; }
public DbSet<Faq> Faqs { get; set; }
public DbSet<IndexText> IndexTexts { get; set; }
}
What could cause this problem? Maybe something connected with in-built ASP.NET Identity functionalities? Anyway, what is Users type? I don't have it in my app...
You do have two DbSets` of the same type.
IdentityDbContext<T> itself contains Users property declared as:
public DbSet<T> Users { get; set; }
You're declaring second one in your class.
review this file "ApplicationDbContext.cs", remove the line, generated automatically by scaffold last, should be like this:
public System.Data.Entity.DbSet<Manager.Models.ApplicationUser> IdentityUsers { get; set; }
This issue can arise from using scaffolding to create a View. You probably did something like this: View > Add > New Scaffold Item... > MVC 5 View > [Model class: ApplicationUser].
The scaffolding wizard added a new line of code in your ApplicationDbContext class.
public System.Data.Entity.DbSet<RecommendationPlatform.Models.ApplicationUser> IdentityUsers { get; set; }
Now you have two DbSet properties of the same type which not only causes an exeptions to be thrown in the FindAsync() method but also when you try to use code-first migrations.
Be very careful when using scaffolding or even better don't use it.
Comment the new generated Dbset from identity model class like below
// public System.Data.Entity.DbSet<SurveyTool.Models.ApplicationUser> ApplicationUsers { get; set; }
Whenever I see this problem, I always double check the DbSet. - ESPECIALLY if you are using another language for Visual Studio.
For us who use other language on VS, always double check because the program doesn´t create controllers or models with the exact name. perhaps this should be a thread.. or there is one already and I missed it.
I am assuming there is something I am missing but when I generate migrations in a project, they keep showing empty Up and Down methods. Even in a brand new project. Here are my steps.
Launch Visual Studio 2019. Create a new ASP.NET Web Application (.net framework) C# project (4.7.2). Choose MVC and under authentication select Individual User Accounts. Click create to create the project.
Next I Enable Migrations.
Enable-Migrations
Next I add my first migration.
Add-Migration First
A first migration is successfully added with all the identity information for individual user accounts. All is good.
I update the database to apply the migration. All is still good.
Update-Database
Now, I add a new class to the Models folder called SchoolContext.
using System;
using System.Data.Entity;
using System.ComponentModel.DataAnnotations;
namespace WebApplication6.Models
{
public class SchoolContext : DbContext
{
public DbSet<Student> Students { get; set; }
public SchoolContext() : base("DefaultConnection") { }
}
public class Student
{
[Key]
public int StudentID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
}
}
I now go back to the Package Manager Console to create another migration. I attempt to create a new migration.
Add-Migration Second
But this time, the class is empty. it does not create my new table.
namespace WebApplication6.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class Second : DbMigration
{
public override void Up()
{
}
public override void Down()
{
}
}
}
What am I missing? Why doesn't it want to generate a migration with my new table?
As requested, this is what my ApplicationDbContext looks like in my IdentityModel.cs class generated by Visual Studio.
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
If your first migration created users tables, then I assume that it used IdentityDbContext. Then, you need to inherit your SchoolContext not from DbContext, but from IdentityDbContext.
UPDATE: based on the latest update of the question, it is clear that application already has one database context, which is ApplicationDbContext. So usually it is enough to keep all DbSet<> in one database context. No need to create new context.
You are missing the dbset property.
Put it as following example so that migrations can detect the new class/table.
After adding the dbset property, add new migration to see the new class written.
If you already have a migration pending then write -force for the code to see new changes and update migration
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext() : base("DefaultConnection") {
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
public virtual DbSet<Student> Students { get; set; }
}
Note that there are 2 dbcontexts which is not of clear use, use only ApplicationDBContext and remove the other.
So I've started learning how to use the Entity Framework in MVC using the project template and I have encountered a problem. In my HomeController I wrote the below method GenerateContractorCustomer which when called seems to overwrite the default ASPNetUser tables in the database with the new models I created.
public void GenerateContractorCustomer()
{
using (var context = new ContractorCustomerContext())
{
ContractorModel contractor = new ContractorModel { ContractorID =1, ContractorName ="John"};
context.Contractor.Add(contractor);
context.SaveChanges();
}
}
I created a DbContext:
public class ContractorCustomerContext : DbContext
{
public ContractorCustomerContext() : base("DefaultConnection") { }
public DbSet<ContractorModel> Contractor { get; set; }
public DbSet<CustomerModel> Customer { get; set; }
And in the IdentityModel there is the default:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
My question is what do I need to do to my code so that when I call GenerateContractorCustomer it does not delete the deafult ASPNetUser tables?
You are using an initializer that drops the existing database and creates a new database, if your model classes (entity classes) have been changed. So you don't have to worry about maintaining your database schema, when your model classes change. To change a DB initialization strategy, you could set the DB Initializer using Database class in Context class, as shown below:
public class ExampleDBContext: DbContext
{
public ExampleDBContext(): base("AConnectionString")
{
Database.SetInitializer<ExampleDBContext>(new CreateDatabaseIfNotExists<ExampleDBContext>());
}
}
if you provide a null value you turn off this feature. See the links provided if you want an alternative configuration approach.
I got an error using ASP.NET Identity in my app.
Multiple object sets per type are not supported. The object sets
'Identity Users' and 'Users' can both contain instances of type
'Recommendation Platform.Models.ApplicationUser'.
I saw a few questions about this error in StackOverflow. All indicate on two DbSet objects of the same type. But in my DbContext there aren't the same types of DbSets. Exception is thrown on FindAsync() method during logging in.
if (ModelState.IsValid)
var user = await UserManager.FindAsync(model.UserName, model.Password);
if (user != null && user.IsConfirmed)
{
The problem is I don't have two DbSets of the same type. My Contexts look like this:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
public System.Data.Entity.DbSet<RecommendationPlatform.Models.ApplicationUser> IdentityUsers { get; set; }
}
and
public class RecContext : DbContext
{
public RecContext()
: base("RecConnection")
{
Database.SetInitializer<RecContext>(new DropCreateDatabaseIfModelChanges<RecContext>());
}
public DbSet<Recommendation> Recommendations { get; set; }
public DbSet<Geolocation> Geolocations { get; set; }
public DbSet<Faq> Faqs { get; set; }
public DbSet<IndexText> IndexTexts { get; set; }
}
What could cause this problem? Maybe something connected with in-built ASP.NET Identity functionalities? Anyway, what is Users type? I don't have it in my app...
You do have two DbSets` of the same type.
IdentityDbContext<T> itself contains Users property declared as:
public DbSet<T> Users { get; set; }
You're declaring second one in your class.
review this file "ApplicationDbContext.cs", remove the line, generated automatically by scaffold last, should be like this:
public System.Data.Entity.DbSet<Manager.Models.ApplicationUser> IdentityUsers { get; set; }
This issue can arise from using scaffolding to create a View. You probably did something like this: View > Add > New Scaffold Item... > MVC 5 View > [Model class: ApplicationUser].
The scaffolding wizard added a new line of code in your ApplicationDbContext class.
public System.Data.Entity.DbSet<RecommendationPlatform.Models.ApplicationUser> IdentityUsers { get; set; }
Now you have two DbSet properties of the same type which not only causes an exeptions to be thrown in the FindAsync() method but also when you try to use code-first migrations.
Be very careful when using scaffolding or even better don't use it.
Comment the new generated Dbset from identity model class like below
// public System.Data.Entity.DbSet<SurveyTool.Models.ApplicationUser> ApplicationUsers { get; set; }
Whenever I see this problem, I always double check the DbSet. - ESPECIALLY if you are using another language for Visual Studio.
For us who use other language on VS, always double check because the program doesn´t create controllers or models with the exact name. perhaps this should be a thread.. or there is one already and I missed it.
I'm having an issue with the current project I'm working on. It works fine and runs without error with it's current classes and functionality. My issue is that I can't seem to add another model, it's corresponding controller and views and get it to actually work.
Usually I would simply add a new model class to the folder, update my dbcontext class with a dbset with the new model as datatype. Then write "update-database -force" in the package manager console and it would apply the automatic migrations. But for some strange reason I can't seem to comprehend, it simply won't do that anymore.
Instead, after I create the model and add the dbset and then trying to update database it runs the update fine, but it doesn't add any new migrations. The funny thing is when I run the project I get the usual error you always get when you have forgotten to update the database where it's recommending code first migrations etc.
I tried checking my config file and it seems the context key is set to applicationuser instead of the proper dbcontext class, which I'm sensing is why it doesn't detect any changes(usually it figures this out itself?). But when I try changing it to the proper one and updating the database again, it gives me an error saying something about asproles is already in the database?
I'm completely lost here and would appreciate any input an experienced vs13 user can give me.
EDIT:
I should mention I have been working on the identity framework recently, which is probably why it has automatically changed the contextkey? But I haven't had any issues during that with any of my existing classes.
NEW EDIT (29-01-2015)
Relevant part of configuration file:
internal sealed class Configuration : DbMigrationsConfiguration<MEV3.Models.ApplicationDbContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
ContextKey = "MEV3.Models.QuestionContext";
}
protected override void Seed(MEV3.Models.ApplicationDbContext context)
{
// This method will be called after migrating to the latest version.
// You can use the DbSet<T>.AddOrUpdate() helper extension method
// to avoid creating duplicate seed data. E.g.
//
// context.People.AddOrUpdate(
// p => p.FullName,
// new Person { FullName = "Andrew Peters" },
// new Person { FullName = "Brice Lambson" },
// new Person { FullName = "Rowan Miller" }
// );
this.AddUserAndRoles();
}
My QuestionContext file:
namespace MEV3.Models
{
public class QuestionContext : DbContext
{
public DbSet<ExamSet> ExamSets { get; set; }
public DbSet<BlanketSet> BlanketSets { get; set; }
public DbSet<LooseQuestionCase> LooseQuestionCases { get; set; }
public DbSet<Medcase> Medcases { get; set; }
public DbSet<Question> Questions { get; set; }
public DbSet<QuestionChoice> QuestionChoices { get; set; }
public DbSet<QuestionBeforeAfter> QuestionBeforeAfters { get; set; }
public DbSet<Answer> Answers { get; set; }
public DbSet<Tag> Tags { get; set; }
public DbSet<Category> Categories { get; set; }
public DbSet<Institution> Institutions { get; set; }
public DbSet<ExamType> ExamTypes { get; set; }
public DbSet<NewsArticle> NewsArticles { get; set; }
//public DbSet<TaskRecord> TaskRecords { get; set; }
//protected override void OnModelCreating(DbModelBuilder modelBuilder)
//{
// modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
//}
}
My ApplicationDbContext file:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
public DbSet<TaskRecord> TaskRecords { get; set; }
}
When I have the contextkey set to my QuestionContext and try to make changes to any models in that dbset, it gives me an error about the taskrecords (which is strange cuz they are inside the applicationdbcontext class).
This means I can't make any succesful updates of the database if I try to change a model in the QuestionContext.
If I set the contextkey to ApplicationDbContext I can make changes to anything in the applicationdbcontext succesfully, but it wont register any changes done in any of the models in the QuestionContext.
I'm at a loss, the way it works now it seems I can only add new models to the applicationdbcontext or change models in it, can't go back and alter anything in the questioncontext. Any thoughts?
Automatic migrations are succesfully enabled on both contexts btw. I've used this setup succesfully before I started fiddling with the identityframework.
Your application has more than one context, therefore you should tell Update-Migration which configuration to chose from. If the two contexts are in different projects, you can use the -ProjectName <string> command. Otherwise, use the -ConfigurationTypeName <string> command, making sure your configuration classes have different names. Also, make sure your QuestionContext's Configuration is public. More information here.
Also, make sure the connection string is pointing to the right Database when you run the project (under your current build configuration).
Finally, I would recommend you Enable-Migrations as forcing it like you do can lead to data loss.