我想将树结构(存储为邻接列表)放入数据库中,我想使用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上发现了一些帖子,人们解决的问题基本相同,但没有约定。
任何帮助表示赞赏!
答案 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);
}
}