流畅的NHibernate M2M映射([一]到[多 - 多]到[一])

时间:2012-05-30 10:19:25

标签: c# orm fluent-nhibernate

我搜索了这个问题的“整个”因特网,这是一个非常难以搜索的相当复杂的问题。尝试使用带有额外列的桥牌表搜索“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();
    }
}

3 个答案:

答案 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表使用复合键,以确保您可以为多个用户提供多个功能。我认为大多数人都试图避免并最终使用代理键或其他方法。

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

以下是我发布的类似问题:

Nhibernate composite key question

答案 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.