2

我已经在“整个”internetz 中搜索了这个问题,而且由于它相当复杂,所以很难搜索。尝试搜索“Fluent NHibernate Many to Many with a bridge table with extra columns”等...

好的,为了更容易解释我可以参考的一些表格。表:用户,表:函数,表:User_Has_Function。

一个用户可以有很多功能,一个功能可以有很多用户,这在桥表User_Has_Function中链接。桥表具有仅与关系相关的额外列。

无论如何,iv 发现 FNH 没有任何自动解决方案,基本上你必须使用从 User 到 User_Has_Function 的一对多关系以及从 User_Has_Function 到 Function 的多对一关系,因此“[One] 到 [Many - 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();
    }
}
4

3 回答 3

1

我不明白“我真的必须做所有这些手动工作”部分。什么“所有这些手工工作”?那里没有什么特别的。映射很简单,c# 代码不必对持久性做任何事情,它是简单的旧 OO 设计。

如果您得到重复的行,则您的映射有问题。这可能是因为未映射为逆的逆集合。

如果您不需要从 Function 导航到 User,这很容易。如博客中所述,将关系映射为实体,或者更简单地将其映射为复合元素。

(对不起,我不懂流利)

<bag name="Functions" table="User_Has_Function">
  <key column="UserId" />
  <composite-element>
    <many-to-one class="Function"/>
  </composite-element>
</bag>

编辑:

从评论:

我所说的手动工作是手动获取和检查以从用户或功能中删除和添加关系。

您是在谈论保持关系一致性所需的 Add 和 Remove 方法吗?这是简单的 OO 设计。如果你没有 NHibernate,你就必须写完全一样的(给定相同的类模型)。

从函数中删除用户使其一直级联到用户等等......

不会。删除级联会在删除对象时发生。当你删除一个用户时,你应该级联 user_has_function。从那里,您可能会或可能不会级联这些功能。另一个方向也一样。还有“cascade-all-delete-orphans”的概念。这意味着除了常规级联之外,从集合中删除对象时会自动删除它。这不是级联的。这是一种非常基本的垃圾收集。如果您想在您的情况下使用它,则不应将其同时应用于 user->user_has_function 集合和 function->user_has_function 集合,因为它会尝试删除该对象两次。

不要忘记将两个集合反向映射。如果不这样做,您可能会收到重复的条目。

确保三个映射(user->user_has_function 集合、function->user_has_function 和 user_has_function 类映射)使用相同的表名和外键名。

您不需要弄乱复合键。

于 2012-05-30T11:07:21.730 回答
0

不久前,我对用户、组、user_group 做了类似的事情,最后不得不使用一种让两个对象都存在于两边的 hacky 方法,并且还手动选择保存或更新。

我不认为有一种很好的方式来做你想做的事,我同意从数据库的角度来看这是相当合乎逻辑的事情,但从建模的角度来看是一种痛苦。

因为我还假设您必须为 user_has_function 表使用复合键,以确保您可以为多个用户提供多个功能。我认为大多数人都试图避免并最终使用代理键或其他方法。

我知道这不是一个答案,但是当我发布它时,我从未找到同一个问题的真正答案。

这是我不久前发布的一个类似问题:

Nhibernate复合键问题

于 2012-05-30T10:40:26.200 回答
0

我最终使用 ISet 而不是 ILists 中的关系。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.
于 2012-05-31T11:27:12.833 回答