How to update using a newly created standalone object using NHibernate?

Explanation:

Let's say I have a graph of objects nested at several levels in depth, and each object has a bi-directional connection with each other.

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

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

Now let's say that I want to edit some data for an instance of C 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 extract a permanent instance from the database, separate it, send it to the user interface layer for editing, then attach it again and save it back to the database.

Problem:

This is great for Winform applications, because we use the same entity all along, with the only difference being that it goes from permanent to disabled to permanent again.

The problem is that I am now using a web service and browser, sending JSON data. The object is serialized to a string and de-serialized to a new object. This is no longer a separate entity, but rather a transitional one, which simply has the same identifier as the constant identifier (and updated fields). If I use this object for updating, it will destroy the connection with B and D because they do not exist in this new transition entity.

Question:

My question is how do I serialize individual objects over the Internet for a client, get them back and save them, preserving any relationships that I have not explicitly changed? I know about ISession.SaveOrUpdateCopy and ISession.Merge() (do they seem to do the same?), But it will destroy the relationship anyway if I don't explicitly set it. I could copy the fields from the transition object to the permanent object one by one, but this does not work very well when it comes to relationships, and I would have to manually compare the versions.

+4
source share
1 answer

I solved this problem by using an intermediate class to store the data coming from the web service and then copying their properties to the database object. For example, suppose I have two entities:

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 link to the Album in which it is located, so this is a bi-directional relationship. Then, I create a class PhotoDTO :

Class 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 } 

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

Server data

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

Now the client wants to update the name of the photo. They send the following JSON to the server:

Customer 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 Photo as follows:

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 

Description

This was the best solution I found because:

  • You can choose which properties the client is allowed to change. For example, PhotoDTO does not have the PathToFile property, so the client can never change it.

  • You can also choose whether to update the property or not. For example, if the client did not send through AlbumId , it will be 0. You can check this and do not change Album if the identifier is 0. Similarly, if the user does not send Name , you can not update this property.

  • You do not need to worry about the life cycle of an object, because it will always be retrieved and updated in a single session.

AutoMapper

I recommend using AutoMapper to automatically copy properties from the DTO to the object, especially if your entites have many properties. This eliminates the need to write each property manually and has a large configuration.

0
source

Source: https://habr.com/ru/post/1308319/


All Articles