Zapnologica
Zapnologica

Reputation: 22556

Replace default IdentityDbContext with existing database information

I need to add a ASP.NET web API to a system which already has an active user database.

From my experience of using the default Identity built into the asp.net it generally creates the tables from scratch using a code-first approach. This is obviously not the approach I am looking for.

How would I go about pointing the Identity Model to my current database and not creating a new one?

I cant seem to find a decent tutorial or how to on-line?

I assume that I would have to implement an interface some where in the identity package so that I provide the bare minimum fields required for normal operation.

I would like to do this for user roles and user identity.

Do I import current database into code first?

{Edit}

Here is a link to my tables: ERD

Upvotes: 2

Views: 4287

Answers (3)

wilson0x4d
wilson0x4d

Reputation: 8749

Another option may be to subclass Microsoft.AspNet.Identity.EntityFramework.IdentityDbContext<Models.ApplicationUser>, this obviously will only apply when you are not already subclassing another DbContext-derived type.

For example:

public class MyDbContext : IdentityDbContext<ApplicationUser>, IMyDbContext
{
    public IDbSet<MyOtherEntity> OtherEntities { get; set; }
    public MyDbContext() : base("MyDb", false)
    {
        Database.SetInitializer<MyDbContext>(null);
    }
    public static DbContext Create()
    {
        return new MyDbContext();
    }
}

You will need to edit Startup.Auth.cs and update the app.CreatePerOwinContext call to instead refer to the Create() method of your new DbContext type (or whatever DI-based allocator you're already using in your established, mature products.) Just keep in mind that the type returned by the method you provide here is the type used for future Owin context Get() calls.

Thus, you will also need to edit IdentityConfig.cs to instead refer to the MyDbContext type (instead of ApplicationDbContext). Otherwise this method will fail. If, after editing, you get a null reference exception here then you have not properly edited Startup.Auth.cs (see last paragraph.)

You will also be wise to comment-out the default scaffold ApplicationDbContext located in IdentityModels.cs so other developers don't become confused in the future, it should also help you identify any lingering references (there shouldn't be any.)

The above should be sufficient for most common cases, in cases where your existing DB schema differs from what would be generated by default you can override OnModelCreating as outlined in the accepted answer from @pwdst (in addition to what is detailed above.)

Upvotes: 0

Chris Pratt
Chris Pratt

Reputation: 239270

IdentityDbContext is no different than a normal DbContext as far as this goes. If you don't want it to create the tables, then you simply treat it as database-first (yes, you can confusingly have a database-first code-first project).

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext()
        : base("ConnectionStringName")
    {
        Database.SetInitializer<ApplicationDbContext>(null);
    }
}

You will of course have to keep this context separate from your application context you do migrations against, but now it will no longer try to create a database or generate tables.

Upvotes: 3

pwdst
pwdst

Reputation: 13735

It is possible to map the ASP.NET Identity objects and properties by overriding the OnModelCreating event. This will look something like-

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    var user = modelBuilder.Entity<IdentityUser>().HasKey(u => u.Id).ToTable("User", "Users"); //Specify our our own table names instead of the defaults

    user.Property(iu => iu.Id).HasColumnName("Id");
    user.Property(iu => iu.UserName).HasColumnName("UserName");
    user.Property(iu => iu.Email).HasColumnName("EmailAddress").HasMaxLength(254).IsRequired();
    user.Property(iu => iu.IsConfirmed).HasColumnName("EmailConfirmed");
    user.Property(iu => iu.PasswordHash).HasColumnName("PasswordHash");
    user.Property(iu => iu.SecurityStamp).HasColumnName("SecurityStamp");

    user.HasMany(u => u.Roles).WithRequired().HasForeignKey(ur => ur.UserId);
    user.HasMany(u => u.Claims).WithRequired().HasForeignKey(uc => uc.UserId);
    user.HasMany(u => u.Logins).WithRequired().HasForeignKey(ul => ul.UserId);
    user.Property(u => u.UserName).IsRequired();
...

By using "ToTable" for each entity and "HasColumnName" for each property you should be able to map to your own schema.

The obvious problem is that this will only allow you to change the column and table names, and not the column types.

You can extend the IdentityUser class to add new properties that match the additional database columns in your schema, and you can change the primary key type for users and roles (see http://aspnet.codeplex.com/SourceControl/latest#Samples/Identity/ChangePK/readme.txt), but the core column types and information would need to be the same. This is most likely to be a problem with the password hash column, which actually contains both the hash and the salt in the case of ASP.NET Identity.

If you have existing user credentials you may need to translate them - the process for doing this from SQL Membership is given at http://www.asp.net/identity/overview/migrations/migrating-an-existing-website-from-sql-membership-to-aspnet-identity and using the scripts at https://aspnet.codeplex.com/SourceControl/latest#Samples/Identity/SQLMembership-Identity-OWIN/Migrations.sql

The key point of the process is-

The passwords of the users of the application are encrypted and stored in the database. The crypto algorithm used in SQL membership is different from the one in the new Identity system. To reuse old passwords we need to selectively decrypt passwords when old users log in using the SQL memberships algorithm while using the crypto algorithm in Identity for the new users.

It may also be possible to retain the password temporarily after successful login using the previous membership algorithm and then use the plaintext string to add the password to the new ASP.NET Identity password column using the ASP.NET Identity hashing and user manager mechanism - but you should be extremely careful about passing around the plain text credentials in this situation. You should also destroy the (more weakly hashed) credentials from the previous system after successful migration in case of future breach (and in case the credentials have not been changed since migration to the significantly more robustly hashed ASP.NET Identity column).

Upvotes: 4

Related Questions