Как мне обновить только что созданную отдельную сущность с помощью NHibernate? - PullRequest
4 голосов
/ 29 апреля 2010

Пояснение:

Скажем, у меня есть объектный граф, который вложен в несколько уровней, и каждая сущность имеет двунаправленную связь друг с другом.

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

Или, другими словами, A имеет коллекцию B, а B имеет ссылку обратно на A, а 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);
}

Говоря простым языком, мы извлекаем постоянный экземпляр из базы данных, отсоединяем его, передаем его на уровень пользовательского интерфейса для редактирования, затем повторно присоединяем и сохраняем обратно в базу данных.

Проблема:

Это прекрасно работает для приложений Winform, потому что мы все время используем одну и ту же сущность, с той лишь разницей, что она переходит от постоянной к отключенной к постоянной.

Проблема в том, что теперь я использую веб-сервис и браузер, отправляя данные JSON. Объект сериализуется в строку и десериализуется в новый объект. Это уже не отдельная сущность, а скорее временная сущность, у которой просто тот же ID, что и у постоянной (и обновленных полей). Если я использую эту сущность для обновления, она уничтожит отношения с B и D, потому что они не существуют в этой новой переходной сущности.

Вопрос:

У меня вопрос: как я могу сериализовать отдельные объекты через Интернет для клиента, получить их обратно и сохранить, сохраняя при этом любые отношения, которые я не изменил явно? Я знаю о ISession.SaveOrUpdateCopy и ISession.Merge() (они, кажется, делают то же самое?), Но это все равно уничтожит отношения, если я не установлю их явно. Я мог бы копировать поля из временного объекта в постоянный объект по одному, но это не слишком хорошо работает, когда дело касается отношений, и мне придется обрабатывать сравнения версий вручную.

1 Ответ

0 голосов
/ 28 января 2011

Я решил эту проблему, используя промежуточный класс для хранения данных, поступающих из веб-службы, а затем копируя их свойства в объект базы данных. Например, скажем, у меня есть две сущности, такие как:

Классы сущностей

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. Вы можете проверить это и не изменять Album, если идентификатор равен 0. Аналогично, если пользователь не отправляет через Name, вы можете не обновлять это свойство.

  3. Вам не нужно беспокоиться о жизненном цикле объекта, поскольку он всегда будет извлекаться и обновляться в рамках одного сеанса.

AutoMapper

Я рекомендую использовать AutoMapper для автоматического копирования свойств из DTO в объект, особенно если у ваших объектов много свойств. Это избавляет вас от необходимости писать каждое свойство вручную и имеет множество настроек.

...