views:

200

answers:

2

I am trying to figure out what I thought was just a simple one to many mapping using fluent Nhibernate. I hoping someone can point me to the right directory to achieve this one to many relations I have an articles table and a categories table Many Articles can only belong to one Category Now my Categores table has 4 Categories and Articles has one article associated with cateory1

here is my setup.

using FluentNHibernate.Mapping;
using System.Collections;
using System.Collections.Generic;

namespace FluentMapping
{
    public class Article
    {

        public virtual int Id { get; private set; }
        public virtual string Title { get; set; }
        public virtual Category Category{get;set;}
    }
    public class Category
    {
        public virtual int Id { get; private set; }
        public virtual string Description { get; set; }
        public virtual IList<Article> Articles { get; set; }
        public Category()
        {
            Articles=new List<Article>();
        }

        public virtual void AddArticle(Article article)
        {
            article.Category = this;
            Articles.Add(article);
        }
        public virtual void RemoveArticle(Article article)
        {
            Articles.Remove(article);
        }

    }
    public class ArticleMap:ClassMap<Article>
    {
        public ArticleMap()
        {
            Table("Articles");
            Id(x => x.Id).GeneratedBy.Identity();
            Map(x => x.Title);
            References(x => x.Category).Column("CategoryId").LazyLoad();

        }
        public class CategoryMap:ClassMap<Category>
        {
            public CategoryMap()
            {
                Table("Categories");
                Id(x => x.Id).GeneratedBy.Identity();
                Map(x => x.Description);
                HasMany(x => x.Articles).KeyColumn("CategoryId").Fetch.Join();
            }
        }
    }
}

if I run this test

[Fact]
    public void Can_Get_Categories()
    {
        using (var session = SessionManager.Instance.Current)
        {
            using (var transaction = session.BeginTransaction())
            {
                var categories = session.CreateCriteria(typeof(Category))
                              //.CreateCriteria("Articles").Add(NHibernate.Criterion.Restrictions.EqProperty("Category", "Id"))                                
                .AddOrder(Order.Asc("Description"))
                              .List<Category>();

            }
        }
    }

I am getting 7 Categories due to Left outer join used by Nhibernate any idea what I am doing wrong in here? Thanks [Solution] After a couple of hours reading nhibernate docs I here is what I came up with

var criteria = session.CreateCriteria(typeof (Category));
                    criteria.AddOrder(Order.Asc("Description"));
                    criteria.SetResultTransformer(new DistinctRootEntityResultTransformer());
var cats1 = criteria.List<Category>();

Using Nhibernate linq provider

 var linq = session.Linq<Category>();
                    linq.QueryOptions.RegisterCustomAction(c => c.SetResultTransformer(new DistinctRootEntityResultTransformer()));
                    var cats2 = linq.ToList();
A: 

I don't really know what's the problem, because I don't know how you save the categories, but it might be caused by using the wrong cascade setting in the mapping?

Paco
I have no problem in saving any of the entitiesThe problem is selecting the CategoriesI have a four articles with categoryId off 1 "Finanace"when I execute the test case I am getting the Category "Finance" returned multiple times. what I am trying to get is the four Categories and have access to the Articles referencing them.
Sammy
Found solution, see Question's Edit for 2 samples implementations
Sammy
See the edits to my answer. Hope this helps.
Jamie Ide
A: 

Using Join on a HasMany is unusual; it's typically used on References, the many side of a one-to-many relationship. Instead of the solution you came up with, you should lazy load the collection or use Fetch.Select. Both will cause NH to issue two selects, one to load the Category and another to load its associated Articles.

Addendum:

The error you're getting is pretty straight-forward: the collection can't be loaded because the ISession that was used to load the parent is out of scope (or its connection was closed). Setting the fetch mode to Select will resolve this (I think, I haven't tried it). So your collection mapping would be:

HasMany(x => x.Articles).KeyColumn("CategoryId").Fetch.Select();

If you can keep the ISession open I would recommend lazy loading:

HasMany(x => x.Articles).KeyColumn("CategoryId").LazyLoad();

It's unusual to use Join on a collection mapping due to the problem you ran into. Issuing a join from the one side will return a parent object for each object in the collection, just as it would in SQL.

Jamie Ide
James,what you recommended would be a good solution if I the Articles List is not used. when I try to use it to check the count I get this exception when using Fetch.Select or LoazyLoad.Initializing[IMB.Domain.Entities.Category#4]-failed to lazily initialize a collection of role: IMB.Domain.Entities.Category.Articles, no session or session was closedMy initial idea was query the database to get the Categories and each Article's count in every Category.Can you elaborate on "Using Join on a HasMany is unusual;", I used type of mapping thinking it was the norm from the examples I have seen.
Sammy