2

私のプロジェクトにはTicket、プロパティを持つエンティティがありOwnedByます。nHibernateを使用してチケットをデータベースに永続化しています。

潜在的なチケット所有者の正規のソースはActiveDirectoryです。チケットをロードするたびにActiveDirectoryにクエリを実行する必要がないため、チケットをフェッチするときにデータベースに永続化してそこからロードますTicket.OwnedBy

チケットの所有者が再割り当てさOwnerれると、Active Directoryから新しいものを取得して割り当て、 Ticket.OwnedBySession.SaveOrUpdate(ticket)を呼び出します。トランザクションをコミットすると、同じIDを持つがすでにセッションに関連付けられているNonUniqueObjectExceptionため、NHibernateはをスローします。Owner

クラス定義

class Ticket {
    public int Id { get; set; }
    public Owner OwnedBy { get; set; }
    /* other properties, etc */
}
class Owner {
    public Guid Guid { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    /* other properties, etc */
}

流暢なnHibernateマッピング

class TicketMap : ClassMap<Ticket> {
    public TicketMap() {
        Id(x => x.Id);
        References(x => x.OwnedBy)
            .Cascade.SaveUpdate()
            .Not.Nullable();
        /* other properties, etc */
    }    
}
class OwnerMap : ClassMap<Owner> {
    public OwnerMap() {
        Id(x => x.Guid)
            .GeneratedBy.Assigned()
        Map(x => x.Name);
        Map(x => x.Email);
        /* other properties, etc */
    }
}

サンプルコード

// unitOfWork.Session is an instance of NHibernate.ISession
Ticket ticket = unitOfWork.Session.Get<Ticket>(1);
Owner newOwner = activeDirectoryRepo.FindByGuid(/* guid of new owner, from user */);
ticket.OwnedBy = newOwner;
unitOfWork.Session.SaveOrUpdate(ticket);
unitOfWork.Commit(); // Throws NonUniqueObjectException

nHibernateに既存Ownerのプロパティを接続されていないプロパティのプロパティで上書きしてほしい。(ADからフェッチしたオブジェクトの名前または電子メールは異なる場合があり、ADが正規のソースであると想定されます。)SaveOrUpdate(ticket)の前に呼び出してみましSession.SaveOrUpdateCopy(ticket.OwnedBy)Session.Merge(ticket.OwnedBy)が、例外がスローされます。についてのこの関連する質問も読みましNonUniqueObjectExceptionたが、Session.Lock()の呼び出しも機能しませんでした。

2つの質問があります:

  1. nHibernateを私の意志に合わせて曲げる簡単な方法はありますか?
  2. ADからフェッチした所有者を、DBに格納している所有者と同じタイプとして処理しようとして、アーキテクチャ上の誤りを犯した可能性があります。nHibernateを自分の意志で曲げる必要がないように、このデザインをどのように改善できますか?
4

2 に答える 2

2

マージは機能します。最も可能性の高い問題は、正しく呼び出されなかったことです。Mergeは既存のオブジェクトを新しいオブジェクトプロパティで更新しますが、Mergeは新しいオブジェクトをアタッチしません。したがって、既存のものを使用する必要があります。マージ後に新しいオブジェクトを使用しても、同じエラーが発生します。

次のコードで問題を解決できます。

//Merge the new Owner
unitOfWork.Session.Merge(newOwner);

//Get a valid Owner by retrieving from the session
Owner owner = session.Get<Owner>(newOwner.Id);

// Set the ticket to this owner instance instead of the new one
ticket.OwnedBy = owner;

unitOfWork.Session.Update(ticket);
unitOfWork.Commit(); 

Getによってセッションから取得された所有者は、newOwnerプロパティを持ちますが、セッションでも有効です。

于 2010-11-28T21:05:04.333 に答える
0

既存のOwner-entityのデタッチされたインスタンスを永続化するには、SaveOrUpdateを呼び出さずにOwnerインスタンスでmergeを呼び出すだけで十分です。次に、エンティティを挿入するか、既存のエンティティを更新します。

マージが機能しない場合は、何かが間違っています。その場合はさらにコードを投稿し、マッピングを投稿してください。

ところで:あなたもチケットを保持しますか?もしそうなら、マッピングはかなり奇妙に思えます。参照として、おそらくカスケードを使用した逆マッピングとして、TicketとMapOwnedByに一意のIDが必要です。

更新:両側からマップする必要があります。所有者側をHasManyとして、カスケードを使用してチケットにマッピングし、逆マッピングとしてマッピングします。チケット側をCascade.None()および参照としてマップします。

public TicketMap() {
    Id(x => x.Id);
    References(x => x.OwnedBy)
        .Cascade.None()
        .Not.Nullable();
    /* other properties, etc */
}   

class OwnerMap : ClassMap<Owner> {
public OwnerMap() {
    Id(x => x.Guid)
        .GeneratedBy.Assigned()
    Map(x => x.Name);
    Map(x => x.Email);
     HasMany<Ticket>(x => x.Tickets).KeyColumn("TicketId").Cascade.AllDeleteOrphan().LazyLoad().Inverse().NotFound.Ignore();
    /* other properties, etc */
}

それはうまくいくはずです。

于 2010-11-24T20:36:06.670 に答える