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上发现了一些帖子,人们基本上解决了相同的问题,但没有使用惯例。
感谢您的帮助!
我终于找到了解决方案:我的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);
}
}