Search code examples
c#.netnhibernatec#-4.0nhibernate-3

NHibernate, map a collection where key can be two different columns


There's an entity A.

In addition, there's an entity B which has two associations with A.

A has a collection of B.

This collection must load any B if one of associated A is the parent of loaded A.

Problem is collection mapping on A must filter children based on checking if one of two A associations is the parent one.

How can I achieve that?

Note: Order doesn't matter, so you can suggest some mapping using bag.

Note 2: Please suggest how to achieve that using an XML mapping, I won't do it in code.

UPDATE: Real-world scenario:

it's all about a friendship implementation. I want to map a collection of Friendship in an entity UserProfile. Friendship has two associations representing the relation: OneUser, OtherUser. If I want to get all friends for me, I need to check both properties, because one is my friend if one of both properties are myself.


Solution

  • IF you are willing to move the solution slightly out of the data domain, your users could have a one-to-many relationship of other users they have assigned as friends. This could be protected or private if you don't want it to leak out.

    Then, you implement a public property that defines an actual Friendship as requiring the relationship in both directions. Maybe something like:

    public List<User> Friends {
        this.assignedFriends.Where(f => f.assignFriends.Contains(this)).ToList();
    }
    

    You'll want to make sure you are using a form of 2nd level cache if you do this however, otherwise requests to this property will hammer your database.


    If instead you need to return a collection of a dedicated Friendship object, it depends on how you are persisting those entries and how you have mapped that class.

    One option would be to define Friendship to simply have three columns, a primary key, and two foreign keys back to the User table. You then require a little bit of logic whenever someone tries to add a friend: you first need to check if half of the relationship already exists..

    var f = session.Query<Friend>()
        .Where(x => x.User1 == YourCurrentUser)
        .Cacheable()
        .FirstOrDefault();
    

    then if it exists you assign the other half of the relationship

    f.User2 = TheOtherUser;
    

    otherwise create a new object:

    f = new Friendship();
    f.User1 = YourCurrentUser;
    

    and after either branch:

    session.SaveOrUpdate(f);
    

    then a Users friends becomes

    public property List<Friendship> {
        session.Query<Friendship>()
            .Where(f => 
                f.User1 != null && f.User2 != null
                && (f.User1 == this || f.User2 == this)
            ).Cacheable()
            .ToList();
    }
    

    Please note, I'm not near my installation of VS. Please excuse typos or if I've made obvious blundering errors (it's 2:30am where I am presenltly)