我在做什么错了? 编辑2011-05-30所以,我的临时解决方案是在许多方面采用标准的inverse=true设置,并在父级"设置器中做一些魔术:public virtual Child Child{ get { return children.FirstOrDefault(); } set { { this.children.Clear(); if (value != null) { value.Parent = this; this.children.Add(value); } } }}但是我仍然对inverse=false行为感到困惑,它在多对一方面应该等同于inverse=true(有趣的是,FluentNhibernate不允许ManyToOnePart像这样设置inverse=true 这篇文章推荐) 解决方案 何时使用inverse ="true | false" inverse属性用于帮助NHibernate知道应该使用关系的哪一侧来保持关系.非反面(请注意双负数)是将保留的面.如果双方都不相反,则该关系将被保留两次,就像INSERT紧随其后是您在上面提供的UPDATE示例一样.如果双方都是相反的,那么这种关系将根本不会保留,因此正确设置反向很重要.我喜欢以以下方式考虑inverse.我不知道这是否正式是它的工作方式,但它可以帮助我的世界理解:多对一 many-to-one关系始终为inverse="false".它们始终用于保持与数据库的关系.由于它们始终为inverse="false",因此无需指定它,因此NHibernate(以及Fluent NHibernate)也没有为此提供选择. (我只遇到过一种情况,希望我可以在many-to-one上指定inverse="true".如果您的一侧是one-to-many list,而另一侧是many-to-one方面,您应该能够让list控制关系,以便NHibernate可以为您设置index值.目前,您必须在子类中添加一个属性并管理index珍惜自己.)一对一 one-to-one关系始终为inverse="true".在关系的另一侧没有id或many-to-one时,它们将永远不存在,而这将有助于保持该关系.由于inverse值始终相同,因此无需指定它,因此不支持指定它.收藏集诸如bag,list,set等的集合可能是也可能不是双向关系的一部分.如果它们独立存在(也许是一串字符串元素),则它们必须是inverse="false"(这是默认值),因为没有其他人将负责持久化该关系.但是,如果它们与另一种关系一起存在(例如,传统的一对多/多对一),则应将它们指定为inverse="true".在many-to-many集合中,如果在关系的任一侧都有一个集合,则将其中一个标记为inverse="true",将另一个标记为默认的inverse="false".再次强调,关系的一侧必须是非逆的.您应该选择哪一边?例如,如果我们以用户和角色之间的多对多关系为例,您可能会有很多用户和几个角色.在我看来,您应该将Role.Users映射为inverse="true"并让User.Roles控制该关系,因为这是处理的较小数据集,并且无论如何它可能是您更关心的集合. (实际上,我很犹豫是否在模型中包含Role.Users.假设客户"角色有100,000个用户.那么customerRole.Users是等待爆炸的无法使用的延迟加载炸弹. ) ...回到您的问题...由于关系的哪一侧是反方向并不重要,只要一侧是非反方向,那么您应该将one-to-one侧设为反方向,因为这是NHibernate想要的方式.不要将工具与无关紧要的东西作斗争.在您提供的映射中,基本上关系的两面都被标记为非逆的,这导致该关系被保留两次.以下映射应该更适合您: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(); }} ...,这会从您的测试插入代码中产生以下SQL: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'没有更新查询!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 and Map:public class Parent{ private ICollection<Child> children; public Parent() { this.children = new HashedSet<Child>(); } public virtual Guid Id { get; protected internal set; } public virtual Child Child { get { return children.FirstOrDefault(); } set { { this.children.Clear(); if (value != null) { this.children.Add(value); } } } }}public class ParentMap : ClassMap<Parent>{ public ParentMap() { this.Id(x => x.Id) .GeneratedBy.GuidComb(); this.HasMany<Child>(Reveal.Member<Parent>("children")) .Access.Field() .Cascade.All() .Not.Inverse() .AsSet(); }}Child Class and Map:public class Child{ public virtual Guid Id { get; protected internal set; } public virtual Parent Parent { get; set; }}public class ChildMap : ClassMap<Child>{ public ChildMap() { this.Id(x => x.Id) .GeneratedBy.GuidComb(); this.References(x => x.Parent) .Not.Nullable() .Cascade.All(); }}The following code produces two inserts and an update:var parent = new Parent();var child = new Child();parent.Child = child;child.Parent = parent;session.Save(parent);session.Flush();Notice the essentially duplicate SQL for the second insert and the following update:exec sp_executesql N'INSERT INTO [Parent] (Id) VALUES (@p0)',N'@p0 uniqueidentifier',@p0='AA5A146E-E3F5-4373-B7A8-9EF301171401'goexec sp_executesql N'INSERT INTO [Child] (Parent_id, Id) VALUES (@p0, @p1)',N'@p0 uniqueidentifier,@p1 uniqueidentifier',@p0='AA5A146E-E3F5-4373-B7A8-9EF301171401',@p1='B78C4461-A217-47FC-BE02-9EF30117140A'goexec sp_executesql N'UPDATE [Child] SET Parent_id = @p0 WHERE Id = @p1',N'@p0 uniqueidentifier,@p1 uniqueidentifier',@p0='AA5A146E-E3F5-4373-B7A8-9EF301171401',@p1='B78C4461-A217-47FC-BE02-9EF30117140A'goWhile this code produces the infamous not-null property references a null or transient value inverse:var parent = new Parent();var child = new Child();parent.Child = child;//child.Parent = parent;session.Save(parent);session.Flush();I've found numerous posts about this, but have yet to find a definitive guide on how to do zero-to-one, with inverse=false on the one side.I've tried the one-to-many/one-to-one method mentioned here.As well, I've found several open issues on NHibernate about (not)nullable Foreign Keys: NH-941, NH-1050, etc..What am I doing wrong?Edit 2011-05-30So, my temporary solution is to go for the standard inverse=true setting on the many side, and do some magic in the setter of the Parent:public virtual Child Child{ get { return children.FirstOrDefault(); } set { { this.children.Clear(); if (value != null) { value.Parent = this; this.children.Add(value); } } }}But I'm still baffled by the inverse=false behavior, which should be the equivalent of inverse=true on the many-to-one side (interestingly, FluentNhibernate doesn't allow for the ManyToOnePart to set inverse=true like this article recommends). 解决方案 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-onemany-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-oneone-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.CollectionsCollections 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! 这篇关于与NHibernate以零对一关系理解外键not-null = true和逆行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 上岸,阿里云! 09-05 02:52