Asp.Net Mvc Identity için Unity Dependency Injection Konfigürasyonu

Merhabalar herkese. Bu yazımda Asp.Net Mvc’nin kendi bünyesinde barındırmış olduğu Identity sisteminin Unity Dependency Injection yöntemiyle nasıl ayağa kaldırıldığını adım adım inceliyor olacağız. Ayrıca ek olarak, katmanlı mimari yapısını da böylelikle görmüş olacağız.

1.Adım Katmanları Oluşturmak

Katmanlara göz attığımızda her bir katmanın ayrı ayrı kendine has görevleri var. Unity Container konfigürasyonlarımızı .IoC adlı katmanda tutuyor olacağız.

Alt yapı adlı klasörümüzün içinde gördüğünüz gibi .Ninject ve .loC adlı katmanlarımız mevcuttur.

Asp.Net Identity’nin IdentityDbContext ve gerekli entitylerini(User,Role,UserRole gibi) ben .Entities katmanında barındırıyorum. Böylece gerektiğinde her katman bu nesnelere erişebilir.

Identity adlı klasörün altında gerekli nesnelerimi oluşturdum.

IdentityContext adlı nesnemizin kodları şu şekildedir ;

using Microsoft.AspNet.Identity.EntityFramework;
using SeizeTheDay.Entities.Identity.Entities;
using System.Data.Entity;
using System.Data.Entity.Migrations;

namespace SeizeTheDay.Entities.Identity
{

    public class IdentityContext : IdentityDbContext<User>
    {
        public IdentityContext()
            : base("IdendityConnection", throwIfV1Schema: false)
        {
        }
        public static IdentityContext Create()
        {
            return new IdentityContext();
        }
        static IdentityContext()
        {
            Database.SetInitializer<IdentityContext>(null);
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {

            base.OnModelCreating(modelBuilder);

            modelBuilder.Entity<User>().ToTable("UserInfoes");
            modelBuilder.Entity<IdentityRole>().ToTable("Roles");
            modelBuilder.Entity<IdentityUserRole>().ToTable("UserRoles");
            modelBuilder.Entity<IdentityUserClaim>().ToTable("UserClaims");
            modelBuilder.Entity<IdentityUserLogin>().ToTable("UserLogins");
            modelBuilder.Entity<IdentityUser>().ToTable("Users");

            modelBuilder
                .Entity<IdentityRole>()
                .Property(p => p.Name)
                .HasMaxLength(255);

            modelBuilder
                .Entity<IdentityUser>()
                .Property(p => p.UserName)
                .HasMaxLength(256);

            modelBuilder
              .Properties()
              .Where(p => p.PropertyType == typeof(string) &&
                          !p.Name.Contains("Id") &&
                          !p.Name.Contains("Provider"))
              .Configure(p => p.HasMaxLength(255));

        }
    }
    
    //For Mysql configuration
    internal class Configuration : DbMigrationsConfiguration<IdentityContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = true;
            SetSqlGenerator("Devart.Data.MySql", new Devart.Data.MySql.Entity.Migrations.MySqlEntityMigrationSqlGenerator());
        }
    }

}

Örnek olması açısından örneğin bir User nesnesi ise IdentityUser adlı kalıtımdan inherit edilmiştir.

using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using System;
using System.Security.Claims;
using System.Threading.Tasks;

namespace SeizeTheDay.Entities.Identity.Entities
{
    public class User : IdentityUser
    {
        public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<User> manager)
        {
            // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
            var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
            // Add custom user claims here
            return userIdentity;
        }

        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime? BirthDate { get; set; }
        public string Address { get; set; }
        public string PhotoPath { get; set; }
        public string FacebookLink { get; set; }
        public string TwitterLink { get; set; }
        public string SkypeID { get; set; }
        public string Status { get; set; }
        public bool? IsDefault { get; set; }
        public bool? IsActive { get; set; }
        public DateTime? LastLoginDate { get; set; }
        public DateTime? RegisteredDate { get; set; }
        public int? InsertBy { get; set; }
        public DateTime? LastModified { get; set; }
        public int? LastModifiedBy { get; set; }
        public string CoverPhotoPath { get; set; }
        public string UserCity { get; set; }
        public int? CountryID { get; set; }
        public int? UserTypeID { get; set; }
        public string UserTask { get; set; }
        public string TagUserName { get; set; }
        public string TagColor { get; set; }
    }
}

FirstName ile başlayan propertyleri ben ayrı bir UserInfo adlı bir tablo tutuyorum, o yüzden bu nesneleri oluşturdum siz oluşturmak istemiyorsanız nesnelerinin tümünü silebilirsiniz.

2.Adım Identity Managers Nesnelerini Oluşturmak

Bildiğiniz üzere Identity’nin kendi bünyesinde barındırdığı manager sınıfları mevcuttur. Bunlar ApplicationRoleManager, ApplicationSignInManager ve ApplicationUserManager olarak adlandırılır. Bu sınıfları ben .Business adlı katmanın içinde barındırıyorum ve bu sınıfları Unity Dependency Injection yardımıyla ayağa kaldırıyorum.

ApplicationUserManager burada bizim için önemli bir parametredir. Asp.Net Identity’nin default olarak oluşturduğu bu UserManager sınıfı bizim oluşturacağımız bu UserManager daha farklı olacaktır. UserManager sınıfının kodları aşağıdaki gibidir.

using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security.DataProtection;
using SeizeTheDay.Entities.Identity.Entities;
using System;

namespace SeizeTheDay.Business.Concrete.IdentityManagers
{
    public class ApplicationUserManager : UserManager<User>
    {

        //We are using this provider in Startup
        public static IDataProtectionProvider DataProtectionProvider { get; set; }


        public ApplicationUserManager(IUserStore<User> store) : base(store)
        {
            // Configure validation logic for usernames
            this.UserValidator = new UserValidator<User>(this)
            {
                AllowOnlyAlphanumericUserNames = false,
                RequireUniqueEmail = true
            };

            // Configure validation logic for passwords
            this.PasswordValidator = new PasswordValidator
            {
                RequiredLength = 6,
                RequireNonLetterOrDigit = false,
                RequireDigit = false,
                RequireLowercase = false,
                RequireUppercase = false,
            };

            // 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 it in here.
            this.RegisterTwoFactorProvider("Phone Code", new PhoneNumberTokenProvider<User>
            {
                MessageFormat = "Your security code is {0}"
            });
            this.RegisterTwoFactorProvider("Email Code", new EmailTokenProvider<User>
            {
                Subject = "Security Code",
                BodyFormat = "Your security code is {0}"
            });
            this.EmailService = new EmailService();
            this.SmsService = new SmsService();

            var dataProtectionProvider = DataProtectionProvider;
            if (dataProtectionProvider != null)
            {
                IDataProtector dataProtector = dataProtectionProvider.Create("ASP.NET Identity");

                this.UserTokenProvider = new DataProtectorTokenProvider<User>(dataProtector);
            }
        }
    }
}

ApplicationUserManager sınıfımız bu şekildedir. Role ve SignIn sınıflarımız ise şu şekildedir ;

using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security;
using SeizeTheDay.Entities.Identity.Entities;
using System.Security.Claims;
using System.Threading.Tasks;

namespace SeizeTheDay.Business.Concrete.IdentityManagers
{
    // Configure the application user manager used in this application. UserManager is defined in ASP.NET Identity and is used by the application.

    // Configure the application sign-in manager which is used in this application.
    public class ApplicationSignInManager : SignInManager<User, string>
    {
        public ApplicationSignInManager(ApplicationUserManager userManager, IAuthenticationManager authenticationManager)
            : base(userManager, authenticationManager)
        {
        }

        public override Task<ClaimsIdentity> CreateUserIdentityAsync(User user)
        {
            return user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
        }
    }
}
using System.Linq;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using SeizeTheDay.Entities.Identity;
using SeizeTheDay.Entities.Identity.Entities;

namespace SeizeTheDay.Business.Concrete.IdentityManagers
{
    public class ApplicationRoleManager : RoleManager<Roles>
    {
        public ApplicationRoleManager(IRoleStore<Roles, string> roleStore)
            : base(roleStore)
        {

        }
        
        public static ApplicationRoleManager Create(IdentityFactoryOptions<ApplicationRoleManager> options, IOwinContext context)
        {
            return new ApplicationRoleManager(new RoleStore<Roles>(context.Get<IdentityContext>()));
        }
    }
}

Manager sınıflarımız bu şekildedir. Eğer buraya kadar anlaşılmayan bir nokta olursa lütfen çekinmeden benimle iletişime geçiniz. Her yiğidin yoğurt yiyişi farklıdır derler, katmanlı mimari sayesinde her bir nesneyi ait olduğu katmana yerleştirip, daha derli toplu bir mekanizma oluşturmayı hedefledim kendi çapımda 🙂

3.Adım Startup.Auth Konfigürasyonu

Bu adıma kadar önemli aşamalardan geçtik. Şimdi Startup.Auth dosyamızı ayarlamamız gerekmekte.

using System;
using System.Web.Mvc;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.DataProtection;
using Owin;
using SeizeTheDay.Business.Concrete.IdentityManagers;
using SeizeTheDay.Entities.Identity.Entities;

namespace SeizeTheDay
{
    public partial class Startup
    {
   
        public void ConfigureAuth(IAppBuilder app)
        {
            // Configure the db context, user manager and signin manager to use a single instance per request

            ApplicationUserManager.DataProtectionProvider = app.GetDataProtectionProvider();
            app.CreatePerOwinContext(() => DependencyResolver.Current.GetService<ApplicationUserManager>());
            app.CreatePerOwinContext(() => DependencyResolver.Current.GetService<ApplicationSignInManager>());
            app.CreatePerOwinContext(() => DependencyResolver.Current.GetService<ApplicationRoleManager>());
 
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                LoginPath = new PathString("/Account/Login"),
                Provider = new CookieAuthenticationProvider
                {

                    OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, User>(
                        validateInterval: TimeSpan.FromMinutes(0),
                        regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
                }
            });            
            app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

  app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
 app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);

        
        }
    }
}

Bu kod bizim için önemli GetDataProtectionProvider’ı nesnesimi ApplicationUserManager’den elde ediyoruz.

app.CreatePerOwinContext(() => DependencyResolver.Current.GetService<ApplicationUserManager>()); her bir istekte ApplicationUserManager servisimizi getiriyoruz.

4.Adım Unity Config Sınıfı Oluşturmak

Bu adım bizim için en kritik noktadır. Gerekli alt yapıyı bu sınıf yardımıyla sağlıyoruz.

Config dosyasını resimdeki gibi .loC katmanında oluşturduk. Zaten hali hazırda Unity’i biz PackageManager’den indirdiğimizde bizim için App_Start dosyası açıp içine gerekli iki sınıfı kendisi oluşturacaktır. Default olarak UnityConfig isimli sınıf oluşturacaktır ben onun yerine UnityConfigMvc ismini daha uygun gördüm.

UnityConfigMvc sınıfının kodları ;

using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.Owin.Security;
using SeizeTheDay.Business.Abstract.MySQL;
using SeizeTheDay.Business.Concrete.IdentityManagers;
using SeizeTheDay.Business.Concrete.Manager.MySQL;
using SeizeTheDay.DataAccess.Abstract.MySQL;
using SeizeTheDay.DataAccess.Concrete.MySQL;
using SeizeTheDay.Entities.Identity;
using SeizeTheDay.Entities.Identity.Entities;
using System;
using System.Data.Entity.Core.Objects;
using System.Web;
using System.Web.Mvc;
using Unity;
using Unity.AspNet.Mvc;
using Unity.Injection;
using Unity.Lifetime;
using Xgteamc1XgTeamModel;

namespace SeizeTheDay.IoC
{
    /// <summary>
    /// Specifies the Unity configuration for the main container.
    /// </summary>
    public static class UnityConfigMvc
    {
        private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() =>
        {
            var container = new UnityContainer();
            RegisterTypes(container);
            return container;
        });

        public static IUnityContainer GetConfiguredContainer()
        {
            return container.Value;
        }

        public static void RegisterComponents()
        {
            var container = new UnityContainer();
            RegisterTypes(container);
            DependencyResolver.SetResolver(new UnityDependencyResolver(container));
        }


        private static void RegisterTypes(IUnityContainer container)
        {
			//Asp.Net Identity Dependency Injection 
            container.RegisterType<IdentityContext>(new PerRequestLifetimeManager());
            container.RegisterType<ApplicationSignInManager>(new PerRequestLifetimeManager());
            container.RegisterType<ApplicationRoleManager>(new PerRequestLifetimeManager());
            container.RegisterType<ApplicationUserManager>(new PerRequestLifetimeManager());
            container.RegisterType<EmailService>();

            container.RegisterType<IAuthenticationManager>(
              injectionMembers: new InjectionFactory(c => HttpContext.Current.GetOwinContext().Authentication));

            container.RegisterType<IRoleStore<Roles,string>, RoleStore<Roles,string,IdentityUserRole>>(
              new InjectionConstructor(typeof(IdentityContext)));

            container.RegisterType<IUserStore<Entities.Identity.Entities.User>, UserStore<Entities.Identity.Entities.User>>(
                new InjectionConstructor(typeof(IdentityContext)));


			//Examples for other interfaces and managers
            container.BindInRequstScope<IForumPostService, ForumPostManager>();
            container.BindInRequstScope<IForumPostDal,MyForumPostDal>();

            container.BindInRequstScope<IForumService, ForumManager>();
            container.BindInRequstScope<IForumDal, MyForumDal>();

        }
		 public static void BindInRequstScope<T1, T2>(this IUnityContainer container) where T2 : T1
        {
            container.RegisterType<T1, T2>(new HierarchicalLifetimeManager());
        }

    }
}

Diğer interface’leriniz ve manager sınıflarınız için örnek Dependency Injection oluşturmuş bulunmaktayım, dilerseniz onlarıda incelersiniz.

4.Adım Global.asax Konfigürasyonu

Bu adımda ise Unity’de ki gerekli işlemlerimizi Global.asax’da yani bir diğer deyişle web sayfamız ilk açıldığında oluşturduğumuz injectionlar ayağa kalkacaktır.

using SeizeTheDay.IoC;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;

namespace SeizeTheDay
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            GlobalConfiguration.Configure(WebApiConfig.Register);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

            //Ninject Settings
            //ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory(new BusinessModule(), new AutoMapperModule()));

            //Unity Settings
            UnityConfigMvc.RegisterComponents();
        }
    }
}

UnityConfigMvc.RegisterComponents(); sayesinde servislerimizi ayağa kaldırıyoruz.

5.Adım Startup Konfigürasyonu

Bu adımı 3.adım olarak karıştırmayın, ikisi farklı birer sınıftır. Bu sınıfta namespace’in üzerine bu örnek kodu yerleştirmeyi unutmayınız.

[assembly: OwinStartup(typeof(SeizeTheDay.Startup))]

//SeizeTheDay benim projemin adı, sizde kendi projenizin ismini yazınız

6.Adım Identity Kullanımı

Son adımımız ise Asp.Net Identity manager nesnelerimizi bir controller’da kullanalım.

     #region DefinitionofServices
        private ApplicationSignInManager _signInManager;
        private ApplicationUserManager _userManager;
        private ApplicationRoleManager _roleManager;
        private IAuthenticationManager _authenticationManager;


        public AccountController(ApplicationUserManager userManager,ApplicationSignInManager signInManager , 
     ApplicationRoleManager roleManager, IAuthenticationManager authenticationManager )
        {
            _userManager = userManager;
            _signInManager = signInManager;
            _roleManager = roleManager;
            _authenticationManager = authenticationManager;
        }

Bu şekilde bir yapıyla gerekli manager nesnelerimizi inject ediyoruz. Bir sonraki yazımızda görüşmek üzere. Eğer bir sorunuz olursa lütfen çekinmeden benimle iletişime geçiniz. İyi kodlamalar 🙂

Bu yazı Asp.net MVC, Bilişim, Dependency Injection kategorisine gönderilmiş ve , , , , , ile etiketlenmiş. Kalıcı bağlantıyı yer imlerinize ekleyin.

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Alanı Doldurunuz! *