YbSoftwareFactory目前已可快速生成ASP.NET WebForm、MVC、WinForm和WPF的解決方案源代碼,所生成的源代碼可直接在VS中打開並運行。終端用戶還可自行二次開發自己所需的插件。本章將對ASP.NET MVC代碼生成插件所生成項目的主要特點和技術進行解析,其Demo地址:http://mvcdemo.yellbuy.com/。
主要技術特點:
一、數據字典的排序
有童鞋問到數據字典的排序是如何實現的,其實數據字典的排序比較簡單。首先,需要定義一個排序字段,並根據這個字段升序排列。如果要優先顯示某個字典項,則需減小該排序字段的值。如果該排序字段再首位,則無需操作,第二位,則為第一位的排序值小1000,其他設置為前面兩項的值的平均數。反之亦然~
在所生成的解決方案中,使用了通用的Api組件,你只需配置好主代碼即可。
二、數據分頁和動態查詢
現在很多童鞋使用Linq查詢,確實是非常方便,並且是強類型的,這樣編寫代碼也不容易出錯;但最大的缺點就是靈活性差,例如無法把查詢語法寫到一個統一的基類中。在本解決方案中,設計比較巧妙之處就是把過濾查詢的實現寫到了一個泛型基類中,僅需在具體實現類中定義查詢的方式字符串即可,代碼不僅更加簡潔,而且修改、維護也更加方便。使用動態Linq技術可以在泛型基類中統一以如下的方式進行數據查詢、分頁和排序,而無需在具體類中總是寫重復的代碼:
/// <summary> /// 分頁查詢 /// </summary> /// <param name="filter">查詢條件</param> /// <param name="page">頁號</param> /// <param name="rows">記錄數</param> /// <param name="sort">排序字段名稱</param> /// <param name="order">排序方式</param> /// <returns></returns> public ResponseResult<M> Get(string filter, int page, int rows, string sort, string order) { var table = Table; if (!string.IsNullOrWhiteSpace(filter)) { var str = GetFilterFormat(); //有過濾條件,進行過濾查詢 if (!string.IsNullOrWhiteSpace(str)) table = table.Where(str, filter); } var total = table.LongCount(); table = table.OrderBy(string.Format("{0} {1}", sort, order)).Skip((page - 1) * rows).Take(rows); var items = table.ToList(); var models = InjectFrom(items); return new ResponseResult<M>(total, models); }
如果感興趣,可以在前一章中下載DynamicLinq的實現源碼。
三、授權和身份認證
授權和驗證關系到系統的安全,普通的授權驗證方式無法實現本系統所要求的靈活性以及細粒度的權限控制,因此需對授權方式進行擴展,這主要需從AuthorizeAttribute繼承,本解決方案中新增了一個PermissionKey屬性,更據所設置的PermissionKey名稱進行相應的授權處理,通過添加“EveryOne”角色名甚至能設置匿名用戶的操作權限,不可謂不靈活。主要代碼如下:

1 public class YbApiAuthorizeAttribute : System.Web.Http.AuthorizeAttribute 2 { 3 private const string BasicAuthResponseHeader = "WWW-Authenticate"; 4 private const string BasicAuthResponseHeaderValue = "Basic"; 5 public string PermissionKey { get; set; } 6 public override void OnAuthorization(HttpActionContext actionContext) 7 { 8 if (actionContext == null) 9 throw new ArgumentNullException("actionContext"); 10 if (AuthorizationDisabled(actionContext) || AuthorizeRequest(actionContext.ControllerContext.Request)) 11 return; 12 this.HandleUnauthorizedRequest(actionContext); 13 } 14 15 protected override void HandleUnauthorizedRequest(HttpActionContext actionContext) 16 { 17 if (actionContext == null) 18 throw new ArgumentNullException("actionContext"); 19 actionContext.Response = CreateUnauthorizedResponse(actionContext.ControllerContext.Request); 20 } 21 22 private HttpResponseMessage CreateUnauthorizedResponse(HttpRequestMessage request) 23 { 24 var result = new HttpResponseMessage() 25 { 26 StatusCode = HttpStatusCode.Unauthorized, 27 RequestMessage = request 28 }; 29 //we need to include WWW-Authenticate header in our response, 30 //so our client knows we are using HTTP authentication 31 result.Headers.Add(BasicAuthResponseHeader, BasicAuthResponseHeaderValue); 32 return result; 33 } 34 35 private static bool AuthorizationDisabled(HttpActionContext actionContext) 36 { 37 //support new AllowAnonymousAttribute 38 if (!actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any()) 39 return actionContext.ControllerContext.ControllerDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any(); 40 return true; 41 } 42 43 private bool AuthorizeRequest(HttpRequestMessage request) 44 { 45 //匿名用戶的權限驗證 46 if (!HttpContext.Current.User.Identity.IsAuthenticated) 47 { 48 var permissionsOfEveryOne = Permissions.GetPermissionsInRole("EveryOne"); 49 return CheckPermission(request, permissionsOfEveryOne); 50 } 51 //未設置權限Key,則任何用戶均可訪問 52 if (string.IsNullOrWhiteSpace(PermissionKey)) return true; 53 54 //登錄用戶的權限驗證 55 var allowPermissions = Permissions.GetPermissionsForUser(); 56 return CheckPermission(request, allowPermissions); 57 } 58 59 private bool CheckPermission(HttpRequestMessage request, Permission[] permissionsOfEveryOne) 60 { 61 if (permissionsOfEveryOne == null || permissionsOfEveryOne.Length == 0) 62 return false; 63 var permissionKeys = permissionsOfEveryOne.Select(c => c.PermissionKey); 64 if (request.Method.Method.Equals("GET", StringComparison.OrdinalIgnoreCase)) 65 return permissionKeys.Contains(PermissionKey.ToLower()); 66 if (request.Method.Method.Equals("PUT", StringComparison.OrdinalIgnoreCase)) 67 return permissionKeys.Contains(string.Format("{0}.add", PermissionKey.ToLower())); 68 if (request.Method.Method.Equals("POST", StringComparison.OrdinalIgnoreCase)) 69 return permissionKeys.Contains(string.Format("{0}.edit", PermissionKey.ToLower())); 70 if (request.Method.Method.Equals("DELETE", StringComparison.OrdinalIgnoreCase)) 71 return permissionKeys.Contains(string.Format("{0}.delete", PermissionKey.ToLower())); 72 return false; 73 } 74 75 protected string[] RolesSplit 76 { 77 get { return SplitStrings(Roles); } 78 } 79 80 protected string[] UsersSplit 81 { 82 get { return SplitStrings(Users); } 83 } 84 85 protected static string[] SplitStrings(string input) 86 { 87 if (string.IsNullOrWhiteSpace(input)) return new string[0]; 88 var result = input.Split(',').Where(s => !String.IsNullOrWhiteSpace(s.Trim())); 89 return result.Select(s => s.Trim()).ToArray(); 90 } 91 92 /// <summary> 93 /// Implement to include authentication logic and create IPrincipal 94 /// </summary> 95 protected bool TryCreatePrincipal(string user, string password, out IPrincipal principal) 96 { 97 principal = null; 98 if (!Membership.ValidateUser(user, password)) 99 return false; 100 string[] roles = System.Web.Security.Roles.Provider.GetRolesForUser(user); 101 principal = new GenericPrincipal(new GenericIdentity(user), roles); 102 return true; 103 } 104 }
當然,你也需要在具體的Controller上添加相應的Attribute標記,例如:[YbApiAuthorize(PermissionKey = "Categories")]
四、實體對象的驗證
實體對象的驗證使用Fluent Validator組件進行驗證,YbSoftWareFactory自動生成基本的客戶端和服務器端的驗證代碼(如非空檢查,字符串長度檢查等),所生成的服務器端的驗證代碼示例如下:
public class CustomersValidator : AbstractValidator<Customers> { public CustomersValidator() { //此處添加驗證邏輯 RuleFor(x => x.CustomerID).NotEmpty().WithMessage("必須輸入標識"); RuleFor(x => x.CompanyName).NotEmpty().WithMessage("必須輸入公司名"); RuleFor(x => x.ContactName).Length(30).WithMessage("聯系人最多只能輸入30個字符"); RuleFor(x => x.ContactTitle).Length(30).WithMessage("職務最多只能輸入30個字符"); RuleFor(x => x.Address).Length(60).WithMessage("地址最多只能輸入60個字符"); RuleFor(x => x.City).Length(15).WithMessage("市最多只能輸入15個字符"); RuleFor(x => x.Region).Length(15).WithMessage("省/自治區最多只能輸入15個字符"); RuleFor(x => x.PostalCode).Length(10).WithMessage("郵編最多只能輸入10個字符"); RuleFor(x => x.Country).Length(15).WithMessage("國家最多只能輸入15個字符"); RuleFor(x => x.Phone).Length(24).WithMessage("電話最多只能輸入24個字符"); RuleFor(x => x.Fax).Length(24).WithMessage("傳真最多只能輸入24個字符"); } }
五、日志
為方便調試和錯誤診斷,系統集成long4net日志組件,所有異常信息均輸出至相應的日志文件中。
有童鞋問YbSoftwareFactory怎么使用,好用不?(見過很多人是一根筋,自己把自己搞得太累~)其實使用YbSoftwareFactory生成ASP.NET MVC項目的源代碼超級簡單,僅需三步:
第一步:運行YbSoftwareFactory並連接數據庫
第二步:編輯元數據信息,以便生成的界面更加友好
第三步:點擊“解決方案”按鈕生成源代碼
短暫的等待后,在VS中打開所生成的源碼解決方案,然后就是運行它....
注:本插件已正式發布,需要插件和代碼的請和我聯系。