詳解ASP.NET MVC對(duì)表進(jìn)行通用的增刪改
作者的寫下此文的背景是一些項(xiàng)目本身規(guī)模不大的情況下,也有其他網(wǎng)友擔(dān)心性能方面的問題。在這里我們先看一下ASP.NET MVC對(duì)表是如何進(jìn)行通用的增刪改操作的。
預(yù)備知識(shí):
1、了解反射技術(shù)
2、了解C#3.0中擴(kuò)展方法,分布類,Linq to object,Linq to sql
3、了解ASP.NET MVC
在項(xiàng)目中每添加一個(gè)表往往都要添加一套增刪改代碼,而且這些代碼很多情況下都很相似,這里我們給出一個(gè)通用的解決方案供大家參考。
一、準(zhǔn)備工作:
這里我們先要在數(shù)據(jù)庫中添加兩個(gè)表News和User如下圖:然后拖到dbml中生成實(shí)體類。

這里我們先準(zhǔn)備一個(gè)接口:ICommonTable
- Code
- public interface ICommonTable
- {
- int id { get; set; }
- }
然后讓News和User實(shí)體都繼承于此接口
- Code
- public partial class News : ICommonTable
- {
- }
- public partial class User : ICommonTable
- {
- }
二、通用刪除操作
分別添加NewsList.aspx和UserList.aspx兩個(gè)view,添加方式參見ASP.NET MVC實(shí)踐系列2-簡單應(yīng)用
在這兩個(gè)View中加入刪除鏈接:
- <%= Html.ActionLink("刪除", "Delete", new { key = item.id, partialName="News" })%>
- 和
- <%= Html.ActionLink("刪除", "Delete", new { key = item.id, partialName="User" })%>
然后添加一個(gè)Controller:
- public ActionResult Delete(string partialName, int? key)
- {
- RepositoryBase repositoryBase = new RepositoryBase(partialName);
- repositoryBase.Delete(key ?? 0);
- return RedirectToAction(partialName + "List");//返回到list
- }
接下來我們介紹一下RepositoryBase :
- public class RepositoryBase
- {
- public Type EntityType { get; private set; }
- public RepositoryBase(string entityType)
- {
- Type type = GetBllTypeByName(entityType);
- EntityType = type;
- }
- public ICommonTable CreateNew()
- {
- return (ICommonTable)Activator.CreateInstance(EntityType);
- }
- /// <summary>
- /// 通過字符串獲得其Type
- /// </summary>
- /// <param name="typeName"></param>
- /// <returns></returns>
- private static Type GetBllTypeByName(string typeName)
- {
- Type type = null;
- var ass = AppDomain.CurrentDomain.GetAssemblies()
- .Where(p => p.FullName.Contains("CommonCEDemo"));
- foreach (var a in ass)
- {
- type = a.GetTypes().Where(p => p.Name == typeName).FirstOrDefault();
- if (type != null)
- break;
- }
- if (type == null)
- {
- throw new Exception("類型未定義:" + typeName);
- }
- return type;
- }
- public RepositoryBase(Type entityType)
- {
- EntityType = entityType;
- }
- public ICommonTable Get(int id)
- {
- DBDataContext db = Context.GetContext();
- return db.GetTable(EntityType).Cast<ICommonTable>().FirstOrDefault(p => p.id == id);
- }
- public void Delete(int id)
- {
- ICommonTable bllTable = Get(id);
- Context.GetContext().GetTable(EntityType).DeleteOnSubmit(bllTable);
- Context.GetContext().SubmitChanges();
- }
- }
這里邊重點(diǎn)要理解的就是GetBllTypeByName方法。有了這個(gè)方法我們就可以動(dòng)態(tài)的通過名字獲得相應(yīng)的Type了。這里還有個(gè)問題就是DataContext是從何而來的,我們這里為了簡單起見全程聲明了一個(gè)DataContext沒有考慮多線程的情況
- public class Context
- {
- static DBDataContext context;
- static Context()
- {
- if (context==null)
- {
- context = new DBDataContext();
- }
- }
- public static DBDataContext GetContext()
- {
- return context;
- }
- }
有個(gè)這些當(dāng)我們想要對(duì)一個(gè)表進(jìn)行刪除是只要添加相應(yīng)的鏈接就可以了(如<%= Html.ActionLink("刪除", "Delete", new { key = item.id, partialName="News" })%>)
三、通用增加、修改
首先添加一個(gè)CreateEditView.aspx視圖
- <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
- <%Html.RenderPartial(ViewData["PartialName"].ToString()); %>
- </asp:Content>
然后添加兩個(gè)Partial視圖News.ascx和User.ascx,這兩個(gè)視圖是分別基于News和User類的強(qiáng)類型視圖,具體內(nèi)容參加源碼。
接下來我們添加相應(yīng)的Controller
- public ActionResult CreateEditView(string partialName, int? key)
- {
- ViewData["PartialName"] = partialName;
- RepositoryBase repositoryBase = new RepositoryBase(partialName);
- ICommonTable table;
- if (key == null)
- {
- table = repositoryBase.CreateNew();
- }
- else
- {
- table = repositoryBase.Get(key ?? 0);
- }
- return View("CreateEditView", table);
- }
- [AcceptVerbs(HttpVerbs.Post)]
- public ActionResult CreateEditView(string partialName, int? key, FormCollection formCollection)
- {
- RepositoryBase repositoryBase = new RepositoryBase(partialName);
- ICommonTable bllTable;
- if (key == null)
- {
- bllTable = repositoryBase.CreateNew();
- }
- else
- {
- bllTable = repositoryBase.Get(key ?? 0);
- }
- this.UpdateModel(bllTable, true);
- if (key == null)
- {
- Context.GetContext().GetTable(repositoryBase.EntityType).InsertOnSubmit(bllTable);
- }
- Context.GetContext().SubmitChanges();
- return RedirectToAction(partialName+"List");//返回到list
- }
這里邊大家可能有疑問的就是this.UpdateModel(bllTable, true);這個(gè)方法在mvc框架中并不存在,這是我添加的擴(kuò)展方法,這個(gè)地方如果使用UpdateModel(bllTable)雖然編譯不會(huì)報(bào)錯(cuò),但也沒有更新成功,查了一下mvc的源碼,問題就出在如下源碼中:
- protected internal bool TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties, IDictionary<string, ValueProviderResult> valueProvider) where TModel : class {
- if (model == null) {
- throw new ArgumentNullException("model");
- }
- if (valueProvider == null) {
- throw new ArgumentNullException("valueProvider");
- }
- Predicate<string> propertyFilter = propertyName => BindAttribute.IsPropertyAllowed(propertyName, includeProperties, excludeProperties);
- IModelBinder binder = Binders.GetBinder(typeof(TModel));
- ModelBindingContext bindingContext = new ModelBindingContext() {
- Model = model,
- ModelName = prefix,
- ModelState = ModelState,
- ModelType = typeof(TModel),
- PropertyFilter = propertyFilter,
- ValueProvider = valueProvider
- };
- binder.BindModel(ControllerContext, bindingContext);
- return ModelState.IsValid;
- }
這個(gè)typeof(TModel)造成了只會(huì)更新聲明類型中有的屬性,把它換成model.GetType()就可以解決問題了,我擴(kuò)這的這個(gè)方法如下
- public static class ControllerExtension
- {
- /// <summary>
- /// 更新時(shí)是否按照當(dāng)前類型進(jìn)行更新
- /// </summary>
- /// <typeparam name="TModel"></typeparam>
- /// <param name="controller"></param>
- /// <param name="model"></param>
- /// <param name="isEx"></param>
- public static void UpdateModel<TModel>(this Controller controller, TModel model, bool isExtension) where TModel : class
- {
- if (isExtension)
- {
- Predicate<string> propertyFilter = propertyName => IsPropertyAllowed(propertyName, null, null);
- IModelBinder binder = ModelBinders.Binders.GetBinder(model.GetType());
- ModelBindingContext bindingContext = new ModelBindingContext()
- {
- Model = model,
- ModelName = null,
- ModelState = controller.ModelState,
- ModelType = model.GetType(),
- PropertyFilter = propertyFilter,
- ValueProvider = controller.ValueProvider
- };
- binder.BindModel(controller.ControllerContext, bindingContext);
- }
- else
- {
- throw new Exception("isExtension不能選擇false");
- }
- }
- private static bool IsPropertyAllowed(string propertyName, string[] includeProperties, string[] excludeProperties)
- {
- bool includeProperty = (includeProperties == null) || (includeProperties.Length == 0) || includeProperties.Contains(propertyName, StringComparer.OrdinalIgnoreCase);
- bool excludeProperty = (excludeProperties != null) && excludeProperties.Contains(propertyName, StringComparer.OrdinalIgnoreCase);
- return includeProperty && !excludeProperty;
- }
- }
有了這些,當(dāng)我們想對(duì)新表進(jìn)行編輯和添加時(shí)只需要添加相應(yīng)的Partial編輯視圖就可以了,簡化了我們的編程工作。
四、缺點(diǎn)
1、須要按照規(guī)則命名,比方說Partial視圖需要以相應(yīng)的類名來命名
2、頁面引用是弱類型的
原文標(biāo)題:在ASP.NET MVC中對(duì)表進(jìn)行通用的增刪改
鏈接:http://www.cnblogs.com/nuaalfm/archive/2009/11/11/1600811.html