Understanding foreign key not-null=true and inverse behavior in a zero-to-one relationship with NHibernate

后端 未结 1 1965
余生分开走
余生分开走 2020-12-08 23:12

I\'m trying to get NHibernate to use the many side of a collection to manage a bidirectional association to model a zero-to-one relationship.

Parent Class an

相关标签:
1条回答
  • 2020-12-08 23:49

    When to use inverse="true|false"

    The inverse attribute is used to help NHibernate know which side of a relationship should be used to persist the relationship. The non-inverse side (please note the double negative) is the side that will be persisted. If neither side is inverse, then the relationship will be persisted twice, like with the INSERT followed immediately by an UPDATE example you provided above. If both sides are inverse, then the relationship won't be persisted at all, so it's important to set inverse correctly.

    I like to think about inverse in the following way. I don't know whether this is officially the way it works or not, but it helps my world make sense:

    many-to-one

    many-to-one relationships are always inverse="false". They are always used to persist the relationship to the database. Since they are always inverse="false", there's no need to specify it, so NHibernate (and hence Fluent NHibernate) doesn't provide an option for it.

    (I have run across only one situation where I wish I could specify inverse="true" on a many-to-one. If you have a one-to-many list on one side and a many-to-one on the other side, you should be able to let the list control the relationship so that NHibernate can take care of setting the index values for you. As it currently stands, you have to add a property to the child class and manage the index values yourself.)

    one-to-one

    one-to-one relationships are always inverse="true". They never exist without either an id or many-to-one on the other side of the relationship which will take care of persisting the relationship. Since the inverse value is always the same, there's no need to specify it, so specifying it is not supported.

    Collections

    Collections like bag, list, set, etc. may or may not be a part of a bi-directional relationship. If they exist on their own (perhaps a bag of string elements), then they need to be inverse="false" (which is the default) because no one else will be responsible for persisting the relationship. If they exist in conjunction with another relationship, however (like your traditional one-to-many/many-to-one) they should be specified as inverse="true".

    With many-to-many collections where you have a collection on either side of the relationship, mark one of them as inverse="true" and leave the other one as the default inverse="false". Again, the point is that one side of the relationship must be non-inverse. Which side should you pick? If we take a many-to-many relationship between Users and Roles for example, you probably have lots of Users and a few Roles. In my opinion, you should map Role.Users as inverse="true" and let User.Roles control the relationship since it's a smaller set of data to work with and it's probably the collection you care more about anyway.

    (In fact, I would be hesitant to include Role.Users in the model at all. Suppose a "Customer" role has 100,000 users. Then customerRole.Users is an unusable lazy-loading bomb waiting to explode.)

    ...back to your question...

    Since it doesn't really matter which side of the relationship is inverse, just so long as one side is non-inverse, then you should make the one-to-one side the inverse side since that's the way NHibernate wants to do it. Don't fight the tool over stuff that doesn't matter. In the mappings you provided, essentially both sides of the relationship have been marked non-inverse, which caused the relationship to be persisted twice. The following mappings should work better for you:

    public class Parent
    {
        public virtual Guid Id { get; set; }
        public virtual Child Child { get; set; }
    }
    
    public class ParentClassMap : ClassMap<Parent>
    {
        public ParentClassMap()
        {
            Id(x => x.Id);
            HasOne(x => x.Child)
                .PropertyRef(x => x.Parent)
                .Cascade.All();
        }
    }
    
    public class Child
    {
        public virtual Guid Id { get; set; }
        public virtual Parent Parent { get; set; }
    }
    
    public class ChildClassMap : ClassMap<Child>
    {
        public ChildClassMap()
        {
            Id(x => x.Id);
            References(x => x.Parent)
                .Not.Nullable()
                .Unique()
                .Cascade.SaveUpdate();
        }
    }
    

    ... which results in the following SQL from your test insertion code:

    exec sp_executesql N'INSERT INTO [Parent] (Id) VALUES (@p0)',N'@p0 uniqueidentifier',@p0='925237BE-558B-4985-BDA2-9F36000797F5'
    exec sp_executesql N'INSERT INTO [Child] (Parent_id, Id) VALUES (@p0, @p1)',N'@p0 uniqueidentifier,@p1 uniqueidentifier',@p0='925237BE-558B-4985-BDA2-9F36000797F5',@p1='BE6D931A-8A05-4662-B5CD-9F36000797FF'
    

    No update query!

    0 讨论(0)
提交回复
热议问题