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, hasint Id
andGroupNode Parent
(ifnull
, node is at root node)) -
GroupNode
(inherits fromNode
, hasIList<GroupNode> Groups
andIList<ItemNode> Items
) -
ItemNode
(inherits fromItem
)
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?
source to share
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();
}
}
}
source to share
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.
source to share
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
source to share