是否有必要复制我的实体类作为视图模型与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

那么,我在这里错过了什么吗?有没有一种方法可以做到这一点,而不必创建两个几乎相同的类?

是否有必要复制我的实体类作为视图模型与NHibernate一起工作

我建议使用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应用程序开发中起着非常重要的作用。在表中,我们以关系方式存储数据,但在应用程序中,我们肯定需要另一组接近视图和业务逻辑的模型,特别是当您的领域很复杂时。我们可以轻松地分割视图模型,我们遍历关系并根据需要构建视图模型。