前言
上篇的菜鳥去重復之Sql的問題還沒有得到滿意的答案。如果哪位大哥有相關的資料解釋,能夠分享給我,那就太謝謝了。
以后每發表一篇博文我都會將以前遺留的問題在前言里指出,直到解決為止。
本文主要在於探討一下Asp.net Mvc4默認生成的權限的詳細內容。
介紹
在VS2012中創建一個默認的帶有權限的MVC4 Internet項目,如下圖。

生成項目后點運行,在瀏覽器里點登陸。然后觀察項目,此刻生成了數據庫,如下。

本文就是針對這樣的模版項目里的已有權限全面的分析,希望大家能夠從中學到一些東西,如果有問題,請指出。PS:歡迎大家共同討論進步。
有趣的Attribute

上圖中有三個ActionFilter,不清楚ActionFilter的童鞋請點擊這里。
AuthorizeAttribute:表示一個特性,該特性用於限制調用方對操作方法的訪問。
AllowAnonymousAttribute:表示一個特性,該特性用於標記在授權期間要跳過 AuthorizeAttribute 的控制器和操作。
InitializeSimpleMembershipAttribute:這個特性是來初始化數據庫成員關系的,后面會講到。
Authorize
這個Attribute是用來對用戶角色權限限制的,大家應該都清楚,此處就不多說了。
AllowAnonymous
這個Attribute似乎是MVC4才加進去的。大致意思看一下上面的解釋應該也了解了。其實就是微軟團隊在Authorize的實現里面加了對AllowAnonymous的判斷,如果方法上有這個Attribute就不限制。
真相在這里(反編譯的),這個是Authorize的OnAuthorization方法,有興趣的童鞋可以看看12行。
1 public virtual void OnAuthorization(AuthorizationContext filterContext) 2 { 3 if (filterContext == null) 4 { 5 throw new ArgumentNullException("filterContext"); 6 } 7 if (OutputCacheAttribute.IsChildActionCacheActive(filterContext)) 8 { 9 throw new InvalidOperationException(MvcResources.AuthorizeAttribute_CannotUseWithinChildActionCache); 10 } 11 bool inherit = true; 12 if (!filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit) && !filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), true)) 13 { 14 if (this.AuthorizeCore(filterContext.HttpContext)) 15 { 16 HttpCachePolicyBase cache = filterContext.HttpContext.Response.Cache; 17 cache.SetProxyMaxAge(new TimeSpan(0L)); 18 cache.AddValidationCallback(new HttpCacheValidateHandler(this.CacheValidateHandler), null); 19 } 20 else 21 { 22 this.HandleUnauthorizedRequest(filterContext); 23 } 24 } 25 }
下面就到了主要的關於默認權限的關鍵Attribute。
InitializeSimpleMembership
我們先來看下它的實現
1 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] 2 public sealed class InitializeSimpleMembershipAttribute : ActionFilterAttribute 3 { 4 private static SimpleMembershipInitializer _initializer; 5 private static object _initializerLock = new object(); 6 private static bool _isInitialized; 7 8 public override void OnActionExecuting(ActionExecutingContext filterContext) 9 { 10 // Ensure ASP.NET Simple Membership is initialized only once per app start 11 LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock); 12 } 13 14 private class SimpleMembershipInitializer 15 { 16 public SimpleMembershipInitializer() 17 { 18 Database.SetInitializer<UsersContext>(null); 19 20 try 21 { 22 using (var context = new UsersContext()) 23 { 24 if (!context.Database.Exists()) 25 { 26 // Create the SimpleMembership database without Entity Framework migration schema 27 ((IObjectContextAdapter)context).ObjectContext.CreateDatabase(); 28 } 29 } 30 WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true); 31 } 32 catch (Exception ex) 33 { 34 throw new InvalidOperationException("The ASP.NET Simple Membership database could not be initialized. For more information, please see http://go.microsoft.com/fwlink/?LinkId=256588", ex); 35 } 36 } 37 } 38 }
這個Atrribute里面實現了創建我們起初見到的數據庫。
現在讓我們來看看具體是如何實現的。這個創建數據庫的方式還是和以前使用Code First有點出入的。
4-12行 主要是定義了幾個變量並重寫OnActionExcuting,實現了確保Asp.net成員關系只初始化一次。我們可以料到,必然在LazyInitializer.EnsureInitialized函數里面實例化了SimpleMembershipInitializer。
只初始化一次就意味着不會調用SimpleMembershipInitializer的構造函數兩次。其實看代碼我們應該也大致能猜到,這個構造函數里面主要做的就是創建數據庫的功能,當然最好只實現一次。
22-29行 主要就是調用方法創建UsersContext的數據庫,相關代碼如下
public class UsersContext : DbContext { public UsersContext() : base("DefaultConnection") { } public DbSet<UserProfile> UserProfiles { get; set; } } [Table("UserProfile")] public class UserProfile { [Key] [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)] public int UserId { get; set; } public string UserName { get; set; } }
我們從上面可以了解到UsersContext繼承DbContext,並以DefaultConnection字符串為連接字符串,有一張表UserProfile。
那這時我們就會有疑問了,在文章開頭介紹里的那幾個標出來的表是如何生成的呢。先別急,我們繼續往下看
WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true);
這句話的字面意思一看就猜到了。初始化數據庫連接使用連接字符串Default,表UserProfile,字段UserId,UserName,自動創建表。
可是我們之前已經使用UserContext的CreateDatabase()創建過數據庫和表了。難道再重新創建一次!!?好吧!還是讓我們看看MSDN的解釋吧!
InitializeDatabaseConnection()
通過連接到包含用戶信息的數據庫來初始化成員資格系統。
注釋
在應用程序啟動時調用此方法(在 _AppStart.cshtml 或 _AppStart.vbhtml 文件中),以初始化簡單成員資格系統。此方法驗證成員資格數據庫是否存在。它還打開到用戶配置文件表的連接,並在成員資格數據和用戶配置文件數據之間建立 數據庫關系。
如果希望使用包含用戶配置文件信息(用戶名、電子郵件地址等)的數據庫表,則應指定成員資格系統用於連接到那些信息的連接字符串和表名稱。如果不希望使用現有用戶配置文件表,則可指定 InitializeDatabaseConnection() 方法應自動創建用戶配置文件表。(用戶配置文件表的數據庫必須已經存在。)
下面是方法的五個參數解釋:
connectionStringName
包含用戶信息的數據庫的連接字符串的名稱。如果使用的是 SQL Server Compact,此名稱可以是一個不帶 .sdf 文件擴展名的數據庫文件(.sdf 文件)的名稱。
userTableName
包含用戶配置文件信息的數據庫表的名稱。
userIdColumn
包含用戶 ID 的數據庫列的名稱。此列必須以整數 (int) 形式鍵入。
userNameColumn
包含用戶名的數據庫列的名稱。此列用於匹配用戶配置文件數據與成員資格帳戶數據。
autoCreateTables
若為 true,則指示應創建用戶配置文件表和成員資格表(如果它們不存在)。若為 false,則指示不應自動創建這些表。雖然可以自動創建成員資格表,但數據庫本身必須已經存在。
我的理解
這個方法會對已有的數據庫進行分析,如果沒有提前創建數據庫,調用此方法會報錯。
如果已經創建一個空的數據庫並且不存在參數對應的userTableName表,設置autoCreateTables為true,會自動創建該參數名的表,字段與userIdColumn及userNameColumn一一對應,並創建其余的四個成員資格表。
如果設置autoCreateTables為false,不會自動創建任何表。
設置autoCreateTables為true,如果已經使用UserContext創建user表,要把表名與UserId、UserName的字段名作為參數賦給InitializeDatabaseConnection()方法作為參數,表名不一致,數據庫會多創建一個User表,並以方法參數名的那個來使用webSecurity的方法。字段名不一致,使用webSecurity的方法時會報錯。
至此,我們已經大致知道起初見到的那幾個數據表是如何生成的了。對,就是webSecurity.InitializeDatabaseConnection方法里自動實現的。
小結
本來打算把默認生成權限的這部分內容盡可能的在一篇里面說完,但寫本篇博客的過程中發現需要寫的有點多。
奈何我本是菜鳥,內容稍微多了點,就越發感覺腦子有點亂,所以打算分成兩篇來寫。
呵呵!目前還沒有實力寫長篇,那得需要思維足夠清晰,我還需要鍛煉啊!
下篇我打算寫一下WebSecurity,SimpleMemberShip,RoleProvider之間的關系,以及它們的使用場合Asp.net Mvc4默認權限詳細(下),希望有興趣的童鞋繼續關注!
最后,如果本文有什么問題,還請指出。如果對您有幫助,請幫忙推薦給我更多動力!
