原創|其它|編輯:郝浩|2012-09-28 10:49:51.000|閱讀 1742 次
概述:eXpress Persistent Objects framework與APS .NET MVC應用程序集成的最大困難在于這些框架都有自己的方法來創建模型實例。ASP .NET MVC需要模型來提供無參數構造函數。這個無參數構造函數一次只能創建一個新的模型實例。XPO考慮到了通過公用構造函數創建的每個對象,并在這個對象相關的數據庫中插入新的記錄。因此,需要阻止ASP .NET MVC引擎創建新的實例和手動解決這項任務。下面我們將對解決這個問題所使用的兩種可能的方法進行說明。
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
當我的視圖執行POST操作時,XPO 會在數據庫中創建一個新的記錄,而不是更新編輯記錄。這是漏洞嗎?如何解決這個問題?在ASP.NET MVC應用程序中使用XPO的最好的方法是什么?
eXpress Persistent Objects framework與APS .NET MVC應用程序集成的最大困難在于這些框架都有自己的方法來創建模型實例。ASP .NET MVC需要模型來提供無參數構造函數。這個無參數構造函數一次只能創建一個新的模型實例。XPO考慮到了通過公用構造函數創建的每個對象,并在這個對象相關的數據庫中插入新的記錄。因此,需要阻止ASP .NET MVC引擎創建新的實例和手動解決這項任務。下面我們將對解決這個問題所使用的兩種可能的方法進行說明。
方法1
創建一個繼承DefaultModelBinder(或DevExpressEditorsBinder,使用DevExpress ASP.NET組件時)的自定義模型綁定器。這個綁定器用于POST方法參數時,可用來創建新的模型。
雖然這很不錯,但事情并不會如此簡單。會話怎么樣?為避免將加載的持久對象與不同會話混合,最好是在控制器類中創建會話,并將它傳給自定義綁定器。控制器實例可作為參數發送到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(網絡)應用程序知識庫中如何使用XPO”這一文章中所描述的XpoHelper類來創建會話實例。.
現在可以創建自定義模型綁定器。只需要重寫一種方法:創建模。請查看下列代碼。這個方法可獲得實例,然后從第一個參數(ControllerContext實例)開始實現IXpoController接口。若這個方法失敗,它可以拋出一個異常。一旦我們擁有會話實例,剩余的事情就是技術細節。使用Session.GetClassInfo方法,從最后參數(modelType)中獲得元數據、主要屬性值,并通過Session.GetObjectByKey方法加載持久對象。若數據庫中無相應記錄,通過XPClassInfo.CreateNewObject方法創建新的持久對象。
[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
就是這樣了。現在自定義模型綁定器可用于下列應用程序中:
[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類。邏輯可在控制器中集中。
這個方法更有利。其中,數據可與持久對象實現非耦合,開發商也可對持久對象和會話實現完全控制,這是因為ViewModels下,ASP .NET MCV引擎可運行。同時,通過對XPO使用LINQ,可減少SQL服務器加載的信息量,也可明確指定需要加載的屬性。以前,因為使用的是匿名類型的試圖,這個方法只適用于只讀視圖。現在,因ViewModel屬性可通過LINQ查詢結果值進行填充,所以不需要加載全部的持久類對象。
[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被定義為類屬類。通過這種方法,可以聲明將使用類屬參數的抽象成員。在子孫類中,類屬參數將替換為在編譯時允許開發商進入模型屬性的實際類型。下列簡單的ViewModel類繼承自BaseViewModel,并以客戶持久類為依據:
[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方法更新持久對象屬性。以下是控制器基類以及類屬類的實現。
[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
這個類可壓縮保存和刪除方法,從而避免代碼的復制。這些方法可產生子孫類:
[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應用程序中使用UnitOfWork,這不僅僅是因為沒有必要,同時也會不方便。在ASP.NET CMVC應用程序中,沒有必要避免對UnitOfWork的使用。
本站文章除注明轉載外,均為本站原創或翻譯。歡迎任何形式的轉載,但請務必注明出處、不得修改原文相關鏈接,如果存在內容上的異議請郵件反饋至chenjj@fc6vip.cn
文章轉載自:DevExpress中文網