Fluent NHibernate Convention映射树结构

本文关键字:结构 映射 Convention NHibernate Fluent | 更新日期: 2023-09-27 18:28:03

我想把一个树结构(存储为邻接列表)放入数据库,我想使用Fluent NHibernate和Convention映射来完成这项工作。我有以下代码:

[...]
NamespaceAutomapping automapping = new NamespaceAutomapping("NHibernateTree.Model", false); //Maps the classes in the namespace NHibernateTree.Model, false meaning to not map sub-namespaces like NHibernateTree.Model.ABC 
FluentConfiguration fluentConfiguration = Fluently.Configure()
  .Database(FluentNHibernate.Cfg.Db.PostgreSQLConfiguration.PostgreSQL82.ConnectionString(connectionString))
  .Mappings(m => m.AutoMappings.Add(AutoMap.AssemblyOf<TreeNode>(automapping)
      //.Conventions.Add<ForeignKeyNameConvention>()
      ));
[...]
ISessionFactory SessionFactory = fluentConfiguration.BuildSessionFactory();
using (ISession session = SessionFactory.OpenSession())
{
    TreeNode root = new TreeNode() { Value = "Root" };
    TreeNode childA = new TreeNode() { Parent = root, Value = "Child A" };
    root.Children.Add(childA);
    TreeNode childB = new TreeNode() { Parent = root, Value = "Child B" };
    root.Children.Add(childB);
    session.Save(root);
    session.Save(childA);
    session.Save(childB);
}
using (ISession session = SessionFactory.OpenSession())
{
    TreeNode root = session.Query<TreeNode>().First(tn => tn.Value == "Root");
    Console.WriteLine(root.ToString());
}

发表评论的惯例如下:

public class ForeignKeyNameConvention : ForeignKeyConvention
{
    protected override String GetKeyName(Member property, Type type)
    {
        return String.Format("'"{0}Id'"", null == property ? type.Name : property.Name);
    }
}

类NHibernateTree.Model.TreeNode如下所示:(这是该名称空间中唯一的类)

class TreeNode
{
    public virtual int Id { get; set; }
    public virtual TreeNode Parent { get; set; }
    public virtual ICollection<TreeNode> Children { get; set; }
    public virtual String Value { get; set; }
    public TreeNode()
    {
        Children = new Collection<TreeNode>();
    }
    public override string ToString()
    {
        [...]
    }
}

现在,如果我运行这个代码,输出是:

Root
  Child A
  Child B

数据库中的表如下所示:

  Column   |          Type
-----------+-----------------------
 id        | integer
 value     | character varying(255)
 parent_id | integer
 id |  value  | parent_id 
----+---------+-----------
  1 | Root    |
  2 | Child A |         1
  3 | Child B |         1

到目前为止,一切都很好。但如果我不评论程序中添加惯例的行,那么奇怪的事情就会发生。程序的输出仅为"根"(即NHibernate不再找到对子级的引用),而表看起来是这样的:

   Column   |          Type
------------+-----------------------
 id         | integer
 value      | character varying(255)
 ParentId   | integer
 TreeNodeId | integer
 id |  value  | ParentId | TreeNodeId
----+---------+----------+------------
  1 | Root    |          |
  2 | Child A |        1 |
  3 | Child B |        1 |

所有行中的列"TreeNodeId"均为NULL。

很明显,大会把事情搞砸了。我想用这个约定来获得外键的名称,但我无法使用它。

我在SO上发现了一些帖子,人们基本上解决了相同的问题,但没有使用惯例。

感谢您的帮助!

Fluent NHibernate Convention映射树结构

我终于找到了解决方案:我的Convention正在扩展的类是一个标准的Fluent NHibernate类,它看起来是这样的:https://github.com/jagregory/fluent-nhibernate/blob/master/src/FluentNHibernate/Conventions/ForeignKeyConvention.cs

其中有趣的方法如下:

public void Apply(ICollectionInstance instance)
{
    var columnName = GetKeyName(null, instance.EntityType);
    instance.Key.Column(columnName);
}

这使得TreeNode中的集合Children通过在集合的类型(在本例中是同一类)上添加一列而映射。在我的例子中,我已经有了这个专栏ParentId,但NHibernate添加了另一个名为TreeNodeId的专栏。

我的解决方案是跳过包含与包含类相同类型的集合的映射。这意味着NHibernate不会创建额外的列TreeNodeId。因此,我没有扩展ForeignKeyConvention,而是复制了基类ForeignkeyConvention,并将上面的方法修改为:

public void Apply(ICollectionInstance instance)
{
    if (instance.EntityType != instance.ChildType)
    {
        String columnName = GetKeyName(null, instance.EntityType);
        instance.Key.Column(columnName);
    }
}