原创|其它|编辑:郝浩|2012-09-28 10:49:51.000|阅读 1690 次
概述: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@pclwef.cn
文章转载自:DevExpress中文网