使用Fluent NHibernate映射树结构

时间:2009-09-04 22:39:59

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

我无法使用Fluent NHibernate映射树结构,而不必放入我认为是黑客的。

考虑以下课程:

  • Node(摘要,有int IdGroupNode Parent(如果null,则节点位于根节点中))
  • GroupNode(继承自Node,有IList<GroupNode> GroupsIList<ItemNode> Items
  • ItemNode(继承自Item

理想情况下,这将具有以下数据库结构:

  • GroupNodes(整数Id,可空整数ParentId)
  • ItemNodes(整数Id,可空整数ParentId)

我的地图制作者看起来像这样:

public class GroupNodeMap : ClassMap<GroupNode>
{
    public GroupNode()
    {
        Id(x => x.Id);
        References(x => x.Parent);
        HasMany(x => x.Groups).LazyLoad();
        HasMany(x => x.Items).LazyLoad();
    }
}

public class ItemNodeMap : ClassMap<ItemNode>
{
    public ItemNodeMap()
    {
        Id(x => x.Id);
        References(x => x.Parent);
    }
}

不幸的是,这会创建一组重复的引用(每个表都有ParentIdGroupNodeId。我可以通过在.KeyColumn("ParentId")之后添加.LazyLoad()来调整此行为,但这感觉就像一个黑客,我希望它使用约定或lambda而不是魔术字符串来表达。

有人能指出我正确的方向吗?

3 个答案:

答案 0 :(得分:1)

以下是您可以尝试使用AutoMap约定和SQLite的示例:

namespace Entities
{
    public abstract class Node
    {
        public virtual int Id { get; set; }
        public virtual GroupNode Parent { get; set; }
    }

    public class ItemNode : Node
    {
    }

    public class GroupNode : Node
    {
        public virtual IList<GroupNode> Groups { get; set; }
        public virtual IList<ItemNode> Items { get; set; }
    }
}

class Program
{
    static void Main()
    {
        if (File.Exists("data.db3"))
        {
            File.Delete("data.db3");
        }

        using (var factory = CreateSessionFactory())
        {
            using (var connection = factory.OpenSession().Connection)
            {
                ExecuteQuery("create table GroupNode(Id integer primary key, Parent_Id integer)", connection);
                ExecuteQuery("create table ItemNode(Id integer primary key, Parent_Id integer)", connection);

                ExecuteQuery("insert into GroupNode(Id, Parent_Id) values (1, null)", connection);
                ExecuteQuery("insert into GroupNode(Id, Parent_Id) values (2, 1)", connection);
                ExecuteQuery("insert into GroupNode(Id, Parent_Id) values (3, 1)", connection);

                ExecuteQuery("insert into ItemNode(Id, Parent_Id) values (1, 1)", connection);
                ExecuteQuery("insert into ItemNode(Id, Parent_Id) values (2, 1)", connection);
            }

            using (var session = factory.OpenSession())
            using (var tx = session.BeginTransaction())
            {
                var node = session.Get<GroupNode>(1);
                tx.Commit();
            }
        }
    }

    private static ISessionFactory CreateSessionFactory()
    {
        return Fluently.Configure()
            .Database(
                SQLiteConfiguration.Standard.UsingFile("data.db3").ShowSql()
            )
            .Mappings(
                m => m.AutoMappings.Add(
                    AutoMap
                        .AssemblyOf<Program>()
                        .Where(t => t.Namespace == "Entities")
                )
            ).BuildSessionFactory();
    }

    static void ExecuteQuery(string sql, IDbConnection connection)
    {
        using (var command = connection.CreateCommand())
        {
            command.CommandText = sql;
            command.ExecuteNonQuery();
        }
    }
}

答案 1 :(得分:0)

我的数据库中有完全相同类型的结构,我也使用了KeyColumn方法。它不是真正的黑客只是解释了fluentnhibernate这个系列另一面的名称是什么。

这是一个特例,因为通常如果设置“HasMany(x =&gt; x.Groups)”,他将在表Group中搜索“GroupId”。但在你的情况下,GroupId已经是该列的关键,它不是你想要用于此列的列。这就是为什么你必须设置“KeyColumn”来告诉fnh他应该使用哪一列作为关系。

经常使用KeyColumn方法,特别是在处理尚未为NHibernate构建的数据库时,并且列具有奇怪的名称。

答案 2 :(得分:-1)

Fluent NHibernate具有命名约定。 如果您不喜欢使用数据库列名(它不是黑客),则应重命名数据库列。 查看Available Conventions