asp.net-mvc شرح - ASP.NET الارتباك DbContext الهوية




entity framework (5)

يمكنني استخدام فئة سياق مفرد ترث من IdentityDbContext. بهذه الطريقة يمكن أن يكون السياق على علم بأي علاقات بين الفئات الخاصة بك و IdentityUser والأدوار في IdentityDbContext. يوجد مقدار حمل قليل جداً في IdentityDbContext ، وهو في الأساس DbContext عادي مع اثنين من DbSets. واحد للمستخدمين وواحد للأدوار.

يأتي تطبيق MVC 5 الافتراضي مع قطعة التعليمات البرمجية هذه في IdentityModels.cs - هذه القطعة من التعليمات البرمجية مخصصة لكافة عمليات ASP.NET Identity للنماذج الافتراضية:

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

إذا قمت بسقِطة جهاز تحكم جديد باستخدام طرق العرض باستخدام Entity Framework وإنشاء "سياق بيانات جديد ..." في مربع الحوار ، فإن هذا يتم إنشاؤه بالنسبة لي:

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 ، لنفترض على سبيل المثال لنموذج حيواني ، فإن هذا الخط الجديد سيحصل على autogenerated مباشرة تحت public System.Data.Entity.DbSet<WebApplication1.Models.Movie> Movies { get; set; } public System.Data.Entity.DbSet<WebApplication1.Models.Movie> Movies { get; set; } 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 (لكافة الأشياء DbContext ASP.NET) من IdentityDbContext الذي يرث بدوره من DbContext . AllOtherStuffDbContext (من أجل الأشياء الخاصة بي) من DbContext .

لذلك سؤالي هو:

أي من هذين الاثنين ( ApplicationDbContext و AllOtherStuffDbContext ) يجب أن أستخدمه لكافة الطُرز الخاصة بي الأخرى؟ أو يجب فقط استخدام الافتراضي ApplicationDbContext autogenerated لأنه لا يجب أن يكون مشكلة في استخدامه لأنه مشتق من فئة الأساس DbContext ، أو سيكون هناك بعض الحمل؟ يجب عليك استخدام كائن DbContext واحد فقط في تطبيقك لجميع النماذج الخاصة بك (لقد قرأت هذا في مكان ما) لذلك لا ينبغي حتى النظر في استخدام كل من ApplicationDbContext و AllOtherStuffDbContext في تطبيق واحد؟ أو ما هو أفضل الممارسات في MVC 5 مع هوية ASP.NET؟


هناك الكثير من الارتباك حول IdentityDbContext ، بحث سريع في وستجد هذه الأسئلة:
" لماذا Asp.Net Identity IdentityDbContext a Black-Box؟
كيف يمكنني تغيير أسماء الجداول عند استخدام Visual Studio 2013 AspNet Identity؟
دمج MyDbContext مع IdentityDbContext "

للإجابة على كل هذه الأسئلة ، نحتاج فقط إلى فهم أن IdentityDbContext هو مجرد فئة موروثة من DbContext.
دعونا نلقي نظرة على مصدر IdentityDbContext :

/// <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 مع DbContext لدينا خياران:

الخيار الأول:
إنشاء DbContext الذي يرث من IdentityDbContext ولديك حق الوصول إلى الفئات.

   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
}


ملاحظات إضافية:

1) يمكننا أيضًا تغيير أسماء الجداول الافتراضية لتعريف asp.net بالحل التالي:

    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) علاوة على ذلك يمكننا تمديد كل فئة وإضافة أي خاصية إلى فئات مثل 'IdentityUser' ، 'IdentityRole' ، ...

    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
}

لتوفير الوقت ، يمكننا استخدام "قالب Project Extensible 2.0 المتوافق مع AspNet Identity" لتوسيع كل الفئات.

الخيار الثاني: (غير مستحسن)
نحن في الواقع لا نحتاج إلى أن نرث من IdentityDbContext إذا كنا نكتب كل الشفرات أنفسنا.
لذلك يمكننا ببساطة أن نرث من DbContext وننفّذ إصدارنا المخصص من "OnModelCreating (ModelBuilder builder)" من كود مصدر IdentityDbContext


هذا هو دخول متأخر للناس ، ولكن أدناه هو تنفيذي. ستلاحظ أيضًا أنني قمت بإدراجه القدرة على تغيير نوع KEYs الافتراضي: التفاصيل حول التي يمكن العثور عليها في المقالات التالية:

ملاحظات:
تجدر الإشارة إلى أنه لا يمكنك استخدام Guid's . ويرجع ذلك إلى أنه تحت غطاء المحرك ، فهي عبارة عن Struct ، وعلى هذا النحو ، لا تحتوي على <TKey> مما يسمح <TKey> معلمة <TKey> عامة.

الصفوف تبدو مثل:

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
    }

إذا قمت بالانتقال من خلال تجريد IdentityDbContext ستجد أنه يبدو تماماً مثل DbContext المشتقة الخاص بك. أسهل طريقة هي إجابة أولاف ، ولكن إذا كنت تريد المزيد من التحكم في ما يتم إنشاؤه ، فإن الاعتماد قليلاً على حزم الهوية قد ألق نظرة على سؤالي والإجابة عليه هنا . هناك مثال التعليمات البرمجية إذا قمت باتباع الارتباط ، ولكن في ملخص تقوم فقط بإضافة DbSets المطلوبة إلى فئة فرعية DbContext الخاصة بك.


قضيت عدة ساعات في العمل من خلال هذا وأخيراً وجدت حلاً قمت بمشاركته على مدونتي here . في الأساس ، تحتاج إلى القيام بكل شيء يقال في جواب ولكن مع شيء إضافي واحد: التأكد من أن Identity Framework يحتوي على سلسلة اتصال SQL-Client محددة أعلى سلسلة اتصال Entity Framework المستخدمة في كيانات التطبيق الخاصة بك.

باختصار ، سيستخدم تطبيقك سلسلة اتصال لـ Identity Framework وآخر لكيانات تطبيقك. كل سلسلة اتصال من نوع مختلف. قراءة منشور مدونتي للحصول على برنامج تعليمي كامل.





asp.net asp.net-mvc entity-framework asp.net-mvc-5 asp.net-identity