ASP.NET Kimlik DbContext karışıklığı


196

Varsayılan MVC 5 uygulaması IdentityModels.cs dosyasında bu kod parçasıyla birlikte gelir - bu kod parçası varsayılan şablonlar için tüm ASP.NET Kimlik işlemleri içindir:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext()
        : base("DefaultConnection")
    {
    }
}

Entity Framework ile görünümleri kullanarak yeni bir denetleyici yapıp iletişim kutusunda "Yeni veri içeriği ..." oluşturursam, bu benim için oluşturulur:

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;

namespace WebApplication1.Models
{
    public class AllTheOtherStuffDbContext : DbContext
    {
        // You can add custom code to this file. Changes will not be overwritten.
        // 
        // If you want Entity Framework to drop and regenerate your database
        // automatically whenever you change your model schema, please use data migrations.
        // For more information refer to the documentation:
        // http://msdn.microsoft.com/en-us/data/jj591621.aspx

        public AllTheOtherStuffDbContext() : base("name=AllTheOtherStuffDbContext")
        {
        }

        public System.Data.Entity.DbSet<WebApplication1.Models.Movie> Movies { get; set; }

    }
} 

EF kullanarak başka bir denetleyici + görünümü iskele edersem, örneğin bir Hayvan modeli için, bu yeni satırın altında otomatik olarak oluşturulacaktı public System.Data.Entity.DbSet<WebApplication1.Models.Movie> Movies { get; set; }:

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;

namespace WebApplication1.Models
{
    public class AllTheOtherStuffDbContext : DbContext
    {
        // You can add custom code to this file. Changes will not be overwritten.
        // 
        // If you want Entity Framework to drop and regenerate your database
        // automatically whenever you change your model schema, please use data migrations.
        // For more information refer to the documentation:
        // http://msdn.microsoft.com/en-us/data/jj591621.aspx

        public AllTheOtherStuffDbContext() : base("name=AllTheOtherStuffDbContext")
        {
        }

        public System.Data.Entity.DbSet<WebApplication1.Models.Movie> Movies { get; set; }
        public System.Data.Entity.DbSet<WebApplication1.Models.Animal> Animals { get; set; }

    }
} 

ApplicationDbContext(tüm ASP.NET Identity şeyleri için) miras IdentityDbContextalır DbContext. AllOtherStuffDbContext(kendi eşyalarım için) miras DbContext.

Benim sorum şu:

Diğer tüm kendi modellerim için bu ikisinden ( ApplicationDbContextve AllOtherStuffDbContext) hangisini kullanmalıyım? Ya ApplicationDbContextda temel sınıftan türetildiği için kullanmakta sorun olmaması gerektiğinden , varsayılan otomatik oluşturulmuş kullanmalı mıyım DbContextyoksa biraz ek yük olacak mı? Sadece bir tane kullanmalısınDbContextTüm modelleriniz için uygulamanızda nesne (bunu bir yerde okudum), bu yüzden her ikisini de ApplicationDbContextve AllOtherStuffDbContexttek bir uygulamada kullanmayı düşünmemeliyim ? Veya ASP.NET Identity ile MVC 5'te en iyi uygulama hangisidir?


1
Bu arada; bu aşırı ve belge tararken gözlerim için gereksiz: public System.Data.Entity.DbSet <WebApplication1.Models.Movie> Movies {get; Ayarlamak; } - System.Data.Entity ve WebApplication1.Models bölümü. Bildirgeden kaldırılamaz ve bunun yerine using ifadeleri bölümüne ad alanları eklenemez mi?
pussinboots

Kedi - Yorumunuza evet. Bu iyi çalışmalı.
SB2055

Bu, MongoDB.Driver için Entity Framework (> = v2.1.0) github.com/saan800/SaanSoft
Stanislav Prusac

Yanıtlar:


178

IdentityDbContext devralma tek bir Context sınıfı kullanırsınız. Bu şekilde, bağlamınız, sınıflarınız ile IdentityUser ve IdentityDbContext'in Rolleri arasındaki ilişkilerin farkında olabilirsiniz. IdentityDbContext'te çok az ek yük var, temelde iki DbSet ile düzenli bir DbContext. Biri kullanıcılar, diğeri roller için.


52
Bu tek bir MVC5 projesi içindir, ancak türetilen DbContext, bazıları MVC5 değil, bazıları Kimlik desteğine ihtiyaç duymayan birden fazla proje arasında paylaşıldığında istenmez.
Dave

Daha kolay bakım ve daha iyi ilişkisel bütünlük için aynı veritabanına oy verildi. Çünkü kullanıcı varlığı ve rol varlığı kolayca diğer uygulama nesneleriyle ilişkilendirilecektir.
anIBMer

6
@Dave - İki farklı bağlam kullanarak kullanıcı verilerinin bölümlenmesini zorlaştırır. MVC uygulama bölüm verilerinizi kullanıcıya göre yapar, ancak diğer uygulamalar yapmaz. Aynı veri katmanını paylaşmak yaygındır, ancak bazı projelerin kullanıcı tarafından bölümlendirilmiş verilere ihtiyaç duyduğunu ve bazılarının gerekmediğini düşünmüyorum.
RickAndMSFT

1
Herkes bir MVC projesinden ApplicationDBContext ayıklama ve mevcut bir EF veri katmanına dahil nasıl bir nasıl biliyor? İkisini birleştirmek, yukarıda açıklandığı gibi doğru yaklaşım gibi görünüyor, ancak zaman kısıtlamalı bir proje üzerinde çalışıyorum. İlk seferinde doğru yapmak istiyorum ama önümde yatan tüm
gotchas

7
Yaklaşık bir saat aradıktan sonra bu cevap bana doğru yönü gösterdi - ama nasıl uygulanacağından emin değildim (çok gerçek bir kişi için). Bu yüzden başka birine yardımcı olursa, en basit yolu IdentityModels.cs açmak ve yeni DbSet ApplicationDbContext sınıf eklemek olduğunu buldum.
SeanOB

45

Stackoverflow'da hızlı bir arama olan IdentityDbContext hakkında çok fazla karışıklık var ve şu soruları bulacaksınız:
" Asp.Net Identity IdentityDbContext neden bir Kara Kutu?
Visual Studio 2013 AspNet Identity kullanırken tablo adlarını nasıl değiştirebilirim?
MyDbContext'i IdentityDbContext ile Birleştir "

Tüm bu soruları yanıtlamak için IdentityDbContext'in sadece DbContext'ten miras alınan bir sınıf olduğunu anlamamız gerekir . IdentityDbContext kaynağına
bir göz atalım :

/// <summary>
/// Base class for the Entity Framework database context used for identity.
/// </summary>
/// <typeparam name="TUser">The type of user objects.</typeparam>
/// <typeparam name="TRole">The type of role objects.</typeparam>
/// <typeparam name="TKey">The type of the primary key for users and roles.</typeparam>
/// <typeparam name="TUserClaim">The type of the user claim object.</typeparam>
/// <typeparam name="TUserRole">The type of the user role object.</typeparam>
/// <typeparam name="TUserLogin">The type of the user login object.</typeparam>
/// <typeparam name="TRoleClaim">The type of the role claim object.</typeparam>
/// <typeparam name="TUserToken">The type of the user token object.</typeparam>
public abstract class IdentityDbContext<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TRoleClaim, TUserToken> : DbContext
    where TUser : IdentityUser<TKey, TUserClaim, TUserRole, TUserLogin>
    where TRole : IdentityRole<TKey, TUserRole, TRoleClaim>
    where TKey : IEquatable<TKey>
    where TUserClaim : IdentityUserClaim<TKey>
    where TUserRole : IdentityUserRole<TKey>
    where TUserLogin : IdentityUserLogin<TKey>
    where TRoleClaim : IdentityRoleClaim<TKey>
    where TUserToken : IdentityUserToken<TKey>
{
    /// <summary>
    /// Initializes a new instance of <see cref="IdentityDbContext"/>.
    /// </summary>
    /// <param name="options">The options to be used by a <see cref="DbContext"/>.</param>
    public IdentityDbContext(DbContextOptions options) : base(options)
    { }

    /// <summary>
    /// Initializes a new instance of the <see cref="IdentityDbContext" /> class.
    /// </summary>
    protected IdentityDbContext()
    { }

    /// <summary>
    /// Gets or sets the <see cref="DbSet{TEntity}"/> of Users.
    /// </summary>
    public DbSet<TUser> Users { get; set; }

    /// <summary>
    /// Gets or sets the <see cref="DbSet{TEntity}"/> of User claims.
    /// </summary>
    public DbSet<TUserClaim> UserClaims { get; set; }

    /// <summary>
    /// Gets or sets the <see cref="DbSet{TEntity}"/> of User logins.
    /// </summary>
    public DbSet<TUserLogin> UserLogins { get; set; }

    /// <summary>
    /// Gets or sets the <see cref="DbSet{TEntity}"/> of User roles.
    /// </summary>
    public DbSet<TUserRole> UserRoles { get; set; }

    /// <summary>
    /// Gets or sets the <see cref="DbSet{TEntity}"/> of User tokens.
    /// </summary>
    public DbSet<TUserToken> UserTokens { get; set; }

    /// <summary>
    /// Gets or sets the <see cref="DbSet{TEntity}"/> of roles.
    /// </summary>
    public DbSet<TRole> Roles { get; set; }

    /// <summary>
    /// Gets or sets the <see cref="DbSet{TEntity}"/> of role claims.
    /// </summary>
    public DbSet<TRoleClaim> RoleClaims { get; set; }

    /// <summary>
    /// Configures the schema needed for the identity framework.
    /// </summary>
    /// <param name="builder">
    /// The builder being used to construct the model for this context.
    /// </param>
    protected override void OnModelCreating(ModelBuilder builder)
    {
        builder.Entity<TUser>(b =>
        {
            b.HasKey(u => u.Id);
            b.HasIndex(u => u.NormalizedUserName).HasName("UserNameIndex").IsUnique();
            b.HasIndex(u => u.NormalizedEmail).HasName("EmailIndex");
            b.ToTable("AspNetUsers");
            b.Property(u => u.ConcurrencyStamp).IsConcurrencyToken();

            b.Property(u => u.UserName).HasMaxLength(256);
            b.Property(u => u.NormalizedUserName).HasMaxLength(256);
            b.Property(u => u.Email).HasMaxLength(256);
            b.Property(u => u.NormalizedEmail).HasMaxLength(256);
            b.HasMany(u => u.Claims).WithOne().HasForeignKey(uc => uc.UserId).IsRequired();
            b.HasMany(u => u.Logins).WithOne().HasForeignKey(ul => ul.UserId).IsRequired();
            b.HasMany(u => u.Roles).WithOne().HasForeignKey(ur => ur.UserId).IsRequired();
        });

        builder.Entity<TRole>(b =>
        {
            b.HasKey(r => r.Id);
            b.HasIndex(r => r.NormalizedName).HasName("RoleNameIndex");
            b.ToTable("AspNetRoles");
            b.Property(r => r.ConcurrencyStamp).IsConcurrencyToken();

            b.Property(u => u.Name).HasMaxLength(256);
            b.Property(u => u.NormalizedName).HasMaxLength(256);

            b.HasMany(r => r.Users).WithOne().HasForeignKey(ur => ur.RoleId).IsRequired();
            b.HasMany(r => r.Claims).WithOne().HasForeignKey(rc => rc.RoleId).IsRequired();
        });

        builder.Entity<TUserClaim>(b => 
        {
            b.HasKey(uc => uc.Id);
            b.ToTable("AspNetUserClaims");
        });

        builder.Entity<TRoleClaim>(b => 
        {
            b.HasKey(rc => rc.Id);
            b.ToTable("AspNetRoleClaims");
        });

        builder.Entity<TUserRole>(b => 
        {
            b.HasKey(r => new { r.UserId, r.RoleId });
            b.ToTable("AspNetUserRoles");
        });

        builder.Entity<TUserLogin>(b =>
        {
            b.HasKey(l => new { l.LoginProvider, l.ProviderKey });
            b.ToTable("AspNetUserLogins");
        });

        builder.Entity<TUserToken>(b => 
        {
            b.HasKey(l => new { l.UserId, l.LoginProvider, l.Name });
            b.ToTable("AspNetUserTokens");
        });
    }
}


IdentityDbContext'i DbContext'imizle birleştirmek istiyorsak, kaynak koduna dayanarak iki seçeneğimiz vardır:

İlk Seçenek:
IdentityDbContext'ten devralan ve sınıflara erişimi olan bir DbContext oluşturun.

   public class ApplicationDbContext 
    : IdentityDbContext
{
    public ApplicationDbContext()
        : base("DefaultConnection")
    {
    }

    static ApplicationDbContext()
    {
        Database.SetInitializer<ApplicationDbContext>(new ApplicationDbInitializer());
    }

    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }

    // Add additional items here as needed
}


Ekstra notlar:

1) Aşağıdaki çözümle asp.net Identity default tablo adlarını da değiştirebiliriz:

    public class ApplicationDbContext : IdentityDbContext
    {    
        public ApplicationDbContext(): base("DefaultConnection")
        {
        }

        protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.Entity<IdentityUser>().ToTable("user");
            modelBuilder.Entity<ApplicationUser>().ToTable("user");

            modelBuilder.Entity<IdentityRole>().ToTable("role");
            modelBuilder.Entity<IdentityUserRole>().ToTable("userrole");
            modelBuilder.Entity<IdentityUserClaim>().ToTable("userclaim");
            modelBuilder.Entity<IdentityUserLogin>().ToTable("userlogin");
        }
    }

2) Ayrıca her sınıfı genişletebilir ve 'IdentityUser', 'IdentityRole', ... gibi sınıflara herhangi bir özellik ekleyebiliriz.

    public class ApplicationRole : IdentityRole<string, ApplicationUserRole>
{
    public ApplicationRole() 
    {
        this.Id = Guid.NewGuid().ToString();
    }

    public ApplicationRole(string name)
        : this()
    {
        this.Name = name;
    }

    // Add any custom Role properties/code here
}


// Must be expressed in terms of our custom types:
public class ApplicationDbContext 
    : IdentityDbContext<ApplicationUser, ApplicationRole, 
    string, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>
{
    public ApplicationDbContext()
        : base("DefaultConnection")
    {
    }

    static ApplicationDbContext()
    {
        Database.SetInitializer<ApplicationDbContext>(new ApplicationDbInitializer());
    }

    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }

    // Add additional items here as needed
}

Zaman kazanmak için AspNet Identity 2.0 Genişletilebilir Proje Şablonu'nu kullanarak tüm sınıfları genişletebiliriz.

İkinci seçenek:(Önerilmez)
Tüm kodu kendimiz yazarsak aslında IdentityDbContext'ten miras almak zorunda değiliz.
Yani temelde DbContext'den miras alabiliriz ve "OnModelCreating (ModelBuilder builder)" özel sürümümüzü IdentityDbContext kaynak kodundan uygulayabiliriz


2
@ mike-devenney İşte, iki bağlam katmanını birleştirmeyle ilgili cevabınız gider, umarım yardımcı olur.
Arvand

1
Teşekkürler Arvand, bunu kaçırdım ve 1,5 yıl sonra tekrar konuya bakarken garip bir şekilde tökezledi. :)
Mike Devenney

9

Bu millet için geç bir giriş, ama aşağıda benim uygulama. Ayrıca KEYs varsayılan türünü değiştirme yeteneğini çıkardığımı fark edeceksiniz: ilgili ayrıntılar aşağıdaki makalelerde bulunabilir:

NOTLAR: Anahtarlarınız için
kullanamayacağınıza dikkat edilmelidir Guid's. Bunun nedeni, kaputun altında bir olduklarıdır Structve bu nedenle, genel bir dönüşümden dönüşümüne izin verecek hiçbir kutulama içermez.<TKey> parametreden .

SINIFLAR SEVİYOR:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser, CustomRole, string, CustomUserLogin, CustomUserRole, CustomUserClaim>
{
    #region <Constructors>

    public ApplicationDbContext() : base(Settings.ConnectionString.Database.AdministrativeAccess)
    {
    }

    #endregion

    #region <Properties>

    //public DbSet<Case> Case { get; set; }

    #endregion

    #region <Methods>

    #region

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

        //modelBuilder.Configurations.Add(new ResourceConfiguration());
        //modelBuilder.Configurations.Add(new OperationsToRolesConfiguration());
    }

    #endregion

    #region

    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }

    #endregion

    #endregion
}

    public class ApplicationUser : IdentityUser<string, CustomUserLogin, CustomUserRole, CustomUserClaim>
    {
        #region <Constructors>

        public ApplicationUser()
        {
            Init();
        }

        #endregion

        #region <Properties>

        [Required]
        [StringLength(250)]
        public string FirstName { get; set; }

        [Required]
        [StringLength(250)]
        public string LastName { get; set; }

        #endregion

        #region <Methods>

        #region private

        private void Init()
        {
            Id = Guid.Empty.ToString();
        }

        #endregion

        #region public

        public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser, string> 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;
        }

        #endregion

        #endregion
    }

    public class CustomUserStore : UserStore<ApplicationUser, CustomRole, string, CustomUserLogin, CustomUserRole, CustomUserClaim>
    {
        #region <Constructors>

        public CustomUserStore(ApplicationDbContext context) : base(context)
        {
        }

        #endregion
    }

    public class CustomUserRole : IdentityUserRole<string>
    {
    }

    public class CustomUserLogin : IdentityUserLogin<string>
    {
    }

    public class CustomUserClaim : IdentityUserClaim<string> 
    { 
    }

    public class CustomRoleStore : RoleStore<CustomRole, string, CustomUserRole>
    {
        #region <Constructors>

        public CustomRoleStore(ApplicationDbContext context) : base(context)
        {
        } 

        #endregion
    }

    public class CustomRole : IdentityRole<string, CustomUserRole>
    {
        #region <Constructors>

        public CustomRole() { }
        public CustomRole(string name) 
        { 
            Name = name; 
        }

        #endregion
    }

8

IdentityDbContext'in soyutlamalarını incelerseniz, bunun türetilmiş DbContext'inize benzediğini göreceksiniz. En kolay yol Olav'ın cevabıdır, ancak neyin yaratıldığı üzerinde daha fazla kontrol istiyorsanız ve Kimlik paketlerine biraz daha az bağımlı olmak, burada soruma ve cevabına bir göz atın . Bağlantıyı izlerseniz bir kod örneği vardır, ancak özet olarak gerekli DbSet'leri kendi DbContext alt sınıfınıza eklersiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.