I\'m using ASP.NET Identity (Database First) in my ASP.NET MVC application. I followed the instructions here, to set up the ASP.NET Identity with database first approach.
Concern:
Solution:
Disclaimer!!! I understand that the concern is for database first; however, this solution is only for code first approach. But hey, it works!
Here I provide a walk through on how to do this. Please make sure you have all the dependencies in top margin of your code.
Step 1: Add
public virtual DbSet<ModelNameOfInterest> ModelNameOfInterest { get; set; }
to public class ApplicationDbContext : IdentityDbContext<ApplicationUser>{}
as seen in code below.
using System.Data.Entity;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System.ComponentModel.DataAnnotations.Schema;
namespace AwesomeCode.Models
{
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
public class ApplicationUser : IdentityUser
{
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;
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
//A virtul DbSet in order to interact with the autogenerated code the identity framewrok produces.
public virtual DbSet<ModelNameOfInterest> ModelNameOfInterest { get; set; }
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
}
Step 2: Add public virtual ApplicationUser ApplicationUser { get; set; }
to your model that you want to create a relationship with as seen code below.
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Web;
namespace AwesomeCode.Models
{
public class WorkExp
{
[Key]
public int Id { get; set; }
public string JobTitle { get; set; }
//Create foreign key with reference to ApplicationUser_Id that was auto-generated by entity framework.
public virtual ApplicationUser ApplicationUser { get; set; }
}
}
Step 3: Given that you set up your connection string for your database, you need to produce a migration. Path to Package Manager Console: Tools->NuGet Packer Manager->Package Manager Console
PM>
, type Enable-Migrations
you should see a migration folder with two files. PM>
, type Update-Database
You should see tables in your database now. PM>
, type Add-Migration
After Name:
, type InitialCreate
or Your model of interest
You should see tables in your database now. You should see tables in your database now.
Step 4: Double check that the model of interest's foreign key is properly referenced to the AspNetUser table. In MS Management Studio, you can create a relational diagram to show the references. You can find how to do that on google.
Step 5: As always stay cool, calm, and collected.
I cannot reproduce the issue, even when I create the tables in another database without keys and relations. So I'm sure that there is a problem with your model. Unfortunately you didn't add code which I can compare, so I can't tell what is different and answer the question directly. The only thing I can do is to show what works for me. However, first I have some remarks.
I think you shouldn't follow the article. As there is no reason to add the context to an existing database.
Like Ivan Stoev mentioned you are not supposed to mix contexts. The Identity context is meant to authenticate the user. It stores the credentials, the roles of the user and claims. Where claims are meant to add identity information about the user.
In fact, the default Hometown
field of the ApplicationUser template can be removed, as it is an identity claim which should be stored in the AspNetUserClaims table. Not something you need to extend the ApplicationUser for. Actually I can't think of any reason to extend the ApplicationUser.
About the roles, these are not really claims, as they tell nothing about the identity but rather are used for authorization. That's why it's fine that they are stored in the AspNetUserRoles table. Unfortunately roles are added to the identity as role claims, which makes things confusing.
Please note that the Identity information is present in the claims. This means that the application doesn't have to call the Identity context. E.g. User.IsInRole checks the role claims of the current identity, not the roles stored in the table.
About the different contexts, the other context (which I usually call the business model) has nothing in common with the Identity context. Email and other fields are not part, nor have meaning to the business model. You may think that those fields are redundant, but in fact they are not. I could login using a google account, but for the business use my work email address.
There are several reasons to keep the context seperated.
As also mentioned in the article:
At this point if you need to add any relationships (E.g. foreign keys) from your own tables to these tables you are welcome to do so but do not modify any of the Entity Framework 2.0 tables directly or later on any of their POCO classes. Doing so will result in errors based upon feedback I’ve received.
So how to manage the information if you shouldn't access the identity context from your application?
For the current user you don't need to access the users table. All the information is present in the identity claims. The only reason to access the identity context is to allow a user to login. Besides user management.
You can suffice by adding a reference to the user (the userid). If you need to show information of other users (like name) in a report, then create a user table in your business context to store the information. You can add relations to this table, as it is part of the same context.
Please let me know if you have questions about this approach.
Now the code that works for me. Like others have mentioned, it is not likely that adding the line:
public ICollection<Employee> Employees { get; set; }
is the cause. Without the virtual
keyword I think it is even ignored (remains null).
When I follow the steps of the article then I end up with the following model:
public class ApplicationUser : IdentityUser
{
public string Hometown { get; set; }
//public virtual ICollection<Employee> Employees { 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;
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
// Disable migrations
//Database.SetInitializer<ApplicationDbContext>(null);
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
And then I add the Employee class and uncomment the line in the ApplicationUser class above:
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
//public virtual ApplicationUser ApplicationUser { get; set; }
public string ApplicationUserId { get; set; }
}
In the database I added the table:
CREATE TABLE [dbo].[Employees](
[Id] [int] NOT NULL,
[Name] [varchar](50) NOT NULL,
[ApplicationUserId] [nvarchar](128) NOT NULL,
PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
You can use the [ForeignKey]
attribute to use a different field name.
You can try this or choose to keep both contexts seperated instead.