Как правильно обновить сущность nhibernate из метода действия asp.net POST? - PullRequest
8 голосов
/ 27 апреля 2009

Я новичок в nHibernate и пытаюсь найти способ обновить отдельные объекты из формы POST веб-приложения. (Мы используем ASP.NET MVC)

Объект, который я пытаюсь обновить, содержит (среди прочего) список дочерних объектов, сопоставленный примерно так:

<bag name="PlannedSlices" inverse="true" cascade="all-delete-orphan">
      <key column="JobNumber" />
      <one-to-many class="SliceClass" />
</bag>

Мы упорядочили нашу форму представления редактирования MVC таким образом, чтобы при ее отправке обратно наш метод действия передавался как объект (включая список <> дочерних элементов. Мы правильно проверяем все идентификаторы сущности через форму.

Наша наивная попытка использования метода пост-действия делает session.SaveOrUpdate (parentObject), с parentObject, который был удален из формы представления привязкой модели по умолчанию.

Похоже, что это работает нормально для любого из следующих сценариев:

  • Создание нового родительского объекта
  • Изменение свойств родителя
  • Добавление новых дочерних объектов
  • Изменение существующих дочерних объектов (Глядя на журналы nHibernate, я вижу, что он правильно устанавливает, являются ли объекты новыми или существующими, и выдает соответствующие UPDATE или INSERT)

Неудачный сценарий: - Удаление дочерних объектов - то есть, если они не находятся в IList, они не удаляются из базы данных. Там нет исключений или что-то, они просто не удаляются.

Насколько я понимаю, это связано с тем, что магия, которую выполняет nHibernate для создания списка дочерних элементов, которые требуют удаления, не работает с отдельными экземплярами.

Мне не удалось найти простой пример того, как должен выглядеть этот метод действия с nHibernate (то есть с использованием объекта привязки модели в качестве отдельного экземпляра nHibernate) - примеры, основанные на MS EF (например, http://stephenwalther.com/blog/archive/2009/02/27/chapter-5-understanding-models.aspx) похоже использует метод ApplyPropertyChanges для копирования измененных свойств из привязанного к модели объекта в повторно загруженный экземпляр объекта.

Итак, после всего этого вопрос довольно прост - если у меня есть связыватель модели, дайте мне новый объект, который содержит коллекции дочерних объектов, как мне обновить его через nHibernate (где «обновление» включает, возможно, удаление дети)?

Ответы [ 2 ]

4 голосов
/ 27 апреля 2009

Вот пример, который делает то, что я думаю, вы пытаетесь сделать. Дайте мне знать, если я неправильно понял, что вы пытаетесь сделать.

Имеются следующие "доменные" классы:

public class Person
{
    private IList<Pet> pets;

    protected Person()
    { }

    public Person(string name)
    {
        Name = name;
        pets = new List<Pet>();
    }

    public virtual Guid Id { get; set; }
    public virtual string Name { get; set; }
    public virtual IEnumerable<Pet> Pets
    {
        get { return pets; }
    }

    public virtual void AddPet(Pet pet)
    {
        pets.Add(pet);
    }

    public virtual void RemovePet(Pet pet)
    {
        pets.Remove(pet);
    }
}

public class Pet
{
    protected Pet()
    { }

    public Pet(string name)
    {
        Name = name;
    }

    public virtual Guid Id { get; set; }
    public virtual string Name { get; set; }
}

Со следующим отображением:

   public class PersonMap : ClassMap<Person>
    {
        public PersonMap()
        {
            LazyLoad();
            Id(x => x.Id).GeneratedBy.GuidComb();
            Map(x => x.Name);
            HasMany(x => x.Pets)
                   .Cascade.AllDeleteOrphan()
                   .Access.AsLowerCaseField()
                   .SetAttribute("lazy", "false");
        }
    }

    public class PetMap : ClassMap<Pet>
    {
        public PetMap()
        {
            Id(x => x.Id).GeneratedBy.GuidComb();
            Map(x => x.Name);
        }
    }

Этот тест:

    [Test]
    public void CanDeleteChildren()
    {
        Person person = new Person("joe");

        Pet dog = new Pet("dog");
        Pet cat = new Pet("cat");

        person.AddPet(dog);
        person.AddPet(cat);

        Repository.Save(person);

        UnitOfWork.Commit();

        CreateSession();
        UnitOfWork.BeginTransaction();

        Person retrievedPerson = Repository.Get<Person>(person.Id);
        Repository.Evict(retrievedPerson);

        retrievedPerson.Name = "Evicted";

        Assert.AreEqual(2, retrievedPerson.Pets.Count());
        retrievedPerson.RemovePet(retrievedPerson.Pets.First());

        Assert.AreEqual(1, retrievedPerson.Pets.Count());

        Repository.Save(retrievedPerson);

        UnitOfWork.Commit();

        CreateSession();
        UnitOfWork.BeginTransaction();

        retrievedPerson = Repository.Get<Person>(person.Id);
        Assert.AreEqual(1, retrievedPerson.Pets.Count());
    }

запускается и генерирует следующий sql:

DeletingChildrenOfEvictedObject.CanDeleteChildren: прошло NHibernate: INSERT INTO [Person] (Name, Id) VALUES (@ p0, @ p1); @ p0 = 'joe', @ p1 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'

NHibernate: INSERT INTO [Pet] (Name, Id) VALUES (@ p0, @ p1); @ p0 = 'собака', @ p1 = '464e59c7-74d0-4317-9c22-9bf801013abb'

NHibernate: INSERT INTO [Pet] (Name, Id) VALUES (@ p0, @ p1); @ p0 = 'cat', @ p1 = '010c2fd9-59c4-4e66-94fb-9bf801013abb'

NHibernate: ОБНОВЛЕНИЕ [Pet] SET Person_id = @ p0 WHERE Id = @ p1; @ p0 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2', @ p1 = '464e59c7-74d0-4317-9c22-9bf801013abb'

NHibernate: ОБНОВЛЕНИЕ [Pet] SET Person_id = @ p0 WHERE Id = @ p1; @ p0 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2', @ p1 = '010c2fd9-59c4-4e66-94fb-9bf801013abb'

NHibernate: ВЫБЕРИТЕ person0_.Id как Id5_0_, person0_.Name как Name5_0_ ИЗ [Person] person0_ ГДЕ person0_.Id=@p0; @ p0 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'

NHibernate: ВЫБЕРИТЕ pets0_.Person_id как Person3_1_, pets0_.Id как Id1_, pets0_.Id как Id6_0_, pets0_.Name как Имя6_0_ ИЗ [Pet] pets0_ WHERE pets0_.Person_id=@p0; @ p0 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'

NHibernate: ОБНОВЛЕНИЕ [Персона] SET Name = @ p0 WHERE Id = @ p1; @ p0 = 'Выселенный', @ p1 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'

NHibernate: ОБНОВЛЕНИЕ [Pet] SET Name = @ p0 WHERE Id = @ p1; @ p0 = 'собака', @ p1 = '464e59c7-74d0-4317-9c22-9bf801013abb' NHibernate: ОБНОВЛЕНИЕ [Pet] SET Person_id = null WHERE Person_id = @ p0 AND Id = @ p1; @ p0 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2', @ p1 = '010c2fd9-59c4-4e66-94fb-9bf801013abb'

NHibernate: УДАЛИТЬ [Pet] WHERE Id = @ p0; @ p0 = '010c2fd9-59c4-4e66-94fb-9bf801013abb'

NHibernate: ВЫБЕРИТЕ person0_.Id как Id5_0_, person0_.Name как Name5_0_ ИЗ [Person] person0_ ГДЕ person0_.Id=@p0; @ p0 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'

NHibernate: ВЫБЕРИТЕ pets0_.Person_id как Person3_1_, pets0_.Id как Id1_, pets0_.Id как Id6_0_, pets0_.Name как Имя6_0_ ИЗ [Pet] pets0_ WHERE pets0_.Person_id=@p0; @ p0 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'

Обратите внимание на УДАЛЕНИЕ ОТ [Pet] ...

Итак, что вам нужно сделать, это вручную передать объект Person (в этом примере) с измененными коллекциями, и он должен иметь возможность определять, что удалять. Убедитесь, что у вас установлен атрибут Cascade.AllDeleteOrphan ().

1 голос
/ 28 апреля 2009

Ответ Роба убедил меня более внимательно взглянуть на подход «загрузить существующий элемент в новый сеанс, а затем объединить», и, конечно же, есть ISession.Merge, который, кажется, делает именно то, что я хотел, то есть взять свежий объект и объединить его с предшественником, который только что был перезагружен во второй сеанс.

Так что я думаю, что ответ на вопрос, который я пытался задать, это «перезагрузить существующую сущность и затем вызвать ISession.Merge с новой сущностью».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...