اعمال تزریق وابستگی‌ها به مثال رسمی ASP.NET Identity
اندازه‌ی قلم متن
تخمین مدت زمان مطالعه‌ی مطلب: هفده دقیقه

پروژه‌ی ASP.NET Identity که نسل جدید سیستم Authentication و Authorization مخصوص ASP.NET است، دارای دو سری مثال رسمی است:
الف) مثال‌های کدپلکس
 ب) مثال نیوگت

در ادامه قصد داریم مثال نیوگت آن‌را که مثال کاملی است از نحوه‌ی استفاده از ASP.NET Identity در ASP.NET MVC، جهت اعمال الگوی واحد کار و تزریق وابستگی‌ها، بازنویسی کنیم.


پیشنیازها
- برای درک مطلب جاری نیاز است ابتدا دور‌ه‌ی مرتبطی را در سایت مطالعه کنید و همچنین با نحوه‌ی پیاده سازی الگوی واحد کار در EF Code First آشنا باشید.
- به علاوه فرض بر این است که یک پروژه‌ی خالی ASP.NET MVC 5 را نیز آغاز کرده‌اید و توسط کنسول پاور شل نیوگت، فایل‌های مثال Microsoft.AspNet.Identity.Samples را به آن افزوده‌اید:
 PM> Install-Package Microsoft.AspNet.Identity.Samples -Pre


ساختار پروژه‌ی تکمیلی

همانند مطلب پیاده سازی الگوی واحد کار در EF Code First، این پروژه‌ی جدید را با چهار اسمبلی class library دیگر به نام‌های
AspNetIdentityDependencyInjectionSample.DataLayer
AspNetIdentityDependencyInjectionSample.DomainClasses
AspNetIdentityDependencyInjectionSample.IocConfig
AspNetIdentityDependencyInjectionSample.ServiceLayer
تکمیل می‌کنیم.


ساختار پروژه‌ی AspNetIdentityDependencyInjectionSample.DomainClasses

مثال Microsoft.AspNet.Identity.Samples بر مبنای primary key از نوع string است. برای نمونه کلاس کاربران آن‌را به نام ApplicationUser در فایل Models\IdentityModels.cs می‌توانید مشاهده کنید. در مطلب جاری، این نوع پیش فرض، به نوع متداول int تغییر خواهد یافت. به همین جهت نیاز است کلاس‌های ذیل را به پروژه‌ی DomainClasses اضافه کرد:
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.AspNet.Identity.EntityFramework;
 
namespace AspNetIdentityDependencyInjectionSample.DomainClasses
{
  public class ApplicationUser : IdentityUser<int, CustomUserLogin, CustomUserRole, CustomUserClaim>
  {
   // سایر خواص اضافی در اینجا
 
   [ForeignKey("AddressId")]
   public virtual Address Address { get; set; }
   public int? AddressId { get; set; }
  }
}

using System.Collections.Generic;
 
namespace AspNetIdentityDependencyInjectionSample.DomainClasses
{
  public class Address
  {
   public int Id { get; set; }
   public string City { get; set; }
   public string State { get; set; }
 
   public virtual ICollection<ApplicationUser> ApplicationUsers { set; get; }
  }
}

using Microsoft.AspNet.Identity.EntityFramework;
 
namespace AspNetIdentityDependencyInjectionSample.DomainClasses
{
  public class CustomRole : IdentityRole<int, CustomUserRole>
  {
   public CustomRole() { }
   public CustomRole(string name) { Name = name; }
 
 
  }
}

using Microsoft.AspNet.Identity.EntityFramework;
 
namespace AspNetIdentityDependencyInjectionSample.DomainClasses
{
  public class CustomUserClaim : IdentityUserClaim<int>
  {
 
  }
}

using Microsoft.AspNet.Identity.EntityFramework;
 
namespace AspNetIdentityDependencyInjectionSample.DomainClasses
{
  public class CustomUserLogin : IdentityUserLogin<int>
  {
 
  }
}

using Microsoft.AspNet.Identity.EntityFramework;
 
namespace AspNetIdentityDependencyInjectionSample.DomainClasses
{
  public class CustomUserRole : IdentityUserRole<int>
  {
 
  }
}
در اینجا نحوه‌ی تغییر primary key از نوع string را به نوع int، مشاهده می‌کنید. این تغییر نیاز به اعمال به کلاس‌های کاربران و همچنین نقش‌های آن‌ها نیز دارد. به همین جهت صرفا تغییر کلاس ابتدایی ApplicationUser کافی نیست و باید کلاس‌های فوق را نیز اضافه کرد و تغییر داد.
بدیهی است در اینجا کلاس پایه کاربران را می‌توان سفارشی سازی کرد و خواص دیگری را نیز به آن افزود. برای مثال در اینجا یک کلاس جدید آدرس تعریف شده‌است که ارجاعی از آن در کلاس کاربران نیز قابل مشاهده است.
سایر کلاس‌های مدل‌های اصلی برنامه که جداول بانک اطلاعاتی را تشکیل خواهند داد نیز در آینده به همین اسمبلی DomainClasses اضافه می‌شوند.


ساختار پروژه‌ی AspNetIdentityDependencyInjectionSample.DataLayer جهت اعمال الگوی واحد کار

اگر به همان فایل Models\IdentityModels.cs ابتدایی پروژه که اکنون کلاس ApplicationUser آن‌را به پروژه‌ی DomainClasses منتقل کرده‌ایم، مجددا مراجعه کنید، کلاس DbContext مخصوص ASP.NET Identity نیز در آن تعریف شده‌است:
 public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
این کلاس را به پروژه‌ی DataLayer منتقل می‌کنیم و از آن به عنوان DbContext اصلی برنامه استفاده خواهیم کرد. بنابراین دیگر نیازی نیست چندین DbContext در برنامه داشته باشیم. IdentityDbContext، در اصل از DbContext مشتق شده‌است.
اینترفیس IUnitOfWork برنامه، در پروژه‌ی DataLayer چنین شکلی را دارد که نمونه‌ای از آن‌را در مطلب آشنایی با نحوه‌ی پیاده سازی الگوی واحد کار در EF Code First، پیشتر ملاحظه کرده‌اید.
using System.Collections.Generic;
using System.Data.Entity;
 
namespace AspNetIdentityDependencyInjectionSample.DataLayer.Context
{
  public interface IUnitOfWork
  {
   IDbSet<TEntity> Set<TEntity>() where TEntity : class;
   int SaveAllChanges();
   void MarkAsChanged<TEntity>(TEntity entity) where TEntity : class;
   IList<T> GetRows<T>(string sql, params object[] parameters) where T : class;
   IEnumerable<TEntity> AddThisRange<TEntity>(IEnumerable<TEntity> entities) where TEntity : class;
   void ForceDatabaseInitialize();
  }
}
اکنون کلاس ApplicationDbContext منتقل شده به DataLayer یک چنین امضایی را خواهد یافت:
public class ApplicationDbContext :
  IdentityDbContext<ApplicationUser, CustomRole, int, CustomUserLogin, CustomUserRole, CustomUserClaim>,
  IUnitOfWork
{
  public DbSet<Category> Categories { set; get; }
  public DbSet<Product> Products { set; get; }
  public DbSet<Address> Addresses { set; get; }
تعریف آن باید جهت اعمال کلاس‌های سفارشی سازی شده‌ی کاربران و نقش‌های آن‌ها برای استفاده از primary key از نوع int به شکل فوق، تغییر یابد. همچنین در انتهای آن مانند قبل، IUnitOfWork نیز ذکر شده‌است. پیاده سازی کامل این کلاس را از پروژه‌ی پیوست انتهای بحث می‌توانید دریافت کنید.
کار کردن با این کلاس، هیچ تفاوتی با DbContext‌های متداول EF Code First ندارد و تمام اصول آن‌ها یکی است.

در ادامه اگر به فایل App_Start\IdentityConfig.cs مراجعه کنید، کلاس ذیل در آن قابل مشاهده‌است:
 public class ApplicationDbInitializer : DropCreateDatabaseIfModelChanges<ApplicationDbContext>
نیازی به این کلاس به این شکل نیست. آن‌را حذف کنید و در پروژه‌ی DataLayer، کلاس جدید ذیل را اضافه نمائید:
using System.Data.Entity.Migrations;
 
namespace AspNetIdentityDependencyInjectionSample.DataLayer.Context
{
  public class Configuration : DbMigrationsConfiguration<ApplicationDbContext>
  {
   public Configuration()
   {
    AutomaticMigrationsEnabled = true;
    AutomaticMigrationDataLossAllowed = true;
   }
  }
}
در این مثال، بحث migrations به حالت خودکار تنظیم شده‌است و تمام تغییرات در پروژه‌ی DomainClasses را به صورت خودکار به بانک اطلاعاتی اعمال می‌کند. تا همینجا کار تنظیم DataLayer به پایان می‌رسد.


ساختار پروژ‌ه‌ی AspNetIdentityDependencyInjectionSample.ServiceLayer

در ادامه مابقی کلاس‌‌های موجود در فایل App_Start\IdentityConfig.cs را به لایه سرویس برنامه منتقل خواهیم کرد. همچنین برای آن‌ها یک سری اینترفیس جدید نیز تعریف می‌کنیم، تا تزریق وابستگی‌ها به نحو صحیحی صورت گیرد. اگر به فایل‌های کنترلر این مثال پیش فرض مراجعه کنید (پیش از تغییرات بحث جاری)، هرچند به نظر در کنترلرها، کلاس‌های موجود در فایل App_Start\IdentityConfig.cs تزریق شده‌اند، اما به دلیل عدم استفاده از اینترفیس‌ها، وابستگی کاملی بین جزئیات پیاده سازی این کلاس‌ها و نمونه‌های تزریق شده به کنترلرها وجود دارد و عملا معکوس سازی واقعی وابستگی‌ها رخ نداده‌است. بنابراین نیاز است این مسایل را اصلاح کنیم.

الف) انتقال کلاس ApplicationUserManager به لایه سرویس برنامه
کلاس ApplicationUserManager فایل App_Start\IdentityConfig.c را به لایه سرویس منتقل می‌کنیم:
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using AspNetIdentityDependencyInjectionSample.DomainClasses;
using AspNetIdentityDependencyInjectionSample.ServiceLayer.Contracts;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.DataProtection;
 
namespace AspNetIdentityDependencyInjectionSample.ServiceLayer
{
  public class ApplicationUserManager
   : UserManager<ApplicationUser, int>, IApplicationUserManager
  {
   private readonly IDataProtectionProvider _dataProtectionProvider;
   private readonly IIdentityMessageService _emailService;
   private readonly IApplicationRoleManager _roleManager;
   private readonly IIdentityMessageService _smsService;
   private readonly IUserStore<ApplicationUser, int> _store;
 
   public ApplicationUserManager(IUserStore<ApplicationUser, int> store,
    IApplicationRoleManager roleManager,
    IDataProtectionProvider dataProtectionProvider,
    IIdentityMessageService smsService,
    IIdentityMessageService emailService)
    : base(store)
   {
    _store = store;
    _roleManager = roleManager;
    _dataProtectionProvider = dataProtectionProvider;
    _smsService = smsService;
    _emailService = emailService;
 
    createApplicationUserManager();
   }
 
 
   public void SeedDatabase()
   {
   }
 
   private void createApplicationUserManager()
   {
    // Configure validation logic for usernames
    this.UserValidator = new UserValidator<ApplicationUser, int>(this)
    {
      AllowOnlyAlphanumericUserNames = false,
      RequireUniqueEmail = true
    };
 
    // Configure validation logic for passwords
    this.PasswordValidator = new PasswordValidator
    {
      RequiredLength = 6,
      RequireNonLetterOrDigit = true,
      RequireDigit = true,
      RequireLowercase = true,
      RequireUppercase = true,
    };
 
    // Configure user lockout defaults
    this.UserLockoutEnabledByDefault = true;
    this.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
    this.MaxFailedAccessAttemptsBeforeLockout = 5;
 
    // Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
    // You can write your own provider and plug in here.
    this.RegisterTwoFactorProvider("PhoneCode", new PhoneNumberTokenProvider<ApplicationUser, int>
    {
      MessageFormat = "Your security code is: {0}"
    });
    this.RegisterTwoFactorProvider("EmailCode", new EmailTokenProvider<ApplicationUser, int>
    {
      Subject = "SecurityCode",
      BodyFormat = "Your security code is {0}"
    });
    this.EmailService = _emailService;
    this.SmsService = _smsService;
 
    if (_dataProtectionProvider != null)
    {
      var dataProtector = _dataProtectionProvider.Create("ASP.NET Identity");
      this.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser, int>(dataProtector);
    }
   } 
  }
}
تغییراتی که در اینجا اعمال شده‌اند، به شرح زیر می‌باشند:
- متد استاتیک Create این کلاس حذف و تعاریف آن به سازنده‌ی کلاس منتقل شده‌اند. به این ترتیب با هربار وهله سازی این کلاس توسط IoC Container به صورت خودکار این تنظیمات نیز به کلاس پایه UserManager اعمال می‌شوند.
- اگر به کلاس پایه UserManager دقت کنید، به آرگومان‌های جنریک آن یک int هم اضافه شده‌است. این مورد جهت استفاده از primary key از نوع int ضروری است.
- در کلاس پایه UserManager تعدادی متد وجود دارند. تعاریف آن‌ها را به اینترفیس IApplicationUserManager منتقل خواهیم کرد. نیازی هم به پیاده سازی این متدها در کلاس جدید ApplicationUserManager نیست؛ زیرا کلاس پایه UserManager پیشتر آن‌ها را پیاده سازی کرده‌است. به این ترتیب می‌توان به یک تزریق وابستگی واقعی و بدون وابستگی به پیاده سازی خاص UserManager رسید. کنترلری که با IApplicationUserManager بجای ApplicationUserManager کار می‌کند، قابلیت تعویض پیاده سازی آن‌را جهت آزمون‌های واحد خواهد یافت.
- در کلاس اصلی ApplicationDbInitializer پیش فرض این مثال، متد Seed هم قابل مشاهده‌است. این متد را از کلاس جدید Configuration اضافه شده به DataLayer حذف کرده‌ایم. از این جهت که در آن از متدهای کلاس ApplicationUserManager مستقیما استفاده شده‌است. متد Seed اکنون به کلاس جدید اضافه شده به لایه سرویس منتقل شده و در آغاز برنامه فراخوانی خواهد شد. DataLayer نباید وابستگی به لایه سرویس داشته باشد. لایه سرویس است که از امکانات DataLayer استفاده می‌کند.
- اگر به سازنده‌ی کلاس جدید ApplicationUserManager دقت کنید، چند اینترفیس دیگر نیز به آن تزریق شده‌اند. اینترفیس IApplicationRoleManager را ادامه تعریف خواهیم کرد. سایر اینترفیس‌های تزریق شده مانند IUserStore، IDataProtectionProvider و IIdentityMessageService جزو تعاریف اصلی ASP.NET Identity بوده و نیازی به تعریف مجدد آن‌ها نیست. فقط کلاس‌های EmailService و SmsService فایل App_Start\IdentityConfig.c را نیز به لایه سرویس منتقل کرده‌ایم. این کلاس‌ها بر اساس تنظیمات IoC Container مورد استفاده، در اینجا به صورت خودکار ترزیق خواهند شد. حالت پیش فرض آن، وهله سازی مستقیم است که مطابق کدهای فوق به حالت تزریق وابستگی‌ها بهبود یافته‌است.


ب) انتقال کلاس ApplicationSignInManager به لایه سرویس برنامه
کلاس ApplicationSignInManager فایل App_Start\IdentityConfig.c را نیز به لایه سرویس منتقل می‌کنیم.
using AspNetIdentityDependencyInjectionSample.DomainClasses;
using AspNetIdentityDependencyInjectionSample.ServiceLayer.Contracts;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security;
 
namespace AspNetIdentityDependencyInjectionSample.ServiceLayer
{
  public class ApplicationSignInManager :
   SignInManager<ApplicationUser, int>, IApplicationSignInManager
  {
   private readonly ApplicationUserManager _userManager;
   private readonly IAuthenticationManager _authenticationManager;
 
   public ApplicationSignInManager(ApplicationUserManager userManager,
              IAuthenticationManager authenticationManager) :
    base(userManager, authenticationManager)
   {
    _userManager = userManager;
    _authenticationManager = authenticationManager;
   }
  }
}
در اینجا نیز اینترفیس جدید IApplicationSignInManager را برای مخفی سازی پیاده سازی کلاس پایه توکار SignInManager، اضافه کرده‌ایم. این اینترفیس دقیقا حاوی تعاریف متدهای کلاس پایه SignInManager است و نیازی به پیاده سازی مجدد در کلاس ApplicationSignInManager نخواهد داشت.


ج) انتقال کلاس ApplicationRoleManager به لایه سرویس برنامه
کلاس ApplicationRoleManager فایل App_Start\IdentityConfig.c را نیز به لایه سرویس منتقل خواهیم کرد:
using AspNetIdentityDependencyInjectionSample.DomainClasses;
using AspNetIdentityDependencyInjectionSample.ServiceLayer.Contracts;
using Microsoft.AspNet.Identity;
 
namespace AspNetIdentityDependencyInjectionSample.ServiceLayer
{
  public class ApplicationRoleManager : RoleManager<CustomRole, int>, IApplicationRoleManager
  {
   private readonly IRoleStore<CustomRole, int> _roleStore;
   public ApplicationRoleManager(IRoleStore<CustomRole, int> roleStore)
    : base(roleStore)
   {
    _roleStore = roleStore;
   }
 
 
   public CustomRole FindRoleByName(string roleName)
   {
    return this.FindByName(roleName); // RoleManagerExtensions
   }
 
   public IdentityResult CreateRole(CustomRole role)
   {
    return this.Create(role); // RoleManagerExtensions
   }
  }
}
روش کار نیز در اینجا همانند دو کلاس قبل است. اینترفیس جدید IApplicationRoleManager را که حاوی تعاریف متدهای کلاس پایه توکار RoleManager است، به لایه سرویس اضافه می‌کنیم. کنترلرهای برنامه با این اینترفیس بجای استفاده مستقیم از کلاس ApplicationRoleManager کار خواهند کرد.

تا اینجا کار تنظیمات لایه سرویس برنامه به پایان می‌رسد.


ساختار پروژه‌ی AspNetIdentityDependencyInjectionSample.IocConfig 

پروژه‌ی IocConfig جایی است که تنظیمات StructureMap را به آن منتقل کرده‌ایم:
using System;
using System.Data.Entity;
using System.Threading;
using System.Web;
using AspNetIdentityDependencyInjectionSample.DataLayer.Context;
using AspNetIdentityDependencyInjectionSample.DomainClasses;
using AspNetIdentityDependencyInjectionSample.ServiceLayer;
using AspNetIdentityDependencyInjectionSample.ServiceLayer.Contracts;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.Owin.Security;
using StructureMap;
using StructureMap.Web;
 
namespace AspNetIdentityDependencyInjectionSample.IocConfig
{
  public static class SmObjectFactory
  {
   private static readonly Lazy<Container> _containerBuilder =
    new Lazy<Container>(defaultContainer, LazyThreadSafetyMode.ExecutionAndPublication);
 
   public static IContainer Container
   {
    get { return _containerBuilder.Value; }
   }
 
   private static Container defaultContainer()
   {
    return new Container(ioc =>
    {
      ioc.For<IUnitOfWork>()
        .HybridHttpOrThreadLocalScoped()
        .Use<ApplicationDbContext>();
 
      ioc.For<ApplicationDbContext>().HybridHttpOrThreadLocalScoped().Use<ApplicationDbContext>();
      ioc.For<DbContext>().HybridHttpOrThreadLocalScoped().Use<ApplicationDbContext>();
 
      ioc.For<IUserStore<ApplicationUser, int>>()
       .HybridHttpOrThreadLocalScoped()
       .Use<UserStore<ApplicationUser, CustomRole, int, CustomUserLogin, CustomUserRole, CustomUserClaim>>();
 
      ioc.For<IRoleStore<CustomRole, int>>()
       .HybridHttpOrThreadLocalScoped()
       .Use<RoleStore<CustomRole, int, CustomUserRole>>();
 
      ioc.For<IAuthenticationManager>()
        .Use(() => HttpContext.Current.GetOwinContext().Authentication);
 
      ioc.For<IApplicationSignInManager>()
        .HybridHttpOrThreadLocalScoped()
        .Use<ApplicationSignInManager>();
 
      ioc.For<IApplicationUserManager>()
        .HybridHttpOrThreadLocalScoped()
        .Use<ApplicationUserManager>();
 
      ioc.For<IApplicationRoleManager>()
        .HybridHttpOrThreadLocalScoped()
        .Use<ApplicationRoleManager>();
 
      ioc.For<IIdentityMessageService>().Use<SmsService>();
      ioc.For<IIdentityMessageService>().Use<EmailService>();
      ioc.For<ICustomRoleStore>()
        .HybridHttpOrThreadLocalScoped()
        .Use<CustomRoleStore>();
 
      ioc.For<ICustomUserStore>()
        .HybridHttpOrThreadLocalScoped()
        .Use<CustomUserStore>();
 
      //config.For<IDataProtectionProvider>().Use(()=> app.GetDataProtectionProvider()); // In Startup class
 
      ioc.For<ICategoryService>().Use<EfCategoryService>();
      ioc.For<IProductService>().Use<EfProductService>();
    });
   }
  }
}
در اینجا نحوه‌ی اتصال اینترفیس‌های برنامه را به کلاس‌ها و یا نمونه‌هایی که آن‌ها را می‌توانند پیاده سازی کنند، مشاهده می‌کنید. برای مثال IUnitOfWork به ApplicationDbContext مرتبط شده‌است و یا دوبار تعاریف متناظر با DbContext را مشاهده می‌کنید. از این تعاریف به صورت توکار توسط ASP.NET Identity زمانیکه قرار است UserStore و RoleStore را وهله سازی کند، استفاده می‌شوند و ذکر آن‌ها الزامی است.
در تعاریف فوق یک مورد را به فایل Startup.cs موکول کرده‌ایم. برای مشخص سازی نمونه‌ی پیاده سازی کننده‌ی IDataProtectionProvider نیاز است به IAppBuilder کلاس Startup برنامه دسترسی داشت. این کلاس آغازین Owin اکنون به نحو ذیل بازنویسی شده‌است و در آن، تنظیمات IDataProtectionProvider را به همراه وهله سازی CreatePerOwinContext مشاهده می‌کنید:
using System;
using AspNetIdentityDependencyInjectionSample.IocConfig;
using AspNetIdentityDependencyInjectionSample.ServiceLayer.Contracts;
using Microsoft.AspNet.Identity;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.DataProtection;
using Owin;
using StructureMap.Web;
 
namespace AspNetIdentityDependencyInjectionSample
{
  public class Startup
  {
   public void Configuration(IAppBuilder app)
   {
    configureAuth(app);
   }
 
   private static void configureAuth(IAppBuilder app)
   {
    SmObjectFactory.Container.Configure(config =>
    {
      config.For<IDataProtectionProvider>()
        .HybridHttpOrThreadLocalScoped()
        .Use(()=> app.GetDataProtectionProvider());
    });
    SmObjectFactory.Container.GetInstance<IApplicationUserManager>().SeedDatabase();
 
    // Configure the db context, user manager and role manager to use a single instance per request
    app.CreatePerOwinContext(() => SmObjectFactory.Container.GetInstance<IApplicationUserManager>());
 
    // Enable the application to use a cookie to store information for the signed in user
    // and to use a cookie to temporarily store information about a user logging in with a third party login provider
    // Configure the sign in cookie
    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
      AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
      LoginPath = new PathString("/Account/Login"),
      Provider = new CookieAuthenticationProvider
      {
       // Enables the application to validate the security stamp when the user logs in.
       // This is a security feature which is used when you change a password or add an external login to your account.
       OnValidateIdentity = SmObjectFactory.Container.GetInstance<IApplicationUserManager>().OnValidateIdentity()
      }
    });
    app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
 
    // Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
    app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
 
    // Enables the application to remember the second login verification factor such as phone or email.
    // Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
    // This is similar to the RememberMe option when you log in.
    app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie); 
   }
 
  }
}
این تعاریف از فایل پیش فرض Startup.Auth.cs پوشه‌ی App_Start دریافت و جهت کار با IoC Container برنامه، بازنویسی شده‌اند.


تنظیمات برنامه‌ی اصلی ASP.NET MVC، جهت اعمال تزریق وابستگی‌ها

الف) ابتدا نیاز است فایل Global.asax.cs را به نحو ذیل بازنویسی کنیم:
using System;
using System.Data.Entity;
using System.Web;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using AspNetIdentityDependencyInjectionSample.DataLayer.Context;
using AspNetIdentityDependencyInjectionSample.IocConfig;
using StructureMap.Web.Pipeline;
 
namespace AspNetIdentityDependencyInjectionSample
{
  public class MvcApplication : HttpApplication
  {
   protected void Application_Start()
   {
    AreaRegistration.RegisterAllAreas();
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);
 
 
    setDbInitializer();
    //Set current Controller factory as StructureMapControllerFactory
    ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());
   }
 
   protected void Application_EndRequest(object sender, EventArgs e)
   {
    HttpContextLifecycle.DisposeAndClearAll();
   }
 
   public class StructureMapControllerFactory : DefaultControllerFactory
   {
    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
      if (controllerType == null)
       throw new InvalidOperationException(string.Format("Page not found: {0}", requestContext.HttpContext.Request.RawUrl));
      return SmObjectFactory.Container.GetInstance(controllerType) as Controller;
    }
   }
 
   private static void setDbInitializer()
   {
    Database.SetInitializer(new MigrateDatabaseToLatestVersion<ApplicationDbContext, Configuration>());
    SmObjectFactory.Container.GetInstance<IUnitOfWork>().ForceDatabaseInitialize();
   }
  }
}
در اینجا در متد setDbInitializer، نحوه‌ی استفاده و تعریف فایل Configuration لایه Data را ملاحظه می‌کنید؛ به همراه متد آغاز بانک اطلاعاتی و اعمال تغییرات لازم به آن در ابتدای کار برنامه. همچنین ControllerFactory برنامه نیز به StructureMapControllerFactory تنظیم شده‌است تا کار تزریق وابستگی‌ها به کنترلرهای برنامه به صورت خودکار میسر شود. در پایان کار هر درخواست نیز منابع Disposable رها می‌شوند.

ب) به پوشه‌ی Models برنامه مراجعه کنید. در اینجا در هر کلاسی که Id از نوع string وجود داشت، باید تبدیل به نوع int شوند. چون primary key برنامه را به نوع int تغییر داده‌ایم. برای مثال کلاس‌های EditUserViewModel و RoleViewModel باید تغییر کنند.

ج) اصلاح کنترلرهای برنامه جهت اعمال تزریق وابستگی‌ها

اکنون اصلاح کنترلرها جهت اعمال تزریق وابستگی‌ها ساده‌است. در ادامه نحوه‌ی تغییر امضای سازنده‌های این کنترلرها را جهت استفاده از اینترفیس‌های جدید مشاهده می‌کنید:
  [Authorize]
public class AccountController : Controller
{
  private readonly IAuthenticationManager _authenticationManager;
  private readonly IApplicationSignInManager _signInManager;
  private readonly IApplicationUserManager _userManager;
  public AccountController(IApplicationUserManager userManager,
          IApplicationSignInManager signInManager,
          IAuthenticationManager authenticationManager)
  {
   _userManager = userManager;
   _signInManager = signInManager;
   _authenticationManager = authenticationManager;
  }

  [Authorize]
public class ManageController : Controller
{
  // Used for XSRF protection when adding external logins
  private const string XsrfKey = "XsrfId";
 
  private readonly IAuthenticationManager _authenticationManager;
  private readonly IApplicationUserManager _userManager;
  public ManageController(IApplicationUserManager userManager, IAuthenticationManager authenticationManager)
  {
   _userManager = userManager;
   _authenticationManager = authenticationManager;
  }

  [Authorize(Roles = "Admin")]
public class RolesAdminController : Controller
{
  private readonly IApplicationRoleManager _roleManager;
  private readonly IApplicationUserManager _userManager;
  public RolesAdminController(IApplicationUserManager userManager,
           IApplicationRoleManager roleManager)
  {
   _userManager = userManager;
   _roleManager = roleManager;
  }


  [Authorize(Roles = "Admin")]
public class UsersAdminController : Controller
{
  private readonly IApplicationRoleManager _roleManager;
  private readonly IApplicationUserManager _userManager;
  public UsersAdminController(IApplicationUserManager userManager,
           IApplicationRoleManager roleManager)
  {
   _userManager = userManager;
   _roleManager = roleManager;
  }
پس از این تغییرات، فقط کافی است بجای خواص برای مثال RoleManager سابق از فیلدهای تزریق شده در کلاس، مثلا roleManager_ جدید استفاده کرد. امضای متدهای یکی است و تنها به یک search و replace نیاز دارد.
البته تعدادی اکشن متد نیز در اینجا وجود دارند که از string id استفاده می‌کنند. این‌ها را باید به int? Id تغییر داد تا با نوع primary key جدید مورد استفاده تطابق پیدا کنند.


کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید:
AspNetIdentityDependencyInjectionSample


معادل این پروژه جهت ASP.NET Core Identity : «سفارشی سازی ASP.NET Core Identity - قسمت اول - موجودیت‌های پایه و DbContext برنامه »
  • #
    ‫۹ سال و ۹ ماه قبل، جمعه ۵ دی ۱۳۹۳، ساعت ۱۹:۴۱
    ممنون از مطلب شما؛
    برای تغییر نام جداول تشکیل شده، درون متد OnModelCreating کد زیر را نوشتم اما با بروز رسانی دیتابیس تغییری در اسامی جداول حاصل نشد:
    modelBuilder.Entity<ApplicationUser>().ToTable("User").Property(p => p.Id).HasColumnName("Id");
                modelBuilder.Entity<CustomUserRole>().ToTable("UserRole").HasKey(p => new { p.RoleId, p.UserId });
                modelBuilder.Entity<CustomUserLogin>().ToTable("UserLogin").HasKey(p => new { p.LoginProvider, p.ProviderKey, p.UserId });
                modelBuilder.Entity<CustomUserClaim>().ToTable("UserClaim").HasKey(p => p.Id).Property(p => p.Id).HasColumnName("UserClaimId");
                modelBuilder.Entity<CustomRole>().ToTable("Role").Property(p => p.Id).HasColumnName("RoleId");

    • #
      ‫۹ سال و ۹ ماه قبل، جمعه ۵ دی ۱۳۹۳، ساعت ۲۲:۲۳
      این روش پس از امتحان، جواب داد:
              protected override void OnModelCreating(DbModelBuilder builder)
              {
                  base.OnModelCreating(builder);
      
                  builder.Entity<ApplicationUser>().ToTable("Users");
                  builder.Entity<CustomRole>().ToTable("Roles");
                  builder.Entity<CustomUserClaim>().ToTable("UserClaims");
                  builder.Entity<CustomUserRole>().ToTable("UserRoles");
                  builder.Entity<CustomUserLogin>().ToTable("UserLogins");
              }
      اگر متد (base.OnModelCreating(builder را در انتهای کار قرار دهید، تنظیمات پیش فرض کلاس پایه IdentityDbContext (یعنی همان نام‌های قدیمی) اعمال می‌شوند و تنظیمات شما بازنویسی خواهند شد. این متد باید در ابتدای کار فراخوانی شود.
  • #
    ‫۹ سال و ۹ ماه قبل، دوشنبه ۸ دی ۱۳۹۳، ساعت ۲۲:۵۱
    با سلام
    ممنون از مطلب مفید شما. واقعا عالی بود
    ولی قسمتی از کد برام نامفهوم بود :
    public ApplicationDbContext()
                : base("connectionString1")
            {
                //this.Database.Log = data => System.Diagnostics.Debug.WriteLine(data);
                //فقط تعریف شده تا یک برک پوینت در اینجا قرار داده شود برای آزمایش تعداد بار فراخوانی آن
            }
    
            protected override void Dispose(bool disposing)
            {
                base.Dispose(disposing);
                //فقط تعریف شده تا یک برک پوینت در اینجا قرار داده شود برای آزمایش فراخوانی آن
            }

    مخصوصا بخش Dispose .
    چون قبلا که از الگوی واحد کار استفاده می‌شد این بخش وجود نداشت !
    آیا وجود این کدها که در بالا اومده الزامی است؟
    • #
      ‫۹ سال و ۹ ماه قبل، دوشنبه ۸ دی ۱۳۹۳، ساعت ۲۲:۵۴
      خیر. متد Dispose را حذف کنید (در کلاس پایه هست). وجود سازنده هم صرفا جهت ساده سازی تعریف و انتخاب رشته اتصالی تعریف شده در web.config است.
  • #
    ‫۹ سال و ۹ ماه قبل، پنجشنبه ۱۱ دی ۱۳۹۳، ساعت ۱۶:۵۷
    نکته‌ای مهم در مورد مدیریت استراکچرمپ در این مثال
    اگر از SmObjectFactory مطلب فوق استفاده می‌کنید، Container آن با ObjectFactory یکی نیست یا به عبارتی ObjectFactory اطلاعی در مورد تنظیمات کلاس سفارشی SmObjectFactory ندارد. بنابراین دیگر نباید از ObjectFactory قدیمی استفاده کنید. در این حالت هرجایی ObjectFactory قدیمی را داشتید، با SmObjectFactory.Container تعویض می‌شود.
  • #
    ‫۹ سال و ۹ ماه قبل، یکشنبه ۱۴ دی ۱۳۹۳، ساعت ۲۲:۱۶
    نحوه دسترسی به کاربری که لاگین کرده چطوریه؟
    من با دستور HttpContext.Current.User کار میکنم
    بعضی اوقات نال بر میگردونه
    • #
      ‫۹ سال و ۹ ماه قبل، یکشنبه ۱۴ دی ۱۳۹۳، ساعت ۲۲:۴۷
      User.Identity را در مثال فوق جستجو کنید:
      User.Identity.GetUserName()
      User.Identity.GetUserId()
  • #
    ‫۹ سال و ۹ ماه قبل، یکشنبه ۱۴ دی ۱۳۹۳، ساعت ۲۲:۵۱
    برای مشخص کردن نمونه پیاده‌سازی کننده IDataProtectionProvider در یک برنامه کنسول نیز باید از فایل Startup استفاده کرد؟ بیشتر هدفم Seed کردن دیتابیس است (مثلاً ایمپورت تعداد زیادی کاربر از طریق یک فایل و...). اینکار رو در متد SeedDatabase هم انجام دادم ولی هر بار استثنای UserId not found رو در:
    result = this.SetLockoutEnabled(user.Id, false);
    صادر میکنه، با گذاشتن Breakpoint متوجه شدم که برای Id صفر رو در نظر میگیره! از این جهت ترجیح دادم برای اینکار از طریق برنامه کنسول ویندوزی هم آن را تست کنم، مثل روشی که در اینجا برای ایجاد کاربر نوشته شده.
      
    • #
      ‫۹ سال و ۹ ماه قبل، دوشنبه ۱۵ دی ۱۳۹۳، ساعت ۱۵:۱۸
      از همان متد SeedDatabase استفاده کردم، مشکل این بود که در متد Create نوع پسورد از این لحاظ که حداقل باید 6 کاراکتر باشه و درست بودن ایمیل و... نیز بررسی میشه، اگر مقادیر معتبر نباشه مقدار user.Id برابر با صفر میشه.
  • #
    ‫۹ سال و ۹ ماه قبل، دوشنبه ۱۵ دی ۱۳۹۳، ساعت ۰۴:۴۵
    در قسمت تزریق وابستگی کد زیر وجود داره
    ioc.For<IIdentityMessageService>().Use<SmsService>();
    ioc.For<IIdentityMessageService>().Use<EmailService>();
    در صورت استفاده از اینترفیس مربوطه کدوم سرویس نمونه سازی میشه؟ در صورتی که هر دو سرویس وجود داشته باشه؟
    • #
      ‫۹ سال و ۹ ماه قبل، دوشنبه ۱۵ دی ۱۳۹۳، ساعت ۰۵:۲۵
      این مشکل اصلاح شد. باید از named instanceها در این حالت خاص استفاده شود.
      • #
        ‫۹ سال و ۹ ماه قبل، دوشنبه ۱۵ دی ۱۳۹۳، ساعت ۰۹:۳۶
        با انجام اصلاحات فوق، زمانی که کاربر اقدام به تایید شماره تلفن همراه کنه با اینکه PhoneCode انتخاب میشه اما بازم قسمت ارسال ایمیل اجرا میشه.
        • #
          ‫۹ سال و ۹ ماه قبل، دوشنبه ۱۵ دی ۱۳۹۳، ساعت ۱۶:۵۲
          نیاز به تنظیمات setter injection هم داشت که اضافه شد.
          • #
            ‫۹ سال و ۹ ماه قبل، دوشنبه ۱۵ دی ۱۳۹۳، ساعت ۲۳:۵۳
            با این تنظیمات
             // map same interface to different concrete classes
                            ioc.For<IIdentityMessageService>().Use<SmsService>().Named("smsService");
                            ioc.For<IIdentityMessageService>().Use<EmailService>().Named("emailService");
                            ioc.For<IApplicationUserManager>().HybridHttpOrThreadLocalScoped()
                                .Use<ApplicationUserManager>()
                                .Ctor<IIdentityMessageService>("smsService").Is<SmsService>()
                                .Ctor<IIdentityMessageService>("emailService").Is<EmailService>()
                                .Setter<IIdentityMessageService>(userManager => userManager.SmsService).Is<SmsService>()
                                .Setter<IIdentityMessageService>(userManager => userManager.EmailService).Is<EmailService>();

            و این کد سازنده UserManager
             public ApplicationUserManager(
                        IApplicationUserStore store,
                        IApplicationRoleManager roleManager,
                        IDataProtectionProvider dataProtectionProvider,
                        IIdentityMessageService smsService,
                        IIdentityMessageService emailService)
                        : base(store as IUserStore<User,int>)
                    {
                        this.roleManager = roleManager;
                        this.dataProtectionProvider = dataProtectionProvider;
                        EmailService = emailService;
                        SmsService = smsService;
                        Debug.WriteLine("Ticks = {0}",DateTime.Now.Ticks);
                        Debug.WriteLine(emailService.ToString());
                        Debug.WriteLine(smsService.ToString());
                        CreateApplicationUserManager();
                    }

            در زمان دیباگ اینجور خروجی توی پنجره دیباگ گرفتم
            Ticks = 635560859158196052
            PWS.ServiceLayer.EF.Identity.EmailService
            PWS.ServiceLayer.EF.Identity.SmsService
            Ticks = 635560859158246098
            PWS.ServiceLayer.EF.Identity.EmailService
            PWS.ServiceLayer.EF.Identity.EmailService

            نمی‌دونم چرا دوبار سازنده فراخوانی شده در بار اول تزریق وابستگی درست انجام شده اما در اخرین بار هم سرویس ایمیل به ایمیل و پیامک هر تزریق شده.
  • #
    ‫۹ سال و ۸ ماه قبل، سه‌شنبه ۲۳ دی ۱۳۹۳، ساعت ۱۶:۰۴
    سلام؛ در روش برنامه نویسی لایه ای به این صورتی که شما آموزش دادید امکان دیدن جدول‌های بانک در بخش Server Explorer نیست؟ منظورم قبل از اینزرت اطلاعات در آنهاست؟ چون می‌خوام یکسری از جداول رو دستی مستقیم در بانک پر کنم. 
    و اینکه بعد از اینکه dbContext را در پوشه App_Data حذف کردم چه جوری  اتصال کاملشو از بین ببرم چون برنامه ام ارور پیدا نکردن dbcontext را می‌ده و چه جوری می‌تونم دوباره ReCreate کنمش.
    • #
      ‫۹ سال و ۸ ماه قبل، سه‌شنبه ۲۳ دی ۱۳۹۳، ساعت ۱۶:۳۲
      - در سازنده‌ی کلاس ApplicationDbContext ، به connectionString1 اشاره شده که تعریف آن در فایل web.config برنامه موجود است. به عبارتی در این مثال چنین رشته‌ی اتصالی تعریف شده‌است:
      <add name="connectionString1"
      connectionString="Data Source=(local);Initial Catalog=TestDbIdentity;Integrated Security = true" 
      providerName="System.Data.SqlClient" />
      بنابراین بانک اطلاعاتی پیش فرض آن TestDbIdentity نام دارد (جهت اتصال به آن، برای مشاهده جداول و یا تغییر و ثبت اطلاعات). این رشته اتصالی هم مخصوص SQL Server تنظیم شده‌است که می‌توانید توسط management studio و یا سایر ابزارهای مشابه، همانند قبل به آن متصل شوید.
      - «در پوشه App_Data حذف کردم» ... از مثال نهایی کامل شده استفاده کنید (^) و نیازی به تکرار این مراحل نیست تا خطای یافت نشدن dbcontext را دریافت نکنید.
      - برای ReCreate فقط کافی هست که بانک اطلاعاتی TestDbIdentity را drop کنید. بعد برنامه را مجددا از نو اجرا کنید. چون مراحل migrations آن به حالت خودکار تنظیم شده‌است، بانک اطلاعاتی را به صورت خودکار ایجاد می‌کند یا تغییرات کلاس‌های دومین برنامه را به صورت خودکار به بانک اطلاعاتی اعمال خواهد کرد.
      - برای آشنایی بیشتر با مباحث تعریف رشته اتصالی EF Code First و مباحث Migrations آن، سری EF Code First را در سایت یکبار مطالعه کنید.
  • #
    ‫۹ سال و ۸ ماه قبل، شنبه ۲۷ دی ۱۳۹۳، ساعت ۱۹:۴۰
    ممنون از مطالب شما 
     اگر بخواهیم از IApplicationRoleManager و IApplicationUserManager  یا به طور کلی از ایترفیس‌ها در  AuthorizeAttribute سفارشی (یا فیلتر هایی) که ایجاد کرده ایم   استفاده کنیم ، تنظیمات strucutremap به چه صورت خواهد بود.

     public class CustomAuthorizeAttribute : AuthorizeAttribute
        {
    
            
            public IApplicationRoleManager _roleManager { get; set; }
            
            public IApplicationUserManager _userManager { get; set; }
    
    
            public CustomAuthorizeAttribute(IApplicationUserManager userManager,
                                        IApplicationRoleManager roleManager)
            {
                _userManager = userManager;
                _roleManager = roleManager;
            }
            protected override bool AuthorizeCore(HttpContextBase httpContext)
            {
                // Put your custom logic here, returning true for success and false for failure,
                // or return base.AuthorizeCore(httpContext) to defer to the base implementation
                IPrincipal user = httpContext.User;
                IIdentity identity = user.Identity;
    
                if (!identity.IsAuthenticated)
                {
                    return false;
                }
    
                bool isAuthorized = true;
                // TODO: perform custom authorization against the App
                var qry = _userManager.Users.Where(u => u.UserName == identity.Name).ToList();
    
                return isAuthorized;
            }
        }

  • #
    ‫۹ سال و ۸ ماه قبل، سه‌شنبه ۷ بهمن ۱۳۹۳، ساعت ۱۶:۰۳
    با سلام؛ پروژه این مثال و دانلود کردم و سپس اجراش کردم - مشکلی نداره ولی بعد چند دقیقه ناگهان با این پیغام مواجه میشم.

    لطفا راهنمایی بفرمائید. با تشکر.
    • #
      ‫۹ سال و ۸ ماه قبل، سه‌شنبه ۷ بهمن ۱۳۹۳، ساعت ۱۶:۵۳
      - لطفا خطاها را تصویری ارسال نکنید. متن موجود در تصاویر، قابلیت جستجو و پیگیری ندارند.
      - متن آن واضح است و به عمد تنظیم شده‌است. عنوان کرده‌است که آدرس خاصی را یافت نمی‌کند و این آدرس‌ها قابلیت تزریق وابستگی ندارند. آدرس آن (arterySignalR/ping) مرتبط است به browser link که به این صورت می‌توانید آن‌را غیرفعال کنید:
      public static void RegisterRoutes(RouteCollection routes)
      {
          routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
          routes.IgnoreRoute("{*browserlink}", new { browserlink = @".*/arterySignalR/ping" });
          //...
      }
      و یا
      <appSettings>
          <add key="vs:EnableBrowserLink" value="false" />
      </appSettings>
  • #
    ‫۹ سال و ۶ ماه قبل، یکشنبه ۱۶ فروردین ۱۳۹۴، ساعت ۲۱:۰۸
    تنظیمات جهت اعمال تزریق وابستگی‌ها در WEB API چگونه خواهد بود ؟
    • #
      ‫۹ سال و ۶ ماه قبل، یکشنبه ۱۶ فروردین ۱۳۹۴، ساعت ۲۱:۲۷
      تنظیمات اولیه‌ی IoC Container در این حالت، با نمونه‌ی موجود یکی است. فقط Web API نیاز به معرفی SmObjectFactory.Container را به GlobalConfiguration مخصوص خودش، به صورت جداگانه دارد. اطلاعات بیشتر
  • #
    ‫۹ سال و ۵ ماه قبل، جمعه ۲۸ فروردین ۱۳۹۴، ساعت ۲۳:۰۵
    با سلام؛ خیلی معماری جالبی بود. اما یه سوالی ذهن مرا مشغول کرده . البته من ساختار structureMap را درست نمی‌دانم ولی اکثر IOC Container‌ها نمونه singlethon  را از اینترفیس‌ها برمی گردانند . که اگر اینگونه باشد به معنی وجود یک Unit OF Work و DbContext بوده در کل appllication بوده که این برای یک وب اپلیکیشن به منزله یک فاجعه است. اگر ممکن است مقداری راهنمایی بفرمایید. سپاسگزارم.
    • #
      ‫۹ سال و ۵ ماه قبل، جمعه ۲۸ فروردین ۱۳۹۴، ساعت ۲۳:۳۶
      طول عمر پیش فرض اشیاء ایجاد شده‌ی توسط استراکچرمپ از نوع transient است. به این معنا که هر درخواستی از آن شیء، سبب ایجاد یک وهله‌ی جدید از آن خواهد شد. اطلاعات بیشتر
      • #
        ‫۹ سال و ۵ ماه قبل، یکشنبه ۳۰ فروردین ۱۳۹۴، ساعت ۲۲:۲۸
        فرض کنید ما از این پروژه استفاده کنیم . اما از الگوی repository و unit of work بر روی آنها نیز استفاده کنیم. به این ترتیب IOC از یک طرف applicationDbContext را به کلاس store در identity پاس داده و از طرف دیگر به کلاس  unit of work آیا به نظر شما مشکلی پیش نخواهد آمد. 
        با توجه به آنکه به فرموده شما IOC در هر HttpRequest  فقط یک آبجکت از کلاس applicationDbContext  خواهد ساخت اگر store  و unit of work  هر دو در همان HttpRequest   اقدام به اعمال savechange کنند چه؟ اصلا چینین چیزی ممکن است؟
        • #
          ‫۹ سال و ۵ ماه قبل، یکشنبه ۳۰ فروردین ۱۳۹۴، ساعت ۲۳:۰۲
          context و uow یکی هستند. قسمت‌های 11 و 12 سری EF Code first سایت را نیاز است یکبار مطالعه کنید؛ به همراه تمام نظرات آن‌ها.
  • #
    ‫۹ سال و ۴ ماه قبل، دوشنبه ۲۸ اردیبهشت ۱۳۹۴، ساعت ۱۸:۰۰
    من یک پروژه انجام شده دارم  که Id از نوع string هست ولی در مثالی که شما زدید Id از نوع int هست چجوری میتونم این روشی که گفتید رو پیاده سازی کنم ولی نوع فیلد Id رو تغییر ندم، اگه بخوام تغییر بدم تمام اطلاعات دیتابیس من دستخوش تغییرات زیادی میشه؟
    • #
      ‫۹ سال و ۴ ماه قبل، دوشنبه ۲۸ اردیبهشت ۱۳۹۴، ساعت ۱۸:۴۰
      - متن را یکبار مطالعه کردید؟ یکبار مثال اصلی را با مثال فوق انطباق دادید؟
      - در متن ذکر شده؛ مثال اصلی آن نوع ID را از نوع string تعریف کرده‌است. در اینجا برای نزدیک شدن مثال به دنیای واقعی و آن چیزی که مرسوم است، نوع Id به int تغییر داده شد.
      اگر مورد نظر شما نیست، هرجایی که int مشاهده می‌کنید، تغییرش دهید به string. مثلا:
       ApplicationUser : IdentityUser<int, CustomUserLogin, CustomUserRole, CustomUserClaim>
      این int را تغییر دهید به string و در هر جای دیگری که تعریف شده و تغییر کرده. این تغییرات را با انطباق مثال تغییر یافته با نمونه‌ی اصلی که لینکش در متن هست می‌توانید پیدا کنید.
    • #
      ‫۸ سال و ۱۱ ماه قبل، چهارشنبه ۱۳ آبان ۱۳۹۴، ساعت ۲۱:۲۰
      درصورتی که نوع Id را به string تغییر دهید، در هنگام اجرای برنامه و ایجاد یک کاربر جدید با خطای اعتبار سنجی زیر مواجه خواهید شد : 
      The Id field is required
      به این دلیل که در حالت پیش فرض، هنگام ایجاد یک وهله از کلاس IdentityUser به صورت خودکار برای خصوصیت Id یک مقدار Guid تولید میشود اما چون نوع Id به string تغییر کرده دیگر این مقدار تولید نشده و فیلد Id با null مقدار دهی میشود.
      برای رفع این مشکل کافی است تا داخل سازنده‌ی کلاس سفارشی ApplicationUser برای فیلد Id یک مقدار Guid تولید کنیم.
      public class ApplicationUser : IdentityUser<string, CustomUserLogin, CustomUserRole, CustomUserClaim>
      {
          public ApplicationUser()
          {
              Id = Guid.NewGuid().ToString();
          }
          
          // ...
      }

  • #
    ‫۹ سال و ۳ ماه قبل، جمعه ۲۹ خرداد ۱۳۹۴، ساعت ۲۳:۳۲
    چطور در پروژه DomainClass که از نوع classLibrary هست
    using Microsoft.AspNet.Identity.EntityFramework را اضافه کردین؟
    • #
      ‫۹ سال و ۳ ماه قبل، شنبه ۳۰ خرداد ۱۳۹۴، ساعت ۰۰:۳۰
      - وابستگی‌های هر پروژه را با مراجعه به فایل packages.config آن می‌توانید بررسی کنید.
      - بعد از مشخص شدن لیست وابستگی‌ها، فقط کافی است تک تک آن‌ها را با دستور زیر نصب کنید:
      PM> install-package name_here
    • #
      ‫۹ سال و ۳ ماه قبل، یکشنبه ۳۱ خرداد ۱۳۹۴، ساعت ۱۵:۱۰
      البته پنجره گرافیکی nuget هم  این امکان رو میده که مجبور نباشیم تک تکشون رو نصب کنیم
      به محض باز شدن پنجره ، خودش شناسایی میکنه چه بسته‌های تعریف شدند ولی موجود نیست و با کلیک بر دکمه مربوطه همه رو نصب میکنه
      • #
        ‫۹ سال و ۳ ماه قبل، یکشنبه ۳۱ خرداد ۱۳۹۴، ساعت ۱۵:۲۱
        بله. البته اگر فایل packages.config را به پروژه‌ی جدید مربوطه کپی کنند.
  • #
    ‫۹ سال و ۳ ماه قبل، یکشنبه ۳۱ خرداد ۱۳۹۴، ساعت ۱۹:۱۷
    می‌خواستم بدونم اگر در یک custom authorize attribute بخواهم role‌های کاربر فعلی را بدانم باید چکار کنم؟
    در سیستم membership خیلی راحت می‌نوشتم:
    var currentUserRoles =System.Web.Security.Roles.GetRolesForUser().Select(u => u.ToLower()).ToList();
    اما در این معماری ای که شما نوشته اید چکار باید کرد؟ آیا باید IApplicationRoleManager   را به attribute پاس داد که فکر نمی‌کنم ممکن باشد. ممکن است قدری راهنمایی بفرمایید؟
  • #
    ‫۹ سال و ۲ ماه قبل، پنجشنبه ۱۵ مرداد ۱۳۹۴، ساعت ۰۵:۱۰
    برای ایجاد دیتابیس در مثال گفته شده ، Database.Initialize به کانکشن استرینگ پاس داده شده توسط stracturemap  کاری نداره و از فایل کانفیگ  برای ساخت دیتابیس استفاده می‌کنه و اگه کانکشین استرینگ در فایل کانفیگ نباشه یه دیتابیس به نام connectionString1  می‌سازه. 
    برای بررسی، در مثال خودتون کانکشن استرینگ کانفیگ و stracturemap  را با نام دیتابیس متفاوت بدید.
    فکر کنم بعد از اجرای Database.Initialize یک وهله جدید از ApplicationDbContext  با امضای اول اون میسازه و کاری به وهله تزریق شده  نداره
  • #
    ‫۹ سال و ۲ ماه قبل، پنجشنبه ۱۵ مرداد ۱۳۹۴، ساعت ۱۸:۳۵
    در لایه سرویس چگونه بابد یک Edit  انجام شود؟ یا با این دستور چطور میشه کار کرد : MarkAsChanged 
  • #
    ‫۹ سال و ۱ ماه قبل، یکشنبه ۱۸ مرداد ۱۳۹۴، ساعت ۲۰:۳۹
    در مثال بالا هر لاگین کاربر حداقل 9 بار کاربر همراه با رل هاش از دیتابیس فراخوانی میشه اگر ورود دو مرحله فعال باشه حدودا 28 بار میشه ، روش پیش فرض که آی دی رو string پیاد سازی کرده رو بررسی کردم چنین مشکلی نداشت. در کلاس ApplicationUserManager متد FindByIdAsync  رو بصورت زیر تغییر بدید می‌تونید تعداد فراخوانی را بررسی کنید: 
          public override Task<ApplicationUser> FindByIdAsync(int userId)
            {
                         return base.FindByIdAsync(userId);
            }
    
            public override Task<ApplicationUser> FindByNameAsync(string userName)
            {
                return base.FindByNameAsync(userName);
            }
    در کدهای  Identity به دفعات از متود  FindByIdAsync  استفاده شده و فرض بر این بوده که دفعات بعد از کش DbContext اسفاده می‌شود.
    در سورس UserStore متود FindByIdAsync  از یک متد واسطه  به نام GetUserAggregateAsync استفاده کرده :
    public virtual Task<TUser> FindByIdAsync(TKey userId)
            {
                ThrowIfDisposed();
                return GetUserAggregateAsync(u => u.Id.Equals(userId));
            }
         protected virtual async Task<TUser> GetUserAggregateAsync(Expression<Func<TUser, bool>> filter)
            {
                TKey id;
                TUser user;
                if (FindByIdFilterParser.TryMatchAndGetId(filter, out id))
                {
                    user = await _userStore.GetByIdAsync(id).WithCurrentCulture();
                }
                else
                {
                    user = await Users.FirstOrDefaultAsync(filter).WithCurrentCulture();
                }
                if (user != null)
                {
                    await EnsureClaimsLoaded(user).WithCurrentCulture();
                    await EnsureLoginsLoaded(user).WithCurrentCulture();
                    await EnsureRolesLoaded(user).WithCurrentCulture();
                }
                return user;
            }
    وقتی پیاده سازی آی دی براساس int باشه متد FindByIdFilterParser.TryMatchAndGetId درست عمل نمی‌کنه و به جای فراخوانی GetByIdAsync که پشت صحنه از FindAsync و قابلیت فراخوانی از کش رو داره از FirstOrDefaultAsync استفاده می‌کنه و باعث فراخوانی مجدد از دیتابیس می‌شود.
    راه حلی که من استفاده کردم پیاده سازی UserStore و تحریف متد FindByIdAsync  :
        public class MyUserStore
            :UserStore<ApplicationUser, CustomRole, int, CustomUserLogin, CustomUserRole, CustomUserClaim>
        {
    
            private DbSet<ApplicationUser> _myUserStore;
            public MyUserStore(ApplicationDbContext context)
                : base(context)
            {
                _myUserStore = (DbSet<ApplicationUser>) context.Set<ApplicationUser>();
    
            }
    
            public override Task<ApplicationUser> FindByIdAsync(int userId)
            {
               return  _myUserStore.FindAsync(userId);
            }
      
        }
    }
     همچنین در کلاس SmObjectFactory کد زیر :
                   ioc.For<IUserStore<ApplicationUser, int>>()
                    .HybridHttpOrThreadLocalScoped()
                    .Use<UserStore<ApplicationUser, CustomRole, int, CustomUserLogin, CustomUserRole, CustomUserClaim>>();
    با این کد جایگزین شود :
    ioc.For<IUserStore<ApplicationUser, int>>()
                        .HybridHttpOrThreadLocalScoped()
                     .Use<MyUserStore>();

    • #
      ‫۸ سال و ۱۲ ماه قبل، سه‌شنبه ۱۴ مهر ۱۳۹۴، ساعت ۰۸:۲۸
      سلام با کد ذیل _userStore واسه من نال بر میگردونه
      using System.Data.Entity;
      using System.Threading.Tasks;
      using Microsoft.AspNet.Identity.EntityFramework;
      using SmartMarket.Core.Domain.Members;
      using SmartMarket.Data;
      
      namespace SmartMarket.Services.Members
      {
          /// <summary>
          /// The ApplicationUserStore Class 
          /// </summary>
          public class ApplicationUserStore : UserStore<User, Role, int, UserLogin, UserRole, UserClaim>, IApplicationUserStore
          {
      #region Fields (1) 
      
              private readonly IDbSet<User> _userStore;
      
      #endregion Fields 
      
      #region Constructors (2) 
      
              /// <summary>
              /// Initializes a new instance of the <see cref="ApplicationUserStore" /> class.
              /// </summary>
              /// <param name="dbContext">The database context.</param>
              public ApplicationUserStore(DbContext dbContext) : base(dbContext) { }
      
              /// <summary>
              /// Initializes a new instance of the <see cref="ApplicationUserStore"/> class.
              /// </summary>
              /// <param name="context">The context.</param>
              public ApplicationUserStore(IdentityDbContext context)
                  : base(context)
              {
                  _userStore = context.Set<User>();
       
              }
      
      #endregion Constructors 
      
      #region Methods (2) 
      
      // Public Methods (2) 
      
              /// <summary>
              /// Adds to previous passwords asynchronous.
              /// </summary>
              /// <param name="user">The user.</param>
              /// <param name="password">The password.</param>
              /// <returns></returns>
              public Task AddToPreviousPasswordsAsync(User user, string password)
              {
                  user.PreviousUserPasswords.Add(new PreviousPassword { UserId = user.Id, PasswordHash = password });
                  return UpdateAsync(user);
              }
      
              /// <summary>
              /// Finds the by identifier asynchronous.
              /// </summary>
              /// <param name="userId">The user identifier.</param>
              /// <returns></returns>
              public override Task<User> FindByIdAsync(int userId)
              {
                  return Task.FromResult(_userStore.Find(userId));
              }
      
      #endregion Methods 
      
              /// <summary>
              /// Creates the asynchronous.
              /// </summary>
              /// <param name="user">The user.</param>
              /// <returns></returns>
              public override async Task CreateAsync(User user)
              {
                  await base.CreateAsync(user);
                  await AddToPreviousPasswordsAsync(user, user.PasswordHash);
              }
          }
      }
      • #
        ‫۸ سال و ۱۲ ماه قبل، سه‌شنبه ۱۴ مهر ۱۳۹۴، ساعت ۱۳:۲۳
        - مثال‌های متفرقه را در این سایت مطرح نکنید. مثال بررسی و اصلاح شده در اینجا قرار دارد.
        - زمانیکه یک شیء وهله سازی می‌شود، فقط بر اساس یکی از سازنده‌های آن وهله سازی خواهد شد و نه تمام آن‌ها. این‌ها اصول اولیه‌ی کار با سی شارپ هست.
        - در مثال متفرقه‌ای که مطرح کردید، نیازی به تعریف همزمان DbContext و همچنین IdentityDbContext نیست و کار اضافی انجام دادید؛ چون در اصل یکی هستند و بر اساس نیاز کلاسی که از آن ارث بری شده باید IdentityDbContext متفرقه باشد و نه DbContext خام.
      • #
        ‫۶ سال و ۳ ماه قبل، چهارشنبه ۳۰ خرداد ۱۳۹۷، ساعت ۲۲:۵۲
        با سلام؛ متد PasswordSignInAsync  که وظیفه لاگین و نتیجه لاگین رو برمیگردونه ، میتونه هم با ایمیل و هم با یوزرنیم کار لاگین رو انجام بده. من توی برنامه یوزرنیم هارو کدملی درنظر گرفتم. یعنی فیلد یوزرنیم خالی و فیلد کد ملی رو واسه لاگین در نظر گرفتم. برای ثبت نام هم فیلد کد ملی اول چک میشه که تکراری نباشه و بعد کار ثبت نام انجام میشه. آیا برای لاگین با کد ملی باید PasswordSignInAsync  رو override کنم؟
        • #
          ‫۶ سال و ۳ ماه قبل، پنجشنبه ۳۱ خرداد ۱۳۹۷، ساعت ۱۳:۲۴
          بهتر است اینکار را انجام ندهید تا تمام claims و طراحی برنامه را به هم نریزد. یک فیلد جداگانه و مخصوص کدملی را به جدول سفارشی کاربران اضافه کنید. سپس در ابتدای لاگین بر اساس کد ملی شخص، شیء user را از بانک اطلاعاتی واکشی کنید. در آخر بر اساس اطلاعات این شیء دریافتی که شامل username و غیره است، PasswordSignInAsync اصلی را فراخوانی کنید تا کوکی‌های برنامه و claims آن به درستی تشکیل شوند.
    • #
      ‫۸ سال و ۱۲ ماه قبل، سه‌شنبه ۱۴ مهر ۱۳۹۴، ساعت ۱۳:۴۸
      این نکته‌ای را که عنوان کردید با استفاده از DNTProfiler بررسی کردم و اصلا چنین چیزی (28 بار فراخوانی) نیست. در پشت صحنه از نسخه‌ی Async متد Find استفاده می‌شود (در stack trace موجود هست) و حذف آن با متد ساده‌ای که نوشته شده، یک سری از سازوکارهای داخلی ASP.NET Identity را حذف می‌کند و به صلاح نیست.
  • #
    ‫۹ سال و ۱ ماه قبل، دوشنبه ۱۹ مرداد ۱۳۹۴، ساعت ۱۸:۴۳
    با توجه به اینکه در ورژن نهایی پروژه ای که ارائه داده اید قسمت زیر در فایل Startup.cs  موجود نیست
    appBuilder.CreatePerOwinContext(
                   () =>ProjectObjectFactory.Container.GetInstance<ApplicationUserManager>());
    و این موضوع باعث خواهد شد تا SecutiyStampValidator  کار کرد صحیحی  نداشته باشد.
  • #
    ‫۹ سال و ۱ ماه قبل، پنجشنبه ۲۲ مرداد ۱۳۹۴، ساعت ۱۶:۲۱
    جهت اطلاع
    به این پروژه پشتیبانی از Web API هم اضافه شد.
  • #
    ‫۸ سال و ۱۲ ماه قبل، یکشنبه ۱۲ مهر ۱۳۹۴، ساعت ۲۳:۲۱
    باسلام، در صورت امکان در مورد این سطر توضیح بفرمایید:
       ioc.For<IIdentity>().Use(() => (HttpContext.Current != null && HttpContext.Current.User != null) ? HttpContext.Current.User.Identity : null);
  • #
    ‫۸ سال و ۹ ماه قبل، شنبه ۲۱ آذر ۱۳۹۴، ساعت ۲۲:۵۶
    با سلام؛ توی قسمت تنظیمات Ioc این تکه کد رو داریم:
    ioc.For<DbContext>().HybridHttpOrThreadLocalScoped().Use<ApplicationDbContext>();
    حالا اگه من دو تا DbContext داشته باشم مثلا به نام‌های ApplicationDbContext و ApplicationDbContext2 کد بالا رو باید چطوری بازنویسی کنم؟
  • #
    ‫۸ سال و ۸ ماه قبل، چهارشنبه ۱۶ دی ۱۳۹۴، ساعت ۱۳:۵۱
    در مثالی که به ضمیمه این مطلب گذاشتید، بعداز Login، فیلدهای درون User.Identity مقادیر نادرستی دارند. مشکل از کجاست؟ درصورتی که درون ویوی _LoginPartial مقدار User.Identity.GetUserName  حاوی مقدار درستیه.  

    ضمن اینکه به اینترفیس IIdentity همیشه مقدار null تزریق میشه. حتی بعداز روالهای ابتدایی برنامه. دقیقا توی همین پروژه، وقتی روی منوی About میزنی، درون سازنده IApplicationUserManager ، متغیر identity با null پر می‌شود که باعث بروز خطا میشه. مشکل چیه؟ 

    • #
      ‫۸ سال و ۸ ماه قبل، چهارشنبه ۱۶ دی ۱۳۹۴، ساعت ۱۶:۰۱
      - هیچ مشکلی ندارد. حاصل این لاگین (در مرحله‌ی اول) SignInStatus. Success نیست. به همین جهت Is Authenticated مساوی false است.
      - این دو سطر را از Startup.cs حذف کنید (نیازی به آن نیست و اگر تعریف شود، ترتیب آن مهم است و باید پس از app.UseTwoFactorRememberBrowserCookie باشد):
      app.CreatePerOwinContext(
                   () => SmObjectFactory.Container.GetInstance<IApplicationUserManager>());
  • #
    ‫۸ سال و ۸ ماه قبل، چهارشنبه ۱۴ بهمن ۱۳۹۴، ساعت ۱۵:۳۰
    با سلام
    برای مدل User شما از Int استفاده کرده اید من با توجه به ساختار کل پروژه که از Guid استفاده می‌کنیم مدل user  رو به صورت زیر تغییر داده ام
        public class User : IdentityUser<Guid, UserLogin, UserRole, UserClaim>
     حال در سرویس  ApplicationUserManager در متد GetCurrentUserId به صورت زیر پیاده سازی شده است
    public Guid GetCurrentUserId()
    {
        return _identity.GetUserId<Guid>();
    }
    که بعد از کامپایل خطای زیر داده می‌شود
    The type 'System.Guid' cannot be used as type parameter 'T' in the generic type 
    or method 'Microsoft.AspNet.Identity.IdentityExtensions.GetUserId<T>(System.Security.Principal.IIdentity)'. 
    There is no boxing conversion from 'System.Guid' to 'System.IConvertible'
    آیا فقط نوع داده باید int باشد؟
    • #
      ‫۸ سال و ۸ ماه قبل، چهارشنبه ۱۴ بهمن ۱۳۹۴، ساعت ۱۵:۵۰
      Guid از نوع IConvertible نیست. به همین جهت باید به این صورت عمل کنید:
      Guid.Parse(User.Identity.GetUserId())
      و یا
      public static class IdentityExtensions
      {
          public static Guid GetGuidUserId(this IIdentity identity)
          {
              Guid result = Guid.Empty;
              Guid.TryParse(identity.GetUserId(), out result);
              return result;
          }   
      }
  • #
    ‫۸ سال و ۷ ماه قبل، چهارشنبه ۵ اسفند ۱۳۹۴، ساعت ۰۲:۱۸
    چطور می‌تونم اطلاعات اضافی از کاربری رو وقتی می‌خواد لاگین کنه نگه دارم؟
    مثلا به جدول user فیلد Avatar رو اضافه کردم. هر جا نیاز بود دسترسی داشته باشم بهش و نیازی به خوندن اون از دیتابیس نباشه.
    • #
      ‫۸ سال و ۷ ماه قبل، چهارشنبه ۵ اسفند ۱۳۹۴، ساعت ۰۲:۴۲
      از Claim‌ها کمک بگیرید (در نهایت در کوکی کاربر ذخیره خواهند شد ) :
       public async Task<ClaimsIdentity> GenerateUserIdentityAsync(ApplicationUser applicationUser)
              {
                  // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
                  var userIdentity = await CreateIdentityAsync(applicationUser, DefaultAuthenticationTypes.ApplicationCookie);
                  // Add custom user claims here
                 userIdentity.AddClaim(new Claim("Avatar",applicationUser.Avatar));
                  return userIdentity;
              }
      متد AddClaim برای کلاس ClaimsIdentity در نظر گرفته شده است.
      یا حتی میتوانید یک ClaimsIdentityFactory شخصی سازی شده در نظر بگیرید؛ برای مثال در پروژه " طراحی فریمورک برای کار با Asp.net MVC و EF به صورت NTier  یک نمونه پیاده سازی شده را میتوانید مشاهده کنید.
      • #
        ‫۸ سال و ۷ ماه قبل، یکشنبه ۹ اسفند ۱۳۹۴، ساعت ۱۳:۰۷
        به توجه به اینکه نهایت حجم یکی کوکی 4 کیلوبایت هست آیا درسته که آواتار کاربر که ممکن هست یک تصویر با حجم بیشتر از این مقدار باشد رو در claim ذخیره کنیم؟
        • #
          ‫۸ سال و ۷ ماه قبل، یکشنبه ۹ اسفند ۱۳۹۴، ساعت ۱۳:۵۹
          applicationUser.Avatar یک فیلد رشته‌ای هست بیانگر نام فایل اواتار شخص.
  • #
    ‫۸ سال و ۲ ماه قبل، دوشنبه ۲۸ تیر ۱۳۹۵، ساعت ۱۷:۵۹
    با تشکر از مطلب شما.
    بنده در پروژه ای دقیقا طبق موارد توضیح داده شده در این مقاله (و سایر پروژه‌های موجود در سایت جاری) عمل کردم ام . مشکلی که دارم کوکی مربوطه موجود می‌باشد (با استفاده از فایبر باگ چک کردم) ولی کاربر در کمتر از ده دقیقه کاربر خارج میشود در صورتی که کوکی هنوز معتبر و زمان منقضی شدن آن به اتمام نرسیده است . تنظیمات به صورت خلاصه به صورت زیر می‌باشد :
       app.UseCookieAuthentication(new CookieAuthenticationOptions
                {
                    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                    LoginPath = new PathString("/Account/Login"),
                     ExpireTimeSpan = TimeSpan.FromDays(30),
                    Provider = new CookieAuthenticationProvider
                    {
                        OnValidateIdentity = StoreObjectFactory.Container.GetInstance<IApplicationUserManagerService>().OnValidateIdentity()
                    },
                    SlidingExpiration = true,
                    CookieName = "iasaccount"
                });

      public Func<CookieValidateIdentityContext, Task> OnValidateIdentity()
            {
                return SecurityStampValidator.OnValidateIdentity<ApplicationUserManagerService, User, Guid>(
                             validateInterval: TimeSpan.FromDays(3),
                             regenerateIdentityCallback: (manager, user) => GenerateUserIdentityAsync(manager, user),
                             getUserIdCallback: id => id.GetGuidUserId());
            }

    و :
                var result = await _signInManagerService.PasswordSignInAsync(loginViewModel.Email, loginViewModel.Password, loginViewModel.RememberMe, shouldLockout: false);

    آیا تنظیمات دیگه ای لازم هست که انجام شود ؟
    • #
      ‫۸ سال و ۲ ماه قبل، شنبه ۲ مرداد ۱۳۹۵، ساعت ۲۲:۰۶
      اگر پروژه‌ی مورد نظر خود را بر روی هاست آپلود کرده و کاربر به صورت اتوماتیک loggout می‌شود ممکن است  به احتمال زیاد مشکل به Idle-timeout  در IIS بر گردد .
      Idle-time Out به صورت پیش فرض بر روی 20 دقیقه تنظیم شده است .

      در سروری که بنده پروزه رو آپلود کرده بودم روی 5 تنظیم شده بود و همین موضوع باعث خروج کاربر میشد .
      غیر فعال کردن  Idle-timeout :
      ابتدا به مسیر زیر مراجعه کرده :
      C:\Windows\System32\inetsrv\config\schema\IIS_schema.xml

      مقدار Idle-timeout به صورت زیر میباشد :
      <attribute name="idleTimeout" 
             type="timeSpan" 
             defaultValue="00:20:00" 
             validationType="timeSpanRange" 
             validationParameter="0,2592000,60"/>

      اگر مقدار آن بر روی 0 تنظیم شود ، Idle-timeout غیر فعال میشود .
  • #
    ‫۸ سال و ۱ ماه قبل، جمعه ۲۲ مرداد ۱۳۹۵، ساعت ۱۴:۳۴
    سلام،

    اگر بخوایم مقاله فعلی رو با درنظر گرفتن موارد زیر به یک برنامه ASP.NET Core اعمال کنیم به چه نحوی هستش؟

    ممنون
  • #
    ‫۷ سال و ۱۱ ماه قبل، پنجشنبه ۶ آبان ۱۳۹۵، ساعت ۱۸:۴۲
    سلام؛ برای پیاده سازی سطح دسترسی پویا در مدیریت کاربران باید چگونه عمل کرد؟
  • #
    ‫۷ سال و ۹ ماه قبل، دوشنبه ۶ دی ۱۳۹۵، ساعت ۱۹:۰۱
    چه فرقی بین دو اینترفیس IUserStore و IApplicationUserManager  وجود دارد؟
    پیاده سازی IUserStore غیر ضروری است ؟
    • #
      ‫۷ سال و ۹ ماه قبل، دوشنبه ۶ دی ۱۳۹۵، ساعت ۲۲:۲۱
      - UserManager برای ذخیره سازی اطلاعات از UserStore استفاده می‌کند.
      - تمام اینترفیس‌های Identity دارای پیاده سازی‌های پیش فرضی هم هستند که امکان سفارشی سازی آن‌ها در لایه سرویس برنامه جاری پیش بینی شده‌است.
      • #
        ‫۷ سال و ۸ ماه قبل، سه‌شنبه ۱۴ دی ۱۳۹۵، ساعت ۱۳:۳۳
        سلام و خسته نباشید.
        دو راهنمایی در مورد این پیاده سازی می‌خواستم:
        1 -چون من توی برنامه خودم با جدول یوزر کار زیاد دارم میتونم یه جدول بسازم برای بقیه فیلدهای جدول کاربر و اون رو بصورت رابطه یک به یک به جدول ApplicationUser متصل کنم یا از روش Strongly Typed استفاده کنم یا اینکه همه  فیلدهای جدید که هم که مربوط به کاربران هستش رو تو همین جدول ApplicationUser تعریف کنم؟
        2 - براساس برنامه‌های بنده که چند تا Mobile App هستن میتونم یه بخش احراز هویت یکپارچه رو با این تکنولوژی راه اندازی کنم که کاربر با یکبار وارد شدن بتونه از بقیه application‌ها هم استفاده کنه؟
        • #
          ‫۷ سال و ۸ ماه قبل، سه‌شنبه ۱۴ دی ۱۳۹۵، ساعت ۱۳:۵۹
          - همین مثال جاری را بررسی کنید، به کلاس کاربران برنامه، لیست آدرس‌های شخص هم اضافه شده‌است (^ و ^).
          - ASP.NET Identity برای حالت single sign-on طراحی نشده‌است (^). هرچند با تغییراتی در کوکی‌ها و Claims میسر است (^). برای حالت single sign-on بهتر است از پروژه‌ی دیگری به نام identity server استفاده کنید (^). همچنین JWT برای این مورد خاص، گزینه‌ی بهتری است (^ و ^).
          • #
            ‫۷ سال و ۸ ماه قبل، شنبه ۱۸ دی ۱۳۹۵، ساعت ۱۶:۱۴
            سلام مجدد
            یه سری Controller پیش فرض هستش که البته mvc هستن مثل AccountController میخواستم ببینم اینا رو با WebApiController هم میتونیم پیاده سازی کنیم.
            و سوال دوم این که این AccountApiTestController دقیقا چه کاری انجام میده؟
            • #
              ‫۷ سال و ۸ ماه قبل، شنبه ۱۸ دی ۱۳۹۵، ساعت ۱۶:۴۳
              - بله. فقط بجای return Viewها شما بازگشت متداول http status‌ها را خواهید داشت مانند return NotFound و یا return Ok. مابقی کدهای آن تفاوتی نمی‌کند.
              - هدف نحوه‌ی نمایش این است که تزریق وابستگی‌های ASP.NET Identity و امکانات تنظیم شده‌ی آن در این مثال، در یک کنترلر Web API هم کار می‌کند.
              - اگر هدف صرفا استفاده از Single page applications و Web API است، روش استفاده‌ی JWT متداول‌تر است.
  • #
    ‫۷ سال و ۸ ماه قبل، سه‌شنبه ۲۱ دی ۱۳۹۵، ساعت ۲۲:۱۴
    بنده بعد از دیباگ و اجرا کردن وب اپلیکیشن در خط  با پیغام خطای زیر مواجه شدم :
    System.Data.Entity.Validation.DbEntityValidationException: Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
    Line 118:            {
    Line 119:                user = new ApplicationUser { UserName = name, Email = name };
    Line 120:                var result = this.Create(user, password);
    Line 121:                result = this.SetLockoutEnabled(user.Id, false);
    Line 122:            }

    ولی خب با تغییر پسورد پیش فرض ادمین و پاک کردن علامت @ در پسورد تو خط بعدی به خطا خوردم با همچنین اخطاری :
    Exception Details: System.InvalidOperationException: UserId not found. 
    Line 119:                user = new ApplicationUser { UserName = name, Email = name };
    Line 120:                var result = this.Create(user, password);
    Line 121:                result = this.SetLockoutEnabled(user.Id, false);
    Line 122:            }
    Line 123:

    بعد از دیباگ کردن متوجه شدم که یوزر رو توی متغیر result نال برگردونده.
      • #
        ‫۷ سال و ۸ ماه قبل، چهارشنبه ۲۲ دی ۱۳۹۵، ساعت ۰۱:۴۸
        پیاده سازی ApplicationUserManager  رو به روز کردم و در پیاده سازی applicationDbContext موارد گفته شده رو درنحوه‌ی استخراج اطلاعات DbEntityValidationException لحاظ کردم.
        //...
                                Debug.WriteLine(err.PropertyName + " " + err.ErrorMessage);
        ولی هنوز مشکل رو نتونستم برطرف کنم متاسفانه :
        Exception Details: System.Data.Entity.Validation.DbEntityValidationException: Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
        • #
          ‫۷ سال و ۸ ماه قبل، چهارشنبه ۲۲ دی ۱۳۹۵، ساعت ۰۳:۲۵
          Debug.WriteLine در پنجره دیباگ ویژوال استودیو پیام‌ها را نمایش می‌دهد. اگر این پنجره را مشاهده نمی‌کنید این سطر را تبدیل به throw new Exception کنید تا واضح‌تر شود؛ یا روی آن یک break point قرار دهید و پیام‌ها را یکی یکی بررسی کنید.
          • #
            ‫۷ سال و ۸ ماه قبل، چهارشنبه ۲۲ دی ۱۳۹۵، ساعت ۱۷:۲۵
            سلام .مشکل برطرف گردید. بعد از دیباگ کردن متوجه شدم که روابط بین جدول‌ها رو در دوتا جدول برعکس تعریف کرده بودم.
  • #
    ‫۷ سال و ۸ ماه قبل، پنجشنبه ۲۳ دی ۱۳۹۵، ساعت ۱۴:۴۴
    سلام مجدد؛ دو تا راهنمایی دیگه از دوستان می‌خواستم.
    1- همانطور که عنوان شد برای راهکارهای single page app و راهکارهای صرفا WebApi بهتر است از روش jwt استفاده گردد. حالا اگر سیستم نرم افزاری ما ترکیبی باشه(ASS.NET MVC و WebApi و ...) آیا پیاده سازی و توسعه همین سیستم منطقی‌تر نیست؟
    2- روال کاری که در ASP.NET Identity در اینجا و در مثال رسمی خود مایکروسافت هستش بصورت ثبت نام و فعال سازی احراز هویت دو مرحله ای بعد از ثبت نام و در کنترل پنل کاربر(User Profile) هستش. ولی اگه بخوایم راهکار ما بصورت ثبت نام کاربر(نام کاربری - رمز عبور - تلفن همراه و ...) باشه و پس از ثبت نام با ارسال کد فعال سازی به تلفن همراه یا ایمیل کاربر و وارد کردن این کد توسط کاربر در صفحه مخصوص خودش(Verify Code) باشه(سیستم احراز هویت برنامه هایی مثل تلگرام و ...) آیا تابع Register دوباره بازنویسی باید بشه(چه در MVC Controller چه در WebApi Controller) یا توابع جدیدی برای این کار تعریف بشه. البته خب فکر میکنم باید View Model‌های جدید تعریف بشه چون یه مقدار اون قسمت ViewModel‌های داخل پروژه(Register  و ...) برای بنده گنگ بود برای پیاده سازی این کار مثل FactorViewModel و  SendCodeViewModel.
    ممنون میشم یه مقدار بنده رو راهنمایی کنید تو این مبحث یا اگه پیاده سازی و مثال خاصی با پیاده سازی در این زمینه می‌شناسید به بنده معرفی کنید.
  • #
    ‫۷ سال و ۸ ماه قبل، پنجشنبه ۳۰ دی ۱۳۹۵، ساعت ۲۱:۴۰
    سلام مجدد
    یه سوالی داشتم. آیا Dependencr Resolver برای کنترلرهای از نوع Web Api در این مثال گنجانده شده، چون الان وقتی دو تا اکشن متد از نوع WebApi  و با Verb از نوع httpPostداخل کنترلر AccountApi تعریف میکنیم هنگام فراخوانی و ارسال json به آن‌ها به مشکل برخورده و ارور زیر را نمایش می‌دهد:
    exceptionMessage": "An error occurred when trying to create a controller of type 'AccountApiController'. Make sure that the controller has a parameterless public constructor.",
    "innerException": {
        "message": "An error has occurred.",
        "exceptionMessage": "Type 'MyProject.Web.Controllers.AccountApiController' does not have a default constructor",
        "exceptionType": "System.ArgumentException",
  • #
    ‫۷ سال و ۸ ماه قبل، پنجشنبه ۷ بهمن ۱۳۹۵، ساعت ۱۶:۵۹
    سلام مجدد.
    برای نوشتن تست‌های واحد(Unit Test) برای پروژه مذکور به چه صورت میشه  EntityFramework رو Mock کرد؟ یعنی تزریق وابستگی رو با StructureMap میشه انجام داد یا باید پیاده سازی دیگه ای رو استفاده کرد. میشه یه راهنمایی در مورد نوشتن یه تست واحد توی این سناریو بکنید.
  • #
    ‫۷ سال و ۶ ماه قبل، چهارشنبه ۱۱ اسفند ۱۳۹۵، ساعت ۱۹:۱۴
    چگونه کد زیر را پیاده سازی کنیم و ایمیل کاربر جاری را دریافت کنیم ؟
    مثال :
    _identity.GetEmailAdress()

    • #
      ‫۷ سال و ۶ ماه قبل، چهارشنبه ۱۱ اسفند ۱۳۹۵، ساعت ۲۰:۳۶
      روش‌های مختلفی در کلاس ApplicationUserManager برای یافتن کاربر جاری (و سپس اطلاعات مرتبط به آن) وجود دارد. از یکی از آن‌ها استفاده کنید.
      • #
        ‫۷ سال و ۶ ماه قبل، چهارشنبه ۱۱ اسفند ۱۳۹۵، ساعت ۲۰:۵۰
        روشی برای این که آدرس ایمیل از بانک دریافت نشود هست ؟ 
        من این سوال ^ را مطرح کردم که پاسخ یکی از کاربران این بود که از claims استفاده کنم.
        • #
          ‫۷ سال و ۶ ماه قبل، چهارشنبه ۱۱ اسفند ۱۳۹۵، ساعت ۲۱:۰۷
          بله. روش آ‌ن‌را برای NET Core. در اینجا توضیح دادم: «سفارشی سازی ASP.NET Core Identity - قسمت چهارم - User Claims» 
          در نگارش 2x هم در متد GenerateUserIdentityAsync (که به همراه اطلاعات ApplicationUser وارد شده‌ی به سیستم هست) فرصت خواهید داشت تا یک Claim جدید را اضافه کنید (همانجایی که نوشته شده Add custom user claims here):
          // Add custom user claims here
          userIdentity.AddClaim(new Claim("key1", "value1"));
          دسترسی به آن هم بعدا به این صورت خواهد بود (که در اینجا IPrincipal همان this.User قابل دسترسی در یک اکشن متد است):
          public static string GetClaimValue(this IPrincipal currentPrincipal, string key)
          {
               var identity = currentPrincipal.Identity as ClaimsIdentity;
               if (identity == null)
                    return null;
          
               var claim = identity.Claims.FirstOrDefault(c => c.Type == key);
               return claim?.Value;
          }
          • #
            ‫۷ سال و ۶ ماه قبل، چهارشنبه ۱۱ اسفند ۱۳۹۵، ساعت ۲۲:۴۵
            طبق فرمایش شما  از کد زیر استفاده کردم اما claims  به نام emailaddress  یافت نمی‌شود.  
               public async Task<ClaimsIdentity> GenerateUserIdentityAsync(User applicationUser)
                    {
                        // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
                        var userIdentity = await CreateIdentityAsync(applicationUser, DefaultAuthenticationTypes.ApplicationCookie);
                        // Add custom user claims here
                        userIdentity.AddClaim(new Claim("emailaddress", applicationUser.Email));
                        return userIdentity;
                    }

            و زمانی که کاربر وارد سایت می‌شود GenerateUserIdentityAsync  را فراخوانی می‌کنم.
              public virtual async Task<ActionResult> Login(LoginViewModel viewModel, string returnUrl)
              {
            
                   // ... more code
                    var user =  await this._userManager.FindByNameAsync(viewModel.UserName);
                    await this._userManager.GenerateUserIdentityAsync(user);
            
                        return this.View(viewModel);
              }

            و در Container :
             _.For<IPrincipal>().Use(() => HttpContext.Current.User);
            نحوه استفاده :
            var useremail = this._principal.GetClaimValue("emailaddress");

            • #
              ‫۷ سال و ۶ ماه قبل، چهارشنبه ۱۱ اسفند ۱۳۹۵، ساعت ۲۲:۵۳
              GenerateUserIdentityAsync نباید به صورت دستی فراخوانی شود (چون صرفا یک خروجی را باز می‌گرداند که هنوز به Response اضافه نشده‌است). این متد به صورت خودکار در متد OnValidateIdentity فراخوانی می‌شود و نتیجه‌ی نهایی آن به کوکی شخص اضافه خواهد شد.
              یک breakpoint روی قسمتی که claim را اضافه کرده‌اید قرار دهید تا مشخص شود که آیا این قطعه از کد پس از لاگین کاربر فراخوانی می‌شود یا خیر.
  • #
    ‫۷ سال و ۶ ماه قبل، پنجشنبه ۱۲ اسفند ۱۳۹۵، ساعت ۱۶:۲۹
    به روز رسانی
    این پروژه جهت بهبود تنظیمات تزریق وابستگی‌های آن به روز رسانی شد (تغییرات در لایه سرویس، تنظیمات IoC و کلاس آغازین برنامه).
  • #
    ‫۷ سال و ۶ ماه قبل، پنجشنبه ۱۲ اسفند ۱۳۹۵، ساعت ۱۷:۲۳
    یک نکته‌ی تکمیلی
    - اگر به سورس ASP.NET Identity 2.x مراجعه کنید، تنها قسمتی در آن که سرویس مورد نیاز خود را مستقیما از owin context دریافت کرده‌است، سطر زیر می‌باشد:
    var manager = context.OwinContext.GetUserManager<TManager>();
    بنابراین در تنظیمات تزریق وابستگی‌های این نوع برنامه‌ها، وجود یک سطر ذیل در کلاس Startup برنامه ضروری می‌باشد:
    app.CreatePerOwinContext(() => (ApplicationUserManager)container.GetInstance<IApplicationUserManager>());
    و اگر این سطر را نداشته باشید، کار تعیین اعتبار SecurityStamp و همچنین ساخت اطلاعات کوکی کاربر بر اساس user claims آن مختل خواهد شد.
    - سایر app.CreatePerOwinContext‌های کلاس آغازین برنامه اختیاری هستند و ارجاع مستقیمی به آن‌ها در کدهای ASP.NET Identity 2.x وجود ندارد. برای مثال اگر در کدهای شما چنین سطری موجود نیست:
    var dbContext = HttpContext.GetOwinContext().Get<ApplicationDbContext>();
    نیازی به ثبت سرویس آن توسط app.CreatePerOwinContext وجود نداشته و ثبت آن یک سربار اضافی خواهد بود.
  • #
    ‫۷ سال و ۶ ماه قبل، شنبه ۱۴ اسفند ۱۳۹۵، ساعت ۰۱:۰۱
    من فایل رو از روی آخرین کامنت شما برداشتم و عینا اجرا کردم. بعد از Login همه چیز خوب کار می‌کند تا وقتی که به قسمت Manage منتقل شویم. مثلا برای تایید email یا تایید شماره تلفن. بعد از اینکه هر کدام از این کارها انجام شود، برای هر Viewای که Controller آن به صفت [Authorize] مزین شده باشد، ابتدا به صفحه‌ی Login هدایت می‌شود. آیا این رفتار طبیعی‌ست یا چیزی از قلم افتاده؟
    • #
      ‫۷ سال و ۶ ماه قبل، شنبه ۱۴ اسفند ۱۳۹۵، ساعت ۰۲:۱۸
      - UpdateSecurityStamp دقیقا همین کار را انجام می‌دهد. اجبار به اعتبارسنجی مجدد کوکی و در صورت نیاز، لاگین مجدد، پس از تغییر قسمت‌های مهم اکانت شخص.
      - روش دیگر آ‌ن‌را برای NET Core. در اینجا توضیح دادم: «سفارشی سازی ASP.NET Core Identity - قسمت چهارم - User Claims» . قسمت «چگونه پس از ویرایش اطلاعات کاربر، اطلاعات کوکی او را نیز به روز کنیم؟ » 
      • #
        ‫۷ سال و ۶ ماه قبل، شنبه ۱۴ اسفند ۱۳۹۵، ساعت ۰۳:۱۰
        در کلاس ApplicationUserManager  و در متد OnValidateIdentity مقدار validateInterval برابر با صفر بود. با تغییر آن به عدد دیگری مانند ۳۰، مشکل حل شد.   
        • #
          ‫۷ سال و ۶ ماه قبل، شنبه ۱۴ اسفند ۱۳۹۵، ساعت ۰۳:۲۳
          علت صفر درنظر گرفتن این عدد این هست که اگر ادمین کاربر را غیرفعال کرد یا مثلا سطوح دسترسی و نقش‌های او را تغییر داد، با فراخوانی UpdateSecurityStamp، کوکی او نیز «بلافاصله» تعیین اعتبار مجدد شده و همچنین به روز شود تا بلافاصله تغییرات را مشاهده کند؛ نه اینکه در 30 دقیقه‌ی دیگر این تغییرات را دریافت کند و کوکی او اعتبارسنجی مجدد شود.
          • #
            ‫۷ سال و ۶ ماه قبل، شنبه ۱۴ اسفند ۱۳۹۵، ساعت ۱۵:۵۳
            با تشکر از توجه شما. 
            در این پروژه نمونه و با همین تنظیمات، شرایط بدین گونه است: 
            کاربر سایت را به صورت ناشناس مشاهده می‌کند. سپس با اطلاعات خود وارد سایت می‌شود. پس از تعییت هویت، مثلا به بخش Product هم دسترسی پیدا می‌کند. تا این مرحله همه چیز خوب است و خیر. سپس از حساب خود خارج می‌شود و دقایقی بعد، مجددا وارد حساب خود می‌شود. از این لحظه به بعد، رفتار برنامه متفاوت می‌شود. چرا که با وجود اینکه کاربر تایید هویت شده، اما اگر بخواهد مثلا دوباره به همان بخش Product مراجعه کند، سیستم ابتدا او را به صفحه‌ی ورود هدایت کرده و با کاربر، به عنوان یک کاربر ناشناخته برخورد می‌کند. همین گونه است برای سایر قسمتهایی که صفت Authorize  برای آنها لحاظ شده. پرسش من این است که آیا این رفتار طبیعی ست؟
            قبلا گفتم که با تغییر مقدار validateInterval   ظاهرا این مشکل بر طرف می‌شود. لااقل این رفتار تغییر می‌کند. شما  فرمودید که در نظرگرفتن هر عددی جز صفر، موجب می‌شود تا کوکی بلافاصله به روز نشود. این صحبت شما کاملا صحیح و منطقی. اما آیا ورودهای مکرر به نظر شما منطقی ست؟
            به کدهای این پروژه هم دقت کردم. مقدار validateInterval   برابر با 30 لحاظ شده که ظاهرا نباید اینگونه باشد. برای اینکه هم به رویکردی که شما به آن اشاره کردید برسیم و هم کاربر مجبور نباشد مدام Login کند، لطفا بفرمایید که چه تغییراتی لازم است؟ در صورتیکه من اشتباه کرده ام و یا چیزی را ندیده و یا از قلم انداخته ام، لطف می‌کنید اگر راهنمایی بفرمایید.
            • #
              ‫۷ سال و ۶ ماه قبل، شنبه ۱۴ اسفند ۱۳۹۵، ساعت ۱۸:۳۰
              نیاز به دو تغییر دارد:
              - یکی کردن کوکی‌های owin و system.web:
              CookieManager = new SystemWebCookieManager()
              - استفاده اجباری از پارامتر manager دریافتی از owin context:
              regenerateIdentityCallback: (manager, user) => manager.GenerateUserIdentityAsync(user)
  • #
    ‫۷ سال و ۶ ماه قبل، سه‌شنبه ۲۴ اسفند ۱۳۹۵، ساعت ۱۴:۵۵
    علت استفاده از ( ConfigureAwait(false در متد‌های Async چیست ؟ 
  • #
    ‫۷ سال و ۵ ماه قبل، جمعه ۱ اردیبهشت ۱۳۹۶، ساعت ۱۷:۵۸
    با سلام؛ میخواستم ببینم چطور میتونم پروژه ام رو Role Base کنم.(طبق اطلاعات همین مقاله)
  • #
    ‫۷ سال و ۴ ماه قبل، دوشنبه ۱۸ اردیبهشت ۱۳۹۶، ساعت ۰۶:۵۱
    با سلام؛ چطور میشه واسه این پروژه یک custom filter attribute که در اون از UserMangerService و UOW استفاده میشه ، درست کرد؟ البته این رو برای لاگ گرفتن کاربران جهت استفاده از اکشن و کنترول‌ها می‌خواستم. من این مقاله ( تزریق وابستگی‌ها در فیلترهای ASP.NET MVC ) رو دنبال کردم ، ولی باز هم جواب نداد.
  • #
    ‫۷ سال و ۳ ماه قبل، دوشنبه ۲۲ خرداد ۱۳۹۶، ساعت ۱۹:۰۳
    با سلام ، در مورد استفاده از Claim‌ها با استفاده از روش شما ، بعد از لاگین نام و نام خانوادگی کاربر را که قرار است در سایت نمایش بدهد را نمایش نداده ، بعد از refresh صفحه نمایش میدهد ؟!
    • #
      ‫۷ سال و ۳ ماه قبل، دوشنبه ۲۲ خرداد ۱۳۹۶، ساعت ۲۱:۲۷
      سلام ، با دیباگ کردن فهمیدم بعد از لاگین در قسمت زیر Claim را null برمیگردونه
              public static string GetClaimValue(this IPrincipal currentPrincipal, string key)
              {
                  var identity = currentPrincipal.Identity as ClaimsIdentity;
                  if (identity == null)
                      return null;
      
                  var claim = identity.Claims.FirstOrDefault(c => c.Type == key);
                  return claim?.Value;
              }
      که با برداشتن breakpoint قسمت CreateUserIdentityAsync در ApplicationSignInManager
              public override Task<ClaimsIdentity> CreateUserIdentityAsync(User user)
              {
                  return _userManager.GenerateUserIdentityAsync(user);
              }
      و استفاده در AccountController در اکشن login قسمت post بصورت زیر مشکل حل شد
                      case SignInStatus.Success:
                          var user = await _userManager.FindByNameAsync(model.UserName);
                          await _signInManager.CreateUserIdentityAsync(user);
                          return RedirectToLocal(returnUrl);

      • #
        ‫۷ سال و ۳ ماه قبل، سه‌شنبه ۲۳ خرداد ۱۳۹۶، ساعت ۱۵:۳۳
        - همان از حالت کامنت خارج کردن قسمت CreateUserIdentityAsync در ApplicationSignInManager کافی هست. علت اینجا است که متد SignInManager.PasswordSignInAsync در انتهای کارش، متد SignInAsync را فراخوانی می‌کند. این متد سبب به روز رسانی کوکی کاربر با Claims جدید او می‌شود.
        - بنابراین هر نوع افزوده شدن Claims باید «پیش از» متد PasswordSignInAsync  باشد و یا دقیقا در همان متد userManager.GenerateUserIdentityAsync و چون در کلاس SignInManager فراخوانی نمی‌شد، در حین لاگین هم تنظیم نمی‌شد. علت تنظیم آن با ریفرش بعدی، وارد عمل شدن Security Stamp Validator است.
        - فراخوانی متد signInManager.CreateUserIdentityAsync پس از لاگین هیچ تاثیری ندارد، چون وارد پروسه‌ی SignInAsync و به روز رسانی کوکی‌های کاربر نمی‌شود.
  • #
    ‫۷ سال قبل، دوشنبه ۲۷ شهریور ۱۳۹۶، ساعت ۱۴:۴۶
    سلام
    من از متد OnValidateIdentity برای بررسی آخرین وضعیت کاربر و .. در بازه زمانی X استفاده می‌کنم. مشکلی که من با این متد دارم تغییرات claim‌ها رو به کوکی response اعمال می‌کنه (مثلا تغییر نام کاربر) و تغییرات تا درخواست بعدی کاربر قابل استفاده نیست یعنی CurrentPrincipal تا درخواست بعدی آپدیت نمیشه. در هنگام لاگین این موضوع به چشم نمی‌آید چون با ریدایرکت کردن صفحه request جدیدی که حاوی کوکی آپدیت شده است ایجاد میشه به دنبال اون CurrentPrincipal بروز شده است.
    • #
      ‫۷ سال قبل، دوشنبه ۲۷ شهریور ۱۳۹۶، ساعت ۱۴:۵۵
      بله. ASP.NET Identity به همین صورت طراحی شده (تمام نگارش‌های آن). اطلاعات CurrentPrincipal در درخواست بعدی از کوکی‌های کاربر خوانده می‌شوند.
      • #
        ‫۷ سال قبل، دوشنبه ۲۷ شهریور ۱۳۹۶، ساعت ۱۵:۰۹
        به نظر شما راهی هست بتونیم خودمون CurrentPrincipal  رو  آپدیت کنیم ؟ من هرکاری کردم نشد حتی سورس owin  رو بررسی کردم چیزی دستگیرم نشد.
        • #
          ‫۷ سال قبل، دوشنبه ۲۷ شهریور ۱۳۹۶، ساعت ۱۵:۳۰
          شما اگر در این بین نیاز به اطلاعات کاربر جاری داشتید، فقط باید از طریق usermanger و یافتن این کاربر به صورت مستقیم از بانک اطلاعاتی اقدام کنید. برای مثال بلافاصله پس از لاگین اطلاعات HttpContext.Current.User نال هست، چون هنوز درخواست بعدی نرسیده‌است تا این User از کوکی او خوانده شده و مقدار دهی شود. در اینجا فقط باید مستقیما از بانک اطلاعاتی کوئری بگیرید.
  • #
    ‫۶ سال و ۳ ماه قبل، یکشنبه ۶ خرداد ۱۳۹۷، ساعت ۲۳:۵۱
    با سلام؛ من همین روش رو رفتم و توی پروژه م اضافه کردم. اما وقتی میخوام Add-Migration بزنم ، به چنین اروری برمی خوردم:
    One or more validation errors were detected during model generation:
    DataLayer.Context.CustomUserRole: : EntityType 'CustomUserRole' has no key defined. Define the key for this EntityType.
    DataLayer.Context.CustomUserLogin: : EntityType 'CustomUserLogin' has no key defined. Define the key for this EntityType.
    CustomUserRoles: EntityType: EntitySet 'CustomUserRoles' is based on type 'CustomUserRole' that has no keys defined.
    CustomUserLogins: EntityType: EntitySet 'CustomUserLogins' is based on type 'CustomUserLogin' that has no keys defined.
    آیا ممکنه بخاطر آپدیت کردن پکیج‌های مربوطه باشه؟
      • #
        ‫۶ سال و ۳ ماه قبل، دوشنبه ۷ خرداد ۱۳۹۷، ساعت ۰۲:۰۴
        درست شد!
        مشکل از 
        protected override void OnModelCreating(DbModelBuilder builder)
                {
                    base.OnModelCreating(builder);
        بود. متد base رو ننوشته بودم.
  • #
    ‫۵ سال و ۵ ماه قبل، جمعه ۲ فروردین ۱۳۹۸، ساعت ۱۸:۰۶
    با سلام؛ می‌خواستم ببینم که مسئله transactionscope rollback  رو چطور تو این مثال رعایت کردین. در AccountController از unitOfWork استفاده نشده است. سوال؟ اگر در هنگام ثبت کاربر لازم باشه که اطلاعات دیگری در جداول دیگری ثبت بشه چی. اگر کاربر ثبت بشه ولی اطلاعات در جداول دیگه در هنگام ثبت دچار خطا بشن، آیا کل تراکنش rollback میشه؟ 
    • #
      ‫۵ سال و ۵ ماه قبل، جمعه ۲ فروردین ۱۳۹۸، ساعت ۱۸:۱۳
      تمام اعمال EF تراکنشی هستند. اگر در Context جاری (واحد کار جاری) چندین عملیات Add و Update و Remove را انجام دهید و در پایان آن‌ها SaveChanges را فراخوانی کنید، تمام این‌ها با هم در طی یک تراکنش درج خواهند شد (این همان مفهوم unit of work است و ضمنا تمام سرویس‌های آن هم از همین مفهوم استفاده می‌کنند و هر کنترلری هم که از این سرویس‌ها استفاده کند، به همین ترتیب). کلا هر جائیکه SaveChanges فراخوانی می‌شود، پایان تراکنش «ضمنی» جاری است. بدیهی است چندین بار می‌توان SaveChanges را در مقاطع مختلفی فراخوانی کرد. همچنین خود پروژه‌ی Identity هم در پشت صحنه SaveChanges را به کرات و راسا فراخوانی می‌کند (خارج از کنترل استفاده کننده). این‌ها مباحث مقدماتی EF Code First هستند. اطلاعات بیشتر
      • #
        ‫۵ سال و ۵ ماه قبل، جمعه ۲ فروردین ۱۳۹۸، ساعت ۱۹:۰۸
        وقتی کاربر جدید ثبت میکنیم:
        var createResult = this.Create(user, password); 
        var result = await _userManager.CreateAsync(user, model.Password).ConfigureAwait(false);
        دیگه به تابع  SaveChanges  دسترسی نداریم. unit of work در AccountController استفاده نشده. برای بقیه entity‌ها که خودمون ایجاد کردیم و اینترفیس و سرویس براشون ایجاد کردیم (مثل کلاس EfCategoryService ) برای اون جواب میده؛ چون که در سازنده IUnitOfWork رو دریافت میکنم.
        • #
          ‫۵ سال و ۵ ماه قبل، جمعه ۲ فروردین ۱۳۹۸، ساعت ۱۹:۱۸
          unit of work نام دیگر Context جاری است که در سرویس userManager هم از آن استفاده می‌شود. این تزریق وابستگی‌ها است که تضمین می‌کند در طول درخواست جاری، در هر قسمتی از برنامه که از Context استفاده می‌کند (چه به صورت مستقیم از طریق تزریق DbContext در سرویس‌های خود Identity در پشت صحنه و چه توسط تزریق IUnitOfWork در سایر قسمت‌های برنامه)، از همان وهله‌ای که در ابتدای کار درخواست ایجاد شده، استفاده شود (این مورد مبحث مدیریت خودکار طول عمر سرویس‌ها توسط یک Ioc Container است).
  • #
    ‫۵ سال قبل، چهارشنبه ۲۰ شهریور ۱۳۹۸، ساعت ۰۲:۳۳
    سلام؛ همچین مثالی با این جزئیات واسه  net core هم تو سایت شما وجود داره؟ 
  • #
    ‫۴ سال و ۱۰ ماه قبل، پنجشنبه ۲۳ آبان ۱۳۹۸، ساعت ۲۱:۵۷
    سلام
    آیا امکانش هست به جای نوشتن [Authorize(Roles = "admin")]  برای هر Controller یا Action به صورت دینامیک role‌های مربوطه اعتبارسنجی شوند؟ 
    • #
      ‫۴ سال و ۱۰ ماه قبل، پنجشنبه ۲۳ آبان ۱۳۹۸، ساعت ۲۲:۳۹
      همان واژه‌ی «دینامیک» را در صفحه‌ی جاری جستجو کنید؛ به همراه بررسی این پروژه‌ها.