原創(chuàng)|其它|編輯:郝浩|2012-09-28 10:49:51.000|閱讀 1742 次
概述:eXpress Persistent Objects framework與APS .NET MVC應(yīng)用程序集成的最大困難在于這些框架都有自己的方法來創(chuàng)建模型實例。ASP .NET MVC需要模型來提供無參數(shù)構(gòu)造函數(shù)。這個無參數(shù)構(gòu)造函數(shù)一次只能創(chuàng)建一個新的模型實例。XPO考慮到了通過公用構(gòu)造函數(shù)創(chuàng)建的每個對象,并在這個對象相關(guān)的數(shù)據(jù)庫中插入新的記錄。因此,需要阻止ASP .NET MVC引擎創(chuàng)建新的實例和手動解決這項任務(wù)。下面我們將對解決這個問題所使用的兩種可能的方法進行說明。
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
當(dāng)我的視圖執(zhí)行POST操作時,XPO 會在數(shù)據(jù)庫中創(chuàng)建一個新的記錄,而不是更新編輯記錄。這是漏洞嗎?如何解決這個問題?在ASP.NET MVC應(yīng)用程序中使用XPO的最好的方法是什么?
eXpress Persistent Objects framework與APS .NET MVC應(yīng)用程序集成的最大困難在于這些框架都有自己的方法來創(chuàng)建模型實例。ASP .NET MVC需要模型來提供無參數(shù)構(gòu)造函數(shù)。這個無參數(shù)構(gòu)造函數(shù)一次只能創(chuàng)建一個新的模型實例。XPO考慮到了通過公用構(gòu)造函數(shù)創(chuàng)建的每個對象,并在這個對象相關(guān)的數(shù)據(jù)庫中插入新的記錄。因此,需要阻止ASP .NET MVC引擎創(chuàng)建新的實例和手動解決這項任務(wù)。下面我們將對解決這個問題所使用的兩種可能的方法進行說明。
方法1
創(chuàng)建一個繼承DefaultModelBinder(或DevExpressEditorsBinder,使用DevExpress ASP.NET組件時)的自定義模型綁定器。這個綁定器用于POST方法參數(shù)時,可用來創(chuàng)建新的模型。
雖然這很不錯,但事情并不會如此簡單。會話怎么樣?為避免將加載的持久對象與不同會話混合,最好是在控制器類中創(chuàng)建會話,并將它傳給自定義綁定器。控制器實例可作為參數(shù)發(fā)送到ModelBinder.CreateModel中。我們所需要做的事情就是聲明接口可用于獲取會話實例,也可用于識別自定義控制器。
[C#]
public interface IXpoController { Session XpoSession { get; } }
[VB.NET]
Public Interface IXpoController ReadOnly Property XpoSession() As Session End Interface
而且,控制器:
[C#]
public class BaseXpoController : Controller, IXpoController { public BaseXpoController() { XpoSession = CreateSession(); } Session fXpoSession; public Session XpoSession { get { return fXpoSession; } private set { fXpoSession = value; } } protected virtual Session CreateSession() { return XpoHelper.GetNewSession(); } }
[VB.NET]
Public Class BaseXpoController Inherits Controller Implements IXpoController Public Sub New() XpoSession = CreateSession() End Sub Private fXpoSession As Session Public Property XpoSession() As Session Get Return fXpoSession End Get Private Set(ByVal value As Session) fXpoSession = value End Set End Property Protected Overridable Function CreateSession() As Session Return XpoHelper.GetNewSession() End Function End Class
控制器使用“在ASP .NET(網(wǎng)絡(luò))應(yīng)用程序知識庫中如何使用XPO”這一文章中所描述的XpoHelper類來創(chuàng)建會話實例。.
現(xiàn)在可以創(chuàng)建自定義模型綁定器。只需要重寫一種方法:創(chuàng)建模。請查看下列代碼。這個方法可獲得實例,然后從第一個參數(shù)(ControllerContext實例)開始實現(xiàn)IXpoController接口。若這個方法失敗,它可以拋出一個異常。一旦我們擁有會話實例,剩余的事情就是技術(shù)細(xì)節(jié)。使用Session.GetClassInfo方法,從最后參數(shù)(modelType)中獲得元數(shù)據(jù)、主要屬性值,并通過Session.GetObjectByKey方法加載持久對象。若數(shù)據(jù)庫中無相應(yīng)記錄,通過XPClassInfo.CreateNewObject方法創(chuàng)建新的持久對象。
[C#]
public class XpoModelBinder :DevExpressEditorsBinder { protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) { IXpoController xpoController = controllerContext.Controller as IXpoController; if (xpoController == null) throw new InvalidOperationException("The controller does not support IXpoController interface"); XPClassInfo classInfo = xpoController.XpoSession.GetClassInfo(modelType); ValueProviderResult result = bindingContext.ValueProvider.GetValue(classInfo.KeyProperty.Name); return result == null ? classInfo.CreateNewObject(xpoController.XpoSession) : xpoController.XpoSession.GetObjectByKey(classInfo, result.ConvertTo(classInfo.KeyProperty.MemberType)); } }
[VB.NET]
Public Class XpoModelBinder Inherits DevExpressEditorsBinder Protected Overrides Function CreateModel(ByVal controllerContext As ControllerContext, ByVal bindingContext As ModelBindingContext, ByVal modelType As Type) As Object Dim xpoController As IXpoController = TryCast(controllerContext.Controller, IXpoController) If xpoController Is Nothing Then Throw New InvalidOperationException("The controller does not support IXpoController interface") End If Dim classInfo As XPClassInfo = xpoController.XpoSession.GetClassInfo(modelType) Dim result As ValueProviderResult = bindingContext.ValueProvider.GetValue(classInfo.KeyProperty.Name) Return If(result Is Nothing, classInfo.CreateNewObject(xpoController.XpoSession), xpoController.XpoSession.GetObjectByKey(classInfo, result.ConvertTo(classInfo.KeyProperty.MemberType))) End Function End Class
就是這樣了。現(xiàn)在自定義模型綁定器可用于下列應(yīng)用程序中:
[C#]
[AcceptVerbs(HttpVerbs.Post)] public ActionResult Create([ModelBinder(typeof(XpoModelBinder))]T newEntity) { return SaveModel(newEntity); }
[VB.NET]
<AcceptVerbs(HttpVerbs.Post)> _ Public Function Create(<ModelBinder(GetType(XpoModelBinder))> ByVal newEntity As T) As ActionResult Return SaveModel(newEntity) End Function
方法2
上一種方法的劣勢在于并不能妥善處理上述所有狀況。例如,。我們給你提供一個完全不同的方法:不要直接將持久對象與視圖綁定,使用中間ViewModel類。這個例子中,ViewModel只是一個簡單的DTO類。邏輯可在控制器中集中。
這個方法更有利。其中,數(shù)據(jù)可與持久對象實現(xiàn)非耦合,開發(fā)商也可對持久對象和會話實現(xiàn)完全控制,這是因為ViewModels下,ASP .NET MCV引擎可運行。同時,通過對XPO使用LINQ,可減少SQL服務(wù)器加載的信息量,也可明確指定需要加載的屬性。以前,因為使用的是匿名類型的試圖,這個方法只適用于只讀視圖。現(xiàn)在,因ViewModel屬性可通過LINQ查詢結(jié)果值進行填充,所以不需要加載全部的持久類對象。
[C#]
IEnumerable<CustomerViewModel> GetCustomers() { return (from c in XpoSession.Query<Customer>().ToList() select new CustomerViewModel() { ID = c.Oid, Name = c.Name }).ToList(); }
[VB.NET]
Private Function GetCustomers() As IEnumerable(Of CustomerViewModel) Return ( _ From c In XpoSession.Query(Of Customer)().ToList() _ Select New CustomerViewModel() With {.ID = c.Oid, .Name = c.Name}).ToList() End Function
BaseViewModel類非常簡單:
[C#]
> using DevExpress.Xpo; public abstract class BaseViewModel<T> { int id = -1; public int ID { get { return id; } set { id = value; } } public abstract void GetData(T model); }
[VB.NET]
Imports DevExpress.Xpo Public MustInherit Class BaseViewModel(Of T) Private id_Renamed As Integer = -1 Public Property ID() As Integer Get Return id_Renamed End Get Set(ByVal value As Integer) id_Renamed = value End Set End Property Public MustOverride Sub GetData(ByVal model As T) End Class
BaseViewModel被定義為類屬類。通過這種方法,可以聲明將使用類屬參數(shù)的抽象成員。在子孫類中,類屬參數(shù)將替換為在編譯時允許開發(fā)商進入模型屬性的實際類型。下列簡單的ViewModel類繼承自BaseViewModel,并以客戶持久類為依據(jù):
[C#]
public class CustomerViewModel : BaseViewModel<Customer> { public string Name { get; set; } public override void GetData(Customer model) { model.Name = Name; } }
[VB.NET]
Public Class CustomerViewModel Inherits BaseViewModel(Of Customer) Private privateName As String Public Property Name() As String Get Return privateName End Get Set(ByVal value As String) privateName = value End Set End Property Public Overrides Sub GetData(ByVal model As Customer) model.Name = Name End Sub End Class
控制器可使用GetData方法更新持久對象屬性。以下是控制器基類以及類屬類的實現(xiàn)。
[C#]
using System.Web.Mvc; using DevExpress.Xpo; using DevExpress.Xpo.DB.Exceptions; namespace DevExpressMvcApplication.Controllers { public abstract class BaseXpoController<T> :Controller where T:XPObject { UnitOfWork fSession; public BaseXpoController() : base() { fSession = CreateSession(); } protected UnitOfWork XpoSession { get { return fSession; } } protected virtual UnitOfWork CreateSession() { return XpoHelper.GetNewUnitOfWork(); } bool Save(BaseViewModel<T> viewModel, bool delete) { T model = XpoSession.GetObjectByKey<T>(viewModel.ID); if (model == null && !delete) model = (T)XpoSession.GetClassInfo<T>().CreateNewObject(XpoSession); if (!delete) viewModel.GetData(model); else if (model != null) XpoSession.Delete(model); try { XpoSession.CommitChanges(); return true; } catch (LockingException) { return false; } } protected bool Save(BaseViewModel<T> viewModel) { return Save(viewModel, false); } protected bool Delete(BaseViewModel<T> viewModel) { return Save(viewModel, true); } } }
[VB.NET]
Imports System.Web.Mvc Imports DevExpress.Xpo Imports DevExpress.Xpo.DB.Exceptions Namespace DevExpressMvcApplication.Controllers Public MustInherit Class BaseXpoController(Of T As XPObject) Inherits Controller Private fSession As UnitOfWork Public Sub New() MyBase.New() fSession = CreateSession() End Sub Protected ReadOnly Property XpoSession() As UnitOfWork Get Return fSession End Get End Property Protected Overridable Function CreateSession() As UnitOfWork Return XpoHelper.GetNewUnitOfWork() End Function Private Function Save(ByVal viewModel As BaseViewModel(Of T), ByVal delete As Boolean) As Boolean Dim model As T = XpoSession.GetObjectByKey(Of T)(viewModel.ID) If model Is Nothing AndAlso (Not delete) Then model = CType(XpoSession.GetClassInfo(Of T)().CreateNewObject(XpoSession), T) End If If (Not delete) Then viewModel.GetData(model) ElseIf model IsNot Nothing Then XpoSession.Delete(model) End If Try XpoSession.CommitChanges() Return True Catch e1 As LockingException Return False End Try End Function Protected Function Save(ByVal viewModel As BaseViewModel(Of T)) As Boolean Return Save(viewModel, False) End Function Protected Function Delete(ByVal viewModel As BaseViewModel(Of T)) As Boolean Return Save(viewModel, True) End Function End Class End Namespace
這個類可壓縮保存和刪除方法,從而避免代碼的復(fù)制。這些方法可產(chǎn)生子孫類:
[C#]
using System; using System.Linq; using System.Web.Mvc; using DevExpress.Xpo; using DevExpress.Web.Mvc; using System.Collections.Generic; namespace DevExpressMvcApplication.Controllers { public class CustomersController : BaseXpoController<Customer> { public ActionResult Index() { return View(GetCustomers()); } public ActionResult IndexPartial() { return PartialView("IndexPartial", GetCustomers()); } [HttpPost] public ActionResult EditCustomer([ModelBinder(typeof(DevExpressEditorsBinder))] CustomerViewModel customer) { Save(customer); return PartialView("IndexPartial", GetCustomers()); } [HttpPost] public ActionResult DeleteCustomer([ModelBinder(typeof(DevExpressEditorsBinder))] CustomerViewModel customer) { Delete(customer); return PartialView("IndexPartial", GetCustomers()); } IEnumerable<CustomerViewModel> GetCustomers() { return (from c in XpoSession.Query<Customer>().ToList() select new CustomerViewModel() { ID = c.Oid, Name = c.Name }).ToList(); } } }
[VB.NET]
Imports System Imports System.Linq Imports System.Web.Mvc Imports DevExpress.Xpo Imports DevExpress.Web.Mvc Imports System.Collections.Generic Namespace DevExpressMvcApplication.Controllers Public Class CustomersController Inherits BaseXpoController(Of Customer) Public Function Index() As ActionResult Return View(GetCustomers()) End Function Public Function IndexPartial() As ActionResult Return PartialView("IndexPartial", GetCustomers()) End Function <HttpPost> _ Public Function EditCustomer(<ModelBinder(GetType(DevExpressEditorsBinder))> ByVal customer As CustomerViewModel) As ActionResult Save(customer) Return PartialView("IndexPartial", GetCustomers()) End Function <HttpPost> _ Public Function DeleteCustomer(<ModelBinder(GetType(DevExpressEditorsBinder))> ByVal customer As CustomerViewModel) As ActionResult Delete(customer) Return PartialView("IndexPartial", GetCustomers()) End Function Private Function GetCustomers() As IEnumerable(Of CustomerViewModel) Return ( _ From c In XpoSession.Query(Of Customer)().ToList() _ Select New CustomerViewModel() With {.ID = c.Oid, .Name = c.Name}).ToList() End Function End Class End Namespace
保存和刪除方法可返回布爾值來表明操作是否成功。若最終用戶對另一最終用戶已更新的記錄進行更新,則這些方法可返回錯誤返回值,從而允許程序員將上述沖突通知最終用戶。
同時,這個XPO控制器使用UnitOfWork代替會話。但我們不建議在ASP .NET應(yīng)用程序中使用UnitOfWork,這不僅僅是因為沒有必要,同時也會不方便。在ASP.NET CMVC應(yīng)用程序中,沒有必要避免對UnitOfWork的使用。
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請郵件反饋至chenjj@fc6vip.cn
文章轉(zhuǎn)載自:DevExpress中文網(wǎng)