我认为你可能从错误的角度来理解它。在 NHibernate 中,将多对多关系显式映射为模型对象是非常不寻常的。请参阅下面的更改建议。
给定域对象MyMember 及其覆盖的映射:
public class MyMember : DomainObjectBase
{
public virtual string Name { get; set; }
public virtual IList<MyMember> Follows { get; set; }
}
public class MemberOverride : IAutoMappingOverride<MyMember>
{
public void Override(AutoMapping mapping)
{
mapping.HasManyToMany<MyMember> (x => x.Follows)
.Table("FollowMap")
.ParentKeyColumn("FollowerID")
.ChildKeyColumn("FollowedID")
.Cascade.SaveUpdate(); ;
}
}
以下测试通过:
[Test]
public void WhoFollowsWho()
{
var a = new MyMember {Name = "A"};
var b = new MyMember {Name = "B"};
var c = new MyMember {Name = "C"};
var d = new MyMember {Name = "D"};
var e = new MyMember {Name = "E"};
a.Follows = new List<MyMember> { b, c, d, e };
d.Follows = new List<MyMember> { a, c, e };
using (var t = Session.BeginTransaction())
{
Session.Save(a);
Session.Save(b);
Session.Save(c);
Session.Save(d);
Session.Save(e);
t.Commit();
}
using (var t = Session.BeginTransaction())
{
DetachedCriteria followersOfC = DetachedCriteria.For<MyMember>();
followersOfC.CreateCriteria("Follows")
.Add(Expression.Eq("Id", c.Id))
.SetProjection(Projections.Property("Name"));
var results = followersOfC.GetExecutableCriteria(Session).List();
t.Commit();
CollectionAssert.AreEquivalent(new[]{"A", "D"}, results);
}
using (var t = Session.BeginTransaction())
{
DetachedCriteria followedByA = DetachedCriteria.For<MyMember>();
followedByA.CreateAlias("Follows", "f")
.Add(Expression.Eq("Id", a.Id))
.SetProjection(Projections.Property("f.Name"));
var results = followedByA.GetExecutableCriteria(Session).List();
t.Commit();
CollectionAssert.AreEquivalent(new[]{"B", "C", "D", "E"}, results);
}
}
生成的 SQL 和预期一样依赖于内部连接:
NHibernate:
SELECT this_.Name as y0_ FROM "MyMember" this_
inner join FollowMap follows3_ on this_.Id=follows3_.FollowerID
inner join "MyMember" mymember1_ on follows3_.FollowedID=mymember1_.Id
WHERE mymember1_.Id = @p0
NHibernate:
SELECT f1_.Name as y0_ FROM "MyMember" this_
inner join FollowMap follows3_ on this_.Id=follows3_.FollowerID
inner join "MyMember" f1_ on follows3_.FollowedID=f1_.Id
WHERE this_.Id = @p0
注意:如果不是只检索每个 MyMember 的“名称”属性,而是检索 MyMember 的完整实例,则 SQL 语句将保持相同的形状。只有额外的投影被添加到 SELECT 子句。但是,您必须修复测试以使其再次通过;-)
注意 2: 如果您愿意处理拥有自己属性的多对多关系,则此 post 来自 Kyle Baley 和此 one 来自Nhibernate 博客可能会在此主题上提供一些帮助。
注意 3:我已经尝试过了 :-)
给定域对象 MySecondMember 和 MyFollowMap 及其覆盖的映射:
public class MySecondMember : DomainObjectBase
{
public virtual string Name { get; set; }
public virtual IList<MyFollowMap> Follows { get; set; }
}
public class MyFollowMap : DomainObjectBase
{
public virtual MySecondMember Who { get; set; }
public virtual DateTime StartedToFollowOn { get; set; }
}
public class MemberSecondOverride : IAutoMappingOverride<MySecondMember>
{
public void Override(AutoMapping mapping)
{
mapping.HasMany(x => x.Follows);
}
}
以下测试通过:
[Test]
public void WhoFollowsWho2()
{
var a = new MySecondMember { Name = "A" };
var b = new MySecondMember { Name = "B" };
var c = new MySecondMember { Name = "C" };
var d = new MySecondMember { Name = "D" };
var e = new MySecondMember { Name = "E" };
var bfm = new MyFollowMap { Who = b, StartedToFollowOn = DateTime.UtcNow };
var cfm = new MyFollowMap { Who = c, StartedToFollowOn = DateTime.UtcNow };
var dfm = new MyFollowMap { Who = d, StartedToFollowOn = DateTime.UtcNow };
var efm = new MyFollowMap { Who = e, StartedToFollowOn = DateTime.UtcNow };
a.Follows = new List { bfm, cfm, dfm, efm };
var afm = new MyFollowMap { Who = a, StartedToFollowOn = DateTime.UtcNow };
cfm = new MyFollowMap { Who = c, StartedToFollowOn = DateTime.UtcNow };
efm = new MyFollowMap { Who = e, StartedToFollowOn = DateTime.UtcNow };
d.Follows = new List { afm, cfm, efm };
using (var t = Session.BeginTransaction())
{
Session.Save(a);
Session.Save(b);
Session.Save(c);
Session.Save(d);
Session.Save(e);
t.Commit();
}
using (var t = Session.BeginTransaction())
{
DetachedCriteria followersOfC = DetachedCriteria.For<MySecondMember>();
followersOfC.CreateAlias("Follows", "f")
.CreateAlias("f.Who", "w")
.Add(Expression.Eq("w.Id", c.Id))
.SetProjection(Projections.Property("Name"));
var results = followersOfC.GetExecutableCriteria(Session).List();
t.Commit();
CollectionAssert.AreEquivalent(new[] { "A", "D" }, results);
}
using (var t = Session.BeginTransaction())
{
DetachedCriteria followedByA = DetachedCriteria.For<MySecondMember>();
followedByA
.CreateAlias("Follows", "f")
.CreateAlias("f.Who", "w")
.Add(Expression.Eq("Id", a.Id))
.SetProjection(Projections.Property("w.Name"));
var results = followedByA.GetExecutableCriteria(Session).List();
t.Commit();
CollectionAssert.AreEquivalent(new[] { "B", "C", "D", "E" }, results);
}
}
生成的 SQL 和预期一样依赖于内部连接:
NHibernate:
SELECT this_.Name as y0_ FROM "MySecondMember" this_
inner join "MyFollowMap" f1_ on this_.Id=f1_.MySecondMember_id
inner join "MySecondMember" w2_ on f1_.Who_id=w2_.Id
WHERE w2_.Id = @p0;
NHibernate:
SELECT w2_.Name as y0_ FROM "MySecondMember" this_
inner join "MyFollowMap" f1_ on this_.Id=f1_.MySecondMember_id
inner join "MySecondMember" w2_ on f1_.Who_id=w2_.Id
WHERE this_.Id = @p0