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

发布于 2024-08-30 07:54:44 字数 1330 浏览 1 评论 0原文

说明:

假设我有一个嵌套了多个级别的对象图,并且每个实体彼此之间具有双向关系。

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

或者换句话说,A 具有 B 的集合,并且 B 具有对 A 的引用,并且 < code>B 有一个 C 的集合,而 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 数据发送。该实体被序列化为字符串,并反序列化为实体。它不再是一个分离的实体,而是一个临时实体,恰好与持久实体(和更新的字段)具有相同的 ID。如果我使用此实体进行更新,它将消除与 BD 的关系,因为它们不存在于这个新的瞬态实体中。

问题:

我的问题是,如何通过网络将分离的实体序列化到客户端、接收它们并保存它们,同时保留我没有明确更改的任何关系?我知道 ISession.SaveOrUpdateCopyISession.Merge() (它们似乎做同样的事情?),但如果我不这样做,这仍然会消除关系明确设置它们。我可以将字段从瞬态实体一一复制到持久实体,但这在关系方面效果不太好,我必须手动处理版本比较。

Explanation:

Let's say I have an object graph that's nested several levels deep and each entity has a bi-directional relationship with each other.

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

Or in other words, A has a collection of B and B has a reference back to A, and B has a collection of C and C has a reference back to B, etc...

Now let's say I want to edit some data for an instance ofC. In Winforms, I would use something like this:

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);
}

In plain English, we grab a persistent instance out of the database, detach it, give it to the UI layer for editing, then re-attach it and save it back to the database.

Problem:

This works fine for Winform applications because we're using the same entity all throughout, the only difference being that it goes from persistent to detached to persistent again.

The problem is that now I'm using a web service and a browser, sending over JSON data. The entity gets serialized into a string, and de-serialized into a new entity. It's no longer a detached entity, but rather a transient one that just happens to have the same ID as the persistent one (and updated fields). If I use this entity to update, it will wipe out the relationship to B and D because they don't exist in this new transient entity.

Question:

My question is, how do I serialize detached entities over the web to a client, receive them back, and save them, while preserving any relationships that I didn't explicitly change? I know about ISession.SaveOrUpdateCopy and ISession.Merge() (they seem to do the same thing?), but this will still wipe out the relationships if I don't explicitly set them. I could copy the fields from the transient entity to the persistent entity one by one, but this doesn't work too well when it comes to relationships and I'd have to handle version comparisons manually.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

单调的奢华 2024-09-06 07:54:44

我通过使用中间类来保存来自 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 对象。在服务器端,我们像这样更新照片

服务器代码

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,则该 ID 将为 0。您可以检查这一点,如果 ID 为 0,则不要更改 Album。同样,如果用户没有发送Name,您可以选择不更新该属性。

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

AutoMapper

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

I solved this problem by using an intermediate class to hold data coming in from the web service, then copying its properties to the database entity. For example, let's say I have two entities like so:

Entity Classes

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 contains a collection of Photo objects, and Photo has a reference back to the Album it's in, so it's a bidirectional relationship. I then create a PhotoDTO class:

DTO Class

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
}

Now let's say I have the following Photo stored in the database:

Server Data

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

The client now wants to update the photo's name. They send over the following JSON to the server:

Client Data

PUT http://server/photos/15

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

The server then deserializes the JSON into a PhotoDTO object. On the server side, we update the Photo like this:

Server Code

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

Explanation

This was the best solution I've found because:

  1. You can choose which properties the client is allowed to modify. For example, PhotoDTO doesn't have a PathToFile property, so the client can never modify it.

  2. You can also choose whether to update a property or not. For example, if the client didn't send over an AlbumId, it will be 0. You can check for that and not change the Album if the ID is 0. Likewise, if the user doesn't send over a Name, you can choose not to update that property.

  3. You don't have to worry about the lifecycle of an entity because it will always be retrieved and updated within the scope of a single session.

AutoMapper

I recommend using AutoMapper to automatically copy the properties from the DTO to the entity, especially if your entites have a lot of properties. It saves you the trouble of having to write every property by hand, and has a lot of configurability.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文