流畅的NHibernate Convention映射树结构

时间:2014-10-27 14:59:40

标签: c# nhibernate fluent-nhibernate fluent-nhibernate-mapping

我想将树结构(存储为邻接列表)放入数据库中,我想使用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

到目前为止,这么好。但是如果我在添加约定的程序中取消注释该行,则会发生奇怪的事情。该程序的输出仅为&#34; Root&#34; (即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 |

列&#34; TreeNodeId&#34;在所有行中都为NULL。

显然,这个惯例搞砸了。我想使用这个约定来获取外键的名称,但是我无法使它工作。

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

任何帮助表示赞赏!

1 个答案:

答案 0 :(得分:0)

我终于找到了解决方案: 我的约会扩展的类是标准的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);
}

这使得Children中的集合TreeNode通过添加关于集合类型的列(在这种情况下是同一个类)。就我而言,我已经有了这个专栏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);
    }
}