2009-04-27 6 views
8

저는 nHibernate를 처음 사용하고 웹 애플리케이션 양식 POST에서 분리 된 객체를 업데이트하는 적절한 방법을 고민하고 있습니다. ,asp.net POST 작업 메서드에서 nhibernate 엔터티를 업데이트하는 적절한 방법은 무엇입니까?

나는 자식 개체의 IList의이 (무엇보다도)이 포함되어 업데이트하기 위해 노력하고있어 개체를 (우리는 ASP.NET MVC를 사용하는)이 같은 매핑 : 우리는 정렬 한

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

우리 MVC 편집보기 양식을 게시 할 때 우리의 작업 메서드는 자식 항목의 List <>을 포함하여 전달됩니다. 우리는 양식을 통해 모든 엔티티 ID를 올바르게 왕복합니다.

사후 조치 메소드는 session.SaveOrUpdate (parentObject)를 수행하며 parentObject는 뷰 모드에서 기본 모드로 스크랩되었습니다. 라 바인더.

이 다음 시나리오 중 하나에 대해 잘 작동하는 것 같다 : 추가

  • 부모의 속성 수정 새 자식을 새로운 부모 개체
  • 만들기

    • (수정 기존 아이가 객체
    • 객체 nHibernate 로그를 보면 개체가 새 것이거나 기존인지를 올바르게 확인하고 적절한 UPDATE 또는 INSERT를 발행 할 수 있습니다.

    실패한 시나리오는 다음과 같습니다. - 자식 개체 삭제 - IList에 없으면 데이터베이스에서 삭제되지 않습니다. 예외 나 예외는 없으며 삭제되지 않습니다.

    내 이해는 nHibernate가 삭제가 필요한 자식 목록을 만들기 위해 수행하는 마법이 분리 된 인스턴스에서 작동하지 않기 때문입니다.

    MS EF (예 : http://stephenwalther.com/blog/archive/2009/02/27/chapter-5-understanding-models.aspx)를 기반으로 한 예제는 nHibernate와 같은 종류의 작업 메서드 (즉, 모델 바인더 개체를 분리 된 nHibernate 인스턴스로 사용)의 간단한 예제를 찾을 수 없었습니다. 'ApplyPropertyChanges'메서드를 사용하여 변경된 속성을 모델 바인딩 된 개체에서 다시로드 된 엔터티 인스턴스로 복사하는 것처럼 보입니다.

    그럼, 질문은 꽤 간단합니다. 모델 바인더에 자식 개체 컬렉션이 포함 된 새 개체를 주면 nHibernate를 통해이를 업데이트해야합니다 (여기서 'update'는 삭제 가능성을 포함합니다). 아이들의)?

  • 답변

    4

    다음은 내가하려는 일을 보여주는 예입니다. 당신이하려는 일을 오해 한 적이 있다면 알려주세요.

    다음 "도메인"클래스를 감안할 때 :

    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 : Passed NHibernate : INSERT INTO [Person] (Name, Id) VALUES (@ p0, @ p1); @ p0 = 'joe', @ p1 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'

    NHibernate : INSERT INTO [이름] ID VALUES (@ p0, @ p1); @ p0 = '개', @ p1 = '464e59c7-74d0-4317-9c22-9bf801013abb'

    NHibernate : INSERT INTO [애완 동물] (이름, ID) VALUES (@ p0, @ p1); @ p0 = 'cat', @ p1 = '010c2fd9-59c4-4e66-94fb-9bf801013abb'

    NHibernate : UPDATE [애완 동물] SET Person_id = @ p0 WHERE Id = @ p1; @ P0 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'@ P1 = '464e59c7-74d0-4317-9c22-9bf801013abb'

    NHibernate에 : UPDATE [동물] SET person_id로 P0 = @ WHERE 식 @ P1; @ P0 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'@ P1 = '010c2fd9-59c4-4e66-94fb-9bf801013abb'

    NHibernate에 [이용시 FROM Name5_0_ 같은 Id5_0_, person0_.Name로 person0_.Id를 선택 ] person0_ 어디서 [email protected]; @ P0 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'

    NHibernate에 : Person3_1_, Id1_ 같은 pets0_.Id, Id6_0_ 같은 pets0_.Id, pets0_.Name Name6_0_ 같은 FROM [동물] pets0_로 pets0_.Person_id를 선택 [email protected]; @ p0 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'

    NHibernate : UPDATE [Person] SET Name = @ p0 WHERE Id = @ p1; @ p0 = 'Evicted', @ p1 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'

    NHibernate : UPDATE [애완 동물] SET 이름 = @ p0 WHERE Id = @ p1; @ p0 = 'dog', @ p1 = '464e59c7-74d0-4317-9c22-9bf801013abb' NHibernate : UPDATE [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에 [동물]에서 삭제 WHERE 식 @ P0; @ P0 = '010c2fd9-59c4-4e66-94fb-9bf801013abb'

    NHibernate에 : Id5_0_, FROM 등 Name5_0_ person0_.Name [PERSON] person0_ [email protected]로 person0_.Id을 선택; @ P0 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'

    NHibernate에 : Person3_1_, Id1_ 같은 pets0_.Id, Id6_0_ 같은 pets0_.Id, pets0_.Name Name6_0_ 같은 FROM [동물] pets0_로 pets0_.Person_id를 선택 [email protected]; 당신이 할 수 있도록 필요한 @ P0 = 'cd123fc8-6163-42a5-aeeb-9bf801013ab2'는이 [애완 동물]에서 삭제

    주 ... 그래서

    는 손 NHibernate에 Person 객체 (에 이 예제)를 수정 된 콜렉션과 비교하여 삭제할 대상을 결정할 수 있어야합니다. Cascade.AllDeleteOrphan() 속성이 설정되어 있는지 확인하십시오.

    +0

    대단히 감사합니다. 많은 작업이있었습니다! 불행히도, 내가 고민하고있는 비트는 두 번째 세션/트랜잭션 (즉, 내 POST)에있는 'Person'객체가 ModelBinder에 의해 생성 된 완전히 새로운 객체이며, 수정 된 필드가 거의없고 '자식 삭제'호출이 몇 개있었습니다. 내가 찾고있는 것은 새로운 객체를 가져 와서 변경된 객체를 검색하여 nh가 필요한 SQL을 처리 할 수있는 방법이라고 생각합니다. 어쩌면 그것은 단지 존재하지 않을 수도 있습니다. –

    +1

    그 상황을 처리하는 경향은 모델 바인더로 만든 새 개체를 프레젠테이션 모델 개체로 처리하는 것입니다. 업데이트하고 해당 객체에 업데이트를 적용하려는 객체를 검색해야합니다 (또는 어떻게 든 "지속 된"클래스의 인스턴스를 생성해야합니다). 그런 다음 해당 객체를 Hibernate에 저장할 수 있습니다. 말이 돼? –

    +0

    '수동'(즉, 속성 별, 루프 별 또는 하위 컬렉션 용)은 '검색된'객체의 속성을 '프레젠테이션 (즉 POST)'객체의 속성으로 덮어 씁니 까? 필요한 하위 삭제를 수동으로 계산 하시겠습니까? 내가 바라던 것보다 더 많은 일 (그리고 유지 보수!)을 보입니다. 모델 바인더로 귀찮게 여길 가치가없는 것처럼 보입니다. 실제로 필드 응답을 양식에서 한 번에 하나씩 처리해야하는 것처럼 보입니다. 그래도 도움을 주셔서 감사합니다. –

    1

    롭의 대답은 '기존 항목을 새 세션에로드 한 다음 병합'방식을보다 자세히 살펴 보았습니다. 물론 내가 원했던대로 수행 할 것으로 보이는 ISession.Merge가 있습니다. 신선한 물체를 가져 와서 두 번째 세션에 방금 다시로드 된 전임자와 병합하십시오.

    그래서 물어 보려는 질문에 대한 대답은 "기존 엔티티를 다시로드 한 다음 새 엔티티로 'ISession.Merge'를 호출하는 것입니다."

    관련 문제