【问题标题】:Fluent NHibernate M2M Mapping ([One] to [Many - Many] to [One])Fluent NHibernate M2M 映射([One] 到 [Many - Many] 到 [One])
【发布时间】:2012-05-30 10:19:25
【问题描述】:

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

好的,为了便于解释,我定义了一些我可以参考的表格。 表:用户,表:函数,表:User_Has_Function。

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

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

【问题讨论】:

  • 好的代码是你想让别人看到的代码,你把它放在他们的脸上来炫耀你的才华。我为解决这个问题而编写的代码,我宁愿从不向任何人展示,这就是我的感受......这不是你对编写代码的感觉,这是一种不好的做法 imo。

标签: c# orm fluent-nhibernate


【解决方案1】:

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

如果您得到重复的行,则说明您的映射有问题。这可能是因为一个逆集合没有被映射为逆。

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

(对不起,我不懂Fluent)

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

编辑:

来自cmets:

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

您是在谈论保持关系一致性的必需的 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 类映射)使用相同的表名和外键名。

您不需要弄乱复合键。

【讨论】:

  • 我所说的手动工作是手动获取和检查以从用户或函数中删除和添加关系。从函数中删除用户使其一直级联到用户等等......也许我对这一切的理解完全是错误的,或者我正在做一些根本性的错误,但这就是为什么我要问这是否是“唯一”的方式,或者有人可以为我指出最佳实践的正确方向。
  • 找到了这个关于复合键的链接devlicio.us/blogs/anne_epstein/archive/2009/11/20/…
  • 是的,我知道如果我编写普通的 sql,我将不得不自己做那些“手动工作”,但我的印象是 hibernate 内置了一些功能,可以让你不必通过使用来做这些事情正确的方式,我不觉得我使用它的方式是正确的。
  • @furier:我不是说用普通 SQL 编写应用程序。我的意思是,在编写完全没有持久性的应用程序时。 NHibernate 允许您编写一个“持久无知”的 OO 域。这意味着它不做任何域的责任。如果是这样,您的模型将无法在后面没有 NH 的情况下工作。
【解决方案2】:

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

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

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

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

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

Nhibernate composite key question

【讨论】:

  • 感谢您的回答,我开始看到这个问题没有“最佳实践”,您只需解决它并让它成为...虽然我讨厌这样的解决方案,让我想要扭曲我的皮肤:P
【解决方案3】:

我最终使用 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.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多