我搜索了这个问题的“整个”因特网,这是一个非常难以搜索的相当复杂的问题。尝试使用带有额外列的桥牌表搜索“Fluent NHibernate Many to Many”等等......
好的,为了更容易解释,我可以参考一些表定义。 表:用户,表:功能,表:User_Has_Function。
一个用户可以拥有多个功能,一个功能可以拥有多个用户,这在桥接表User_Has_Function中链接。桥表有额外的列,只与关系有关。
好吧无论如何iv发现FNH没有任何自动解决方案,基本上你必须使用从User到User_Has_Function的一对多关系以及从User_Has_Function到Function的多对一关系,因此“[One] to [Many - 很多]到[一]“。
我已经解决了这个问题就像在这个链接http://sessionfactory.blogspot.com/2010/12/many-to-many-relationships-with.html一样,只是用FNH类映射而不是xml。
但是我对解决方案不满意,我真的必须手动完成所有这些功能才能正常运行吗?此外,它现在在桥表中插入重复项。
在我的脑海里,我做错了什么,因为我无法想象没有这方面的支持。只需使用SaveAndUpdate(),不会插入重复项,当我删除实体时,关系也会被删除,如果没有关系,则删除实体本身等。
好的,这是我的实体和映射,我对Fluent NHibernate非常陌生,所以如果我做了一些非常错误的话,不要大喊大叫。 :)
实体:
public class XUser
{
public virtual int Id { get; set; }
...
public virtual IList<XUserHasXFunction> XUserHasXFunctions { get; set; }
public XUser()
{
XUserHasXFunctions = new List<XUserHasXFunction>();
}
public virtual void AddXFunction(XFunction xFunction, int isActive)
{
var xUserHasXFunction = new XUserHasXFunction()
{
XUser = this,
XFunction = xFunction,
DeployedDate = DateTime.Now
};
XUserHasXFunctions.Add(xUserHasXFunction);
xFunction.XUserHasXFunctions.Add(xUserHasXFunction);
}
public virtual void RemoveXFunction(XFunction xFunction)
{
var xUserHasXFunction = XUserHasXFunctions.Single(x => x.XFunction == xFunction);
XUserHasXFunctions.Remove(xUserHasXFunction);
xFunction.XUserHasXFunctions.Remove(xUserHasXFunction);
}
}
public class XFunction
{
public virtual int Id { get; set; }
...
public virtual IList<XUserHasXFunction> XUserHasXFunctions { get; set; }
public XFunction()
{
XUserHasXFunctions = new List<XUserHasXFunction>();
}
public virtual void AddXUser(XUser xUser, int isActive)
{
var xUserHasXFunction = new XUserHasXFunction()
{
XUser = xUser,
XFunction = this,
DeployedDate = DateTime.Now
};
XUserHasXFunctions.Add(xUserHasXFunction);
xUser.XUserHasXFunctions.Add(xUserHasXFunction);
}
public virtual void RemoveXUser(XUser xUser)
{
var xUserHasXFunction = XUserHasXFunctions.Single(x => x.XUser == xUser);
XUserHasXFunctions.Remove(xUserHasXFunction);
xUser.XUserHasXFunctions.Remove(xUserHasXFunction);
}
}
public class XUserHasXFunction
{
public virtual int Id { get; set; }
public virtual XUser XUser { get; set; }
public virtual XFunction XFunction { get; set; }
public virtual DateTime DeployedDate { get; set; }
}
映射:
public class XUserMap : ClassMap<XUser>
{
public XUserMap()
{
Id(x => x.Id, "ID").GeneratedBy.Sequence("SEQ").Column("ID");
Table("XUSER");
...
HasMany(x => x.XUserHasXFunctions).Cascade.All();
}
}
public class XFunctionMap : ClassMap<XFunction>
{
public XFunctionMap()
{
Id(x => x.Id, "ID").GeneratedBy.Sequence("SEQ").Column("ID");
Table("XFUNCTION");
...
HasMany(x => x.XUserHasXFunctions).Cascade.All();
}
}
public class XUserHasXFunctionMap : ClassMap<XUserHasXFunction>
{
public XUserHasXFunctionMap()
{
Id(x => x.Id, "ID").GeneratedBy.Sequence("SEQ").Column("ID");
Table("USER_HAS_FUNCTION");
Map(x => x.DeployedDate, "DEPLOYED_DATE");
References(x => x.XUser).ForeignKey("XUSER_ID").Cascade.SaveUpdate();
References(x => x.XFunction).ForeignKey("XFUNCTION_ID").Cascade.SaveUpdate();
}
}
答案 0 :(得分:1)
我不明白“我真的必须做所有这些手工作业”的一部分。什么“所有这些手册工作”?那里没什么特别的。映射很简单,而且c#代码不需要对持久性做任何事情,这是普通的旧OO设计。
如果您获得重复的行,那么您的映射会出现问题。这可能是因为反向集合未被映射为反向。
如果您不需要从“功能”导航到“用户”,则非常容易。将关系映射为实体,如博客中所述,或者更容易,将其映射为复合元素。
(抱歉,我不知道Fluent)
<bag name="Functions" table="User_Has_Function">
<key column="UserId" />
<composite-element>
<many-to-one class="Function"/>
</composite-element>
</bag>
修改强>
来自评论:
我所说的手工工作是手动获取和检查 从用户或函数中删除和添加关系。
您是否在谈论所需的添加和删除方法,这些方法可以保持关系的一致性?这是简单的OO设计。如果你没有NHibernate,你必须完全相同(给定相同的类模型)。
从函数中删除用户使其一直级联到用户和 等等...
没有。删除对象时会发生删除级联。删除用户时,应该级联user_has_function。从那里,您可能会或可能不会级联这些功能。在另一个方向相同。还有“cascade-all-delete-orphans”的概念。这意味着除了常规级联之外,从集合中删除对象时会自动删除该对象。这不是级联的。这是一种非常基本的垃圾收集。如果你想在你的情况下使用它,你不应该将它应用于user-&gt; user_has_function集合和function-&gt; user_has_function集合,因为它会尝试删除对象两次。
不要忘记反映两个集合。如果不这样做,您可能会收到重复的条目。
确保三个映射(user-&gt; user_has_function集合,function-&gt; user_has_function和user_has_function类映射)使用相同的表名和外键名称。
您不需要使用复合键。
答案 1 :(得分:0)
我最后用user,group,user_group做了类似的事情,最后不得不使用hacky方法让两边都存在两个对象,并且还可以手动选择保存或更新。
我认为没有一种 NICE 方式可以做你想做的事情,我同意从数据库的角度来看这是合乎逻辑的,但是从建模点来看观点是一种痛苦。
我还假设您必须为user_has_function表使用复合键,以确保您可以为多个用户提供多个功能。我认为大多数人都试图避免并最终使用代理键或其他方法。
我知道这不是一个答案,但是当我发布它时,我从未找到同一个问题的真实的答案。
以下是我发布的类似问题:
答案 2 :(得分:0)
我最终使用的是ISet而不是IList中的关系。 ISet不允许重复,但IList会这样做。要使用ISet,您必须覆盖存储在ISet中的对象的Equals和GetHashCode方法。
我从XUser和XFunction级联而不是相反,最后我删除了一个实体因为级联而删除了所有3个表中的每条记录。
以下是我如何解决它。
实体:
public class XUser
{
public virtual int Id { get; set; }
...
public virtual ISet<XUserHasXFunction> XUserHasXFunctions { get; set; }
public XUser()
{
XUserHasXFunctions = new HashedSet<XUserHasXFunction>();
}
public virtual void AddXFunction(XFunction xFunction, int isActive)
{
var xUserHasXFunction = new XUserHasXFunction()
{
XUser = this,
XFunction = xFunction,
IsActive = isActive,
DeployedDate = DateTime.Now
};
if (XUserHasXFunctions.Contains(xUserHasXFunction) && xFunction.XUserHasXFunctions.Contains(xUserHasXFunction))
{
return;
}
XUserHasXFunctions.Add(xUserHasXFunction);
xFunction.XUserHasXFunctions.Add(xUserHasXFunction);
}
public virtual void RemoveXFunction(XFunction xFunction)
{
var xUserHasXFunction = XUserHasXFunctions.Single(x => x.XFunction == xFunction);
XUserHasXFunctions.Remove(xUserHasXFunction);
xFunction.XUserHasXFunctions.Remove(xUserHasXFunction);
}
}
public class XFunction
{
public virtual int Id { get; set; }
...
public virtual ISet<XUserHasXFunction> XUserHasXFunctions { get; set; }
public XFunction()
{
XUserHasXFunctions = new HashedSet<XUserHasXFunction>();
}
public virtual void AddXUser(XUser xUser, int isActive)
{
var xUserHasXFunction = new XUserHasXFunction()
{
XUser = xUser,
XFunction = this,
IsActive = isActive,
DeployedDate = DateTime.Now
};
if (XUserHasXFunctions.Contains(xUserHasXFunction) && xUser.XUserHasXFunctions.Contains(xUserHasXFunction))
{
return;
}
XUserHasXFunctions.Add(xUserHasXFunction);
xUser.XUserHasXFunctions.Add(xUserHasXFunction);
}
public virtual void RemoveXUser(XUser xUser)
{
var xUserHasXFunction = XUserHasXFunctions.Single(x => x.XUser == xUser);
XUserHasXFunctions.Remove(xUserHasXFunction);
xUser.XUserHasXFunctions.Remove(xUserHasXFunction);
}
}
public class XUserHasXFunction
{
public virtual int Id { get; set; }
...
public virtual DateTime DeployedDate { get; set; }
public override bool Equals(object obj)
{
if (obj == null)
return false;
var t = obj as XUserHasXFunction;
if (t == null)
return false;
return XUser == t.XUser && XFunction == t.XFunction;
}
public override int GetHashCode()
{
return (XUser.Id + "|" + XFunction.Id).GetHashCode();
}
}
映射:
public class XUserMap : ClassMap<XUser>
{
public XUserMap()
{
Id(x => x.Id, "ID").GeneratedBy.Sequence("SEQ").Column("ID");
Table("XUSER");
...
HasMany(x => x.XUserHasXFunctions).KeyColumn("XUSER_ID").Cascade.All();
}
}
public class XFunctionMap : ClassMap<XFunction>
{
public XFunctionMap()
{
Id(x => x.Id, "ID").GeneratedBy.Sequence("SEQ").Column("ID");
Table("XFUNCTION");
...
HasMany(x => x.XUserHasXFunctions)KeyColumn("XFUNCTION_ID").Cascade.All();
}
}
public class XUserHasXFunctionMap : ClassMap<XUserHasXFunction>
{
public XUserHasXFunctionMap()
{
Id(x => x.Id, "ID").GeneratedBy.Sequence("SEQ").Column("ID");
Table("XUSER_HAS_XFUNCTION");
...
Map(x => x.DeployedDate, "DEPLOYED_DATE");
References(x => x.XUser).Column("XUSER_ID");
References(x => x.XFunction).Column("XFUNCTION_ID");
}
}
用法:
To add relations.
xFunction.AddXUser(xUser, isActive); //visa versa if you like to add a function to a user...
dao.Store(xFunction); //to actually add the relation in the db
now to remove relation
xFunction.RemoveXUser(xUser); //Realtion is removed but neither of the objects xFunction or xUser
dao.Store(xFunction); //...same
to remove a user and its relations.
dao.delete(xUser); //but the xFunction object it was connected to is not removed
//if you want the xFunction object to be removed you have to do that manually.