2014年6月1日 星期日

[ASP.NET Identity 2.0]如何將 ASP.NET Identity 的資料表 PK 欄位的資料型別由 NVARCAHR(128) 改為 INT

ASP.NET Identity 2.0 所提供的資料表中 PK 欄位資料型別皆為 nvarchar(128) ,所儲存的值為 GUID 格式字串。
如果我們想將 PK 的資料型別改為 int 該如何作呢?
以下將一步步實現這個過程。

請建立一個 ASP.NET MVC 5 專案,驗證方式請選擇「個別使用者帳戶」。


  1. 打開並修改 Models\IdentityModels.cs 為以下程式碼
     
    namespace WebApplication1.Models
    {
        // 您可以在 ApplicationUser 類別新增更多屬性,為使用者新增設定檔資料,請造訪 http://go.microsoft.com/fwlink/?LinkID=317594 以深入了解。
        public class ApplicationUser : IdentityUser<int, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>
        {
            public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser, int> manager)
            {
                // 注意 authenticationType 必須符合 CookieAuthenticationOptions.AuthenticationType 中定義的項目
                var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
                // 在這裡新增自訂使用者宣告
                return userIdentity;
            }
        }
        public class ApplicationUserRole : IdentityUserRole<int>
        {
        }
    
        public class ApplicationUserLogin : IdentityUserLogin<int>
        {
        }
    
        public class ApplicationUserClaim : IdentityUserClaim<int>
        {
        }
    
        public class ApplicationRole : IdentityRole<int, ApplicationUserRole>
        {
        }
    
        public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, int, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>
        {
            public ApplicationDbContext()
                : base("DefaultConnection")
            {
            }
    
            public static ApplicationDbContext Create()
            {
                return new ApplicationDbContext();
            }
        }
    }
    
    
  2. 打開並修改 App_Start\IdentityConfig.cs 的 ApplicationUserManager 類別
     
        public class ApplicationUserManager : UserManager<ApplicationUser, int>
        {
            public ApplicationUserManager(IUserStore<ApplicationUser, int> store)
                : base(store)
            {
            }
    
            public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
            {
                var manager = new ApplicationUserManager(new UserStore<ApplicationUser, ApplicationRole, int, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>(context.Get<ApplicationDbContext>()));
                // 設定使用者名稱的驗證邏輯
                manager.UserValidator = new UserValidator<ApplicationUser, int>(manager)
                {
                    AllowOnlyAlphanumericUserNames = false,
                    RequireUniqueEmail = true
                };
                // 設定密碼的驗證邏輯
                manager.PasswordValidator = new PasswordValidator
                {
                    RequiredLength = 6,
                    RequireNonLetterOrDigit = true,
                    RequireDigit = true,
                    RequireLowercase = true,
                    RequireUppercase = true,
                };
                // 註冊雙因素驗證提供者。此應用程式使用手機和電子郵件接收驗證碼以驗證使用者
                // 您可以在這裡寫下自己的提供者和外掛程式。
                manager.RegisterTwoFactorProvider("PhoneCode", new PhoneNumberTokenProvider<ApplicationUser, int>
                {
                    MessageFormat = "Your security code is: {0}"
                });
                manager.RegisterTwoFactorProvider("EmailCode", new EmailTokenProvider<ApplicationUser, int>
                {
                    Subject = "安全碼",
                    BodyFormat = "Your security code is: {0}"
                });
                manager.EmailService = new EmailService();
                manager.SmsService = new SmsService();
                var dataProtectionProvider = options.DataProtectionProvider;
                if (dataProtectionProvider != null)
                {
                    manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser, int>(dataProtectionProvider.Create("ASP.NET Identity"));
                }
                return manager;
            }
        }
    
到這邊大部份重要的類別都改完了,這時建置一下專案後發現還有一些錯誤,讓我們接著來處理

調整 User.Identity.GetUserId() 擴充方法
在 AccountController.cs 中大量使用了 GetUserId() 擴充方法,他是用來從 Identity 中擷取 UserId。














會發生錯誤是因為我們現在的 UserId 定義為 int,而這個擴充方法會回傳 string,因此我們需要自建一個擴充方式來處理這個問題。
建立完成後我們將所有使用到的 GetUserId() 方法,改為 GetUserId<int>()。
記得有使用到自建的擴充方法檔案中要 using WebApplication1.ExtensionMethods 命名空間
 
using System.Security.Principal;
using Microsoft.AspNet.Identity;
namespace WebApplication1.ExtensionMethods
{
    public static class IdentityExtensions
    {
        public static T GetUserId<T>(this IIdentity identity) where T : IConvertible
        {
            return (T)Convert.ChangeType(identity.GetUserId(), typeof(T));
        }
    }
}

再來將 ConfirmEmail 方法的 userId 參數,資料型別由 string 改為 int。

最後我們 Startup 中的 app.UseCookieAuthentication…程式碼換掉
 
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                LoginPath = new PathString("/Account/Login"),
                Provider = new CookieAuthenticationProvider
                {
                    OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser, int>(TimeSpan.FromMinutes(30),
                    regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager),
                    getUserIdCallback: (identity) => identity.GetUserId<int>())
                }
            });


這樣我們就完成了 Table PK 由 nvarchar(128) 改為 int。




參考資料:
http://stackoverflow.com/questions/22547712/asp-net-identity-change-guid-to-int
https://aspnetidentity.codeplex.com/workitem/2186