2024年终活动

DevExpress控件使用交流,DevExpress中国社区Dev联系电话 联系电话:023-68661681

如何在ASP.NET MVC应用程序中使用XPO?

来源:本站原创   发布时间:2012-08-21   浏览:3689次

当我的视图执行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的使用。

本站文章除注明转载外,均为本站原创或翻译
欢迎任何形式的转载,但请务必注明出处,尊重他人劳动成果
转载请注明:文章转载自:DevExpress控件中文网 [https://www.devexpresscn.com/]
本文地址:https://www.devexpresscn.com/post/303.html
扫码咨询
电话咨询
023-68661681
返回
顶部