如何使用NHibernate更新新创建的分离实体?

时间:2010-04-29 05:22:18

标签: web-services nhibernate serialization session-state entity-relationship

说明:

假设我有一个对象图,它嵌套了几层深度,每个实体之间都有双向关系。

A -> B -> C -> D -> E

或者换句话说,A的集合为BB的引用返回A,而B的集合为C {1}}和C引用回B等等......

现在假设我要为C的实例编辑一些数据。在Winforms中,我会使用这样的东西:

var instanceOfC;

using (var session = SessionFactory.OpenSession())
{
    // get the instance of C with Id = 3
    instanceOfC = session.Linq<C>().Where(x => x.Id == 3);
}

SendToUIAndLetUserUpdateData(instanceOfC);

using (var session = SessionFactory.OpenSession())
{
    // re-attach the detached entity and update it
    session.Update(instanceOfC);
}

简单地说,我们从数据库中获取一个持久化实例,将其分离,将其提供给UI层进行编辑,然后重新附加并将其保存回数据库。

问题:

这适用于Winform应用程序,因为我们在整个过程中使用相同的实体,唯一的区别是它从持久性变为分离到再次持久化。

问题是现在我正在使用Web服务和浏览器,通过JSON数据发送。该实体被序列化为一个字符串,并反序列化为 new 实体。它不再是一个分离的实体,而是一个瞬态实体,它恰好具有与持久实体(和更新的字段)相同的ID。如果我使用此实体进行更新,它将消除与BD的关系,因为它们不存在于此新的瞬态实体中。

问题:

我的问题是,如何通过Web将分离的实体序列化到客户端,接收它们并保存它们,同时保留我没有明确更改的任何关系?我知道ISession.SaveOrUpdateCopyISession.Merge()(他们似乎做了同样的事情?),但如果我没有明确设置它们,这仍将消除关系。我可以将瞬态实体中的字段逐个复制到持久性实体,但是当涉及到关系时,这不能很好地工作,我必须手动处理版本比较。

1 个答案:

答案 0 :(得分:0)

我通过使用中间类来保存来自Web服务的数据,然后将其属性复制到数据库实体,从而解决了这个问题。例如,让我们说我有两个这样的实体:

实体类

public class Album
{
    public virtual int Id { get; set; }
    public virtual ICollection Photos { get; set; }
}

public class Photo
{
    public virtual int Id { get; set; }
    public virtual Album Album { get; set; }
    public virtual string Name { get; set; }
    public virtual string PathToFile { get; set; }
}

Album包含Photo个对象的集合,Photo有一个回复Album的对象,因此它是双向的关系。然后我创建了一个PhotoDTO类:

DTO课程

public class PhotoDTO
{
    public virtual int Id { get; set; }
    public virtual int AlbumId { get; set; }
    public virtual string Name { get; set; }
    // note that the DTO does not have a PathToFile property
}

现在让我们说我在数据库中存储了以下Photo

服务器数据

new Photo
{
    Id = 15,
    Name = "Fluffy Kittens",
    Album = Session.Load<Album>(3)
};

客户现在想要更新照片的名称。他们将以下JSON发送到服务器:

客户数据

PUT http://server/photos/15

{
    "id": 15,
    "albumid": 3,
    "name": "Angry Kittens"
}

然后,服务器将JSON反序列化为PhotoDTO对象。在服务器端,我们更新Photo,如下所示:

服务器代码

var photoDTO = DeserializeJson();
var photoDB = Session.Load(photoDTO.Id); // or use the ID in the URL

// copy the properties from photoDTO to photoDB
photoDB.Name = photoDTO.Name;
photoDB.Album = Session.Load<Album>(photoDTO.AlbumId);

Session.Flush(); // save the changes to the DB

说明

这是我发现的最佳解决方案,因为:

  1. 您可以选择允许客户端修改哪些属性。例如,PhotoDTO没有PathToFile属性,因此客户端永远无法对其进行修改。

  2. 您还可以选择是否更新房产。例如,如果客户端没有通过AlbumId发送,则它将为0.如果ID为0,您可以检查并且不更改Album。同样,如果用户不会通过Name发送,您可以选择不更新该属性。

  3. 您不必担心实体的生命周期,因为它将始终在单个会话的范围内检索和更新。

  4. AutoMapper

    我建议使用AutoMapper自动将属性从DTO复制到实体,特别是如果你的entites有很多属性。它为您省去了手动编写每个属性的麻烦,并且具有很多可配置性。