是否有必要复制我的实体类作为视图模型与NHibernate一起工作
本文关键字:模型 视图 NHibernate 工作 一起 复制 实体 我的 是否 | 更新日期: 2023-09-27 18:13:59
我很难在MVC 5中使用NHibernate。问题是NHibernate需要我的类的Id
属性是私有的或受保护的,但是MVC模型绑定器不能在现有实体上设置Id,所以NHibernate将其视为新记录,并插入它而不是编辑它。
public interface IEntity
{
int Id {get; }
}
public class User : IEntity
{
public virtual int Id {get; protected set;}
public virtual string Email {get; set;}
public virtual DateTime? LastLogin {get; set;}
}
运行正常
public JsonResult SaveUser(User user)
{
var userModel = new UserModel();
if(userModel.SaveUser(user)) return Json(new {success = true});
return Json(new {success = false});
}
失败了,因为Id
有一个保护集,这对于NHibernate实体是必需的。因此,由于MVC模型绑定器不能设置Id
, NHibernate将其视为一个新实体。
public JsonResult EditUser(User user)
{
var userModel = new UserModel();
userModel.EditUser(user);
}
所以看起来我基本上需要复制我的实体类作为视图模型,但这似乎真的很乏味(更不用说反dry)。这两个类之间的唯一区别是视图模型类将具有一个可公开设置的Id
。我可以通过使用AutoMapper让这个过程不那么乏味,但是看起来我仍然需要将类复制为视图模型。它确实具有从dynamic
映射的功能,但这仍然不允许我为现有实体正确设置Id
。
那么,我在这里错过了什么吗?有没有一种方法可以做到这一点,而不必创建两个几乎相同的类?
我建议使用MVC特定的视图模型,因为NHibernate模型通常实现动态代理,不能序列化为XML/JSON等。它们可用于在视图中填充数据,但如果某些属性延迟加载数据,则会导致问题。
视图模型可以接近数据模型类,但通常它们更具体于需要在屏幕上显示的内容。
NHibernate不需要Id是私有的或受保护的,你从哪里得到这个想法?假设它是一个自动递增的标识,将其设置为私有是一个很好的做法,但这不是必需的。使用视图模型是可选的,但是从开发和应用程序安全的角度来看,这是一个非常好的实践。例如,使用视图模型可以避免大规模分配攻击:http://odetocode.com/blogs/scott/archive/2012/03/12/complete-guide-to-mass-assignment-in-asp-net-mvc.aspx
除非你在模型或视图模型中做一些奇特的事情,或者使用延迟加载,否则你不一定需要创建视图模型。我总是使用NHibernate
模型类,如果我需要一些特殊的东西,那不是确切的模型,然后我创建单独的视图模型。例如,Login
是用视图模型执行的,因为密码有一些晶体学和安全选项。除此之外,我使用默认ASP的帮助。Net MVC模型绑定器和我的编辑函数通常是这样的-
view -
@model ConsoleUser
<h2>Create</h2>
@Html.HiddenFor(x=>Model.Id)
......
@Html.EditorFor(x=>Model.UserName)
模型-
public class ConsoleUser : .... // some base IDomainEntity inheritence
{
#region Account
//account information
public virtual String UserName { get; set; }
public virtual String Password { get; set; }
public virtual DateTime? LastLogin { get; set; }
#endregion
}
GET -
/// <summary>
/// Edits this instance.
/// </summary>
/// <returns></returns>
[ValidateInput(false)]
public virtual async Task<ActionResult> Edit(string id)
{
var item = (await _domainService.Get(x => x.Id == id)).FirstOrDefault();
if (item.IsNotNull())
return View(item);
throw new CodedException("E.102.3");
}
POST for update -
/// <summary>
///
/// </summary>
/// <param name="Id">The id.</param>
/// <param name="form">The form.</param>
/// <returns></returns>
[HttpPost, ValidateInput(false)]
public virtual async Task<ActionResult> Edit(string Id, FormCollection form)
{
var model = (await _domainService.Get(x=>x.Id == Id)).FirstOrDefault(); //load the entity from db to update, unless we do this, Nhibernate will consider a new entity
if (TryUpdateModel(model))
{
await _domainService.Save(model);
return RedirectToAction("Details", new { id = Id, ....});
}
return View(model);
}
请注意,在应用TryUpdateModel(model)
之前,我从存储库_domainService.Get(x=>x.Id == Id)).FirstOrDefault()
获取了实体,这是非常重要的。因为这是Nhibernate知道我试图更新它,而不是创建一个新的。create方法看起来是这样的,注意其中的区别-
[HttpPost, ValidateInput(false)]
public virtual async Task<ActionResult> Create(FormCollection form)
{
var model = ModelFactory.Instance.Get<T>(); //.... instead of getting from repository I am creating a new model
if(TryUpdateModel(model))
{
await _domainService.Save(model);
return View("Details", model);
}
return View(model);
}
在save方法中是这样的-
public static void Save<T>(T entity, ISession _session = null, IList<Exception> errors = null) where T : IDomainEntity
{
if (_session == null)
{
using (var session = OpenEngineSession())
using (var trans = session.BeginTransaction())
{
try
{
session.SaveOrUpdate(entity);
trans.Commit();
}
catch (Exception e)
{
errors = errors ?? new List<Exception>();
errors.Add(e);
trans.Rollback();
}
}
}else
{
_session.SaveOrUpdate(entity);
}
}
就这些。NHibernate
有一个内置的功能,从某种意义上说,如果你通过nhibernate使用get对象,它将保留一个引用,并将自动运行更新查询而不是插入。
对我来说,ViewModels在MVC应用程序开发中起着非常重要的作用。在表中,我们以关系方式存储数据,但在应用程序中,我们肯定需要另一组接近视图和业务逻辑的模型,特别是当您的领域很复杂时。我们可以轻松地分割视图模型,我们遍历关系并根据需要构建视图模型。