Displaying a tree structure with Fluent NHibernate

I am having trouble displaying a tree structure using Fluent NHibernate without also requiring me to consider this a hack.

Consider the following classes:

  • Node

    (abstract, has int Id

    and GroupNode Parent

    (if null

    , node is at root node))
  • GroupNode

    (inherits from Node

    , has IList<GroupNode> Groups

    and IList<ItemNode> Items

    )
  • ItemNode

    (inherits from Item

    )

Ideally this would have the following database structure:

  • GroupNodes (Integer ID, Integer ParentId)
  • ItemNodes (integer ID, integer with zero ParentId)

My cartographers look like this:

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);
    }
}

      

Unfortunately this creates a duplicate set of references (each table gets both ParentId

and GroupNodeId

. I can customize this behavior by adding .KeyColumn("ParentId")

after .LazyLoad()

, but this seems like a hack and I would like it to be expressed using either conditionals or lambda instead of magic strings.

Can anyone point me in the right direction?

+2


source to share


3 answers


You can try using AutoMap and SQLite conventions here :



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


source


I have exactly the same type of structure in my database and I also used the KeyColumn method. This isn't really a hack, it just explains how to speak fluently what is called the other side of the collection.

This is a bit of a special case because normally if you set "HasMany (x => x.Groups)" it will look for "GroupId" in the Group table. But in your case, the GroupId is already the key of that column, and this is not the column you want to use for that. This is why you must set the "KeyColumn" to tell fnh which column it should use for the relationship.



The KeyColumn method is used a lot, especially when working with a database that was not built for NHibernate and the columns have strange names.

0


source


Free NHibernate has naming conventions. If you don't like the database column names (this is not a hack), you should rename the database columns. Check Available Agreements

-1


source







All Articles