Search code examples
c#nhibernatefluent-nhibernatenhibernate-criteria

Retrieving Navigation Properties with NHibernate Criteria


I think I'm just searching for the wrong terms, since I can't seem to find an answer to what I'm fairly sure is a simple question.

I have two classes/mappings (simplified);

public class Applications
{
    public int Id {get; set;}
    public string Name {get; set;}
    public ApplicationType {get; set;}
    public IEnumerable<ApplicationProperty> ApplicationProperties {get; set;}
}

public class ApplicationProperties
{
    public int Id {get; set;}
    public int Application_Id {get; set}
    public string Value {get; set;}
}

I'm trying to build up a Criteria class in a (possibly misguided) attempt to make our code more readable/reusable, as follows;

public static class ApplicationsQuery
{
    public static ICriteria GetQuery(ISession session)
    {
        return session.CreateCriteria(typeof(Application));
    }

    public static ICriteria WithType(this ICriteria crit, ApplicationType type)
    {
        crit.Add(
            Restrictions.Eq(
                Projections.ProjectionList().Add(
                    Projections.Property<Application>(a => a.ApplicationType)), type));
        return crit;
    }

    public static ICriteria WithProperties(this ICriteria crit)
    {
        // What goes here?!
    }
}

So that I can do something like

ICriteria query = ApplicationsQuery.GetQuery(session).WithType(ApplicationType.GameServer).WithProperties();

I've tried various things such as;

 DetachedCriteria properties =
            DetachedCriteria.For<Application>()
                .SetProjection(Projections.Property<Application>(a => a.ApplicationProperties));
 return crit.Add(Subqueries.Select(properties));
 // Or this
 return crit.SetFetchMode("ApplicationProperties", FetchMode.Eager);

But I'm unable to get the ApplicationProperties to be populated.
My test setup looks like this;

session.Save(new Application("Test Application 1", ApplicationType.GameServer), 1);
session.Save(new Application("Test Application 2", ApplicationType.Manager), , 2);
session.Save(new ApplicationProperty(1, "Test Property"), 1);

EDIT Adding in the mappings as I get the feeling that there might be an issue with them.

public class ApplicationMapping : ClassMap<Application>
{
    public ApplicationMapping()
    {
        Table("Applications");
        Id(o => o.Id, "Application_Id").GeneratedBy.Assigned();
        Map(o => o.Name).Column("Logical_Name");
        Map(o => o.ApplicationType).Column("Application_Type").CustomType<ApplicationType>();

        HasMany(o => o.ApplicationProperties).KeyColumn("Application_Id").ReadOnly().Cascase.None();
    }
}

public class ApplicationPropertyMapping : ClassMap<ApplicationProperty>
{
    public ApplicationPropertyMapping()
    {
        Table("Application_Properties");
        Id(o => o.Id).GeneratedBy.Identity().Column("Property_Id");
        Map(o => o.ApplicationId).Column("Application_Id");
        Map(o => o.Value).Column("Property_Value");
    }
}

Solution

  • Update: i stripped down the code

    public class ApplicationsQueryBuilder
    {
        private static readonly IProjection ApplicationTypeProperty = Projections.Property<Application>(a => a.ApplicationType);
    
        private readonly DetachedCriteria _query = DetachedCriteria.For<Application>();
        private bool _withProperties;
        private bool _filteredByCollection;
    
        public ApplicationsQueryBuilder WithType(ApplicationType type)
        {
            _query.Add(Restrictions.Eq(ApplicationTypeProperty, type));
            return this;
        }
    
        public ApplicationsQueryBuilder WithTypes(params ApplicationType[] types)
        {
            var or = Restrictions.Disjunction();
            foreach (var type in types)
            {
                or.Add(Restrictions.Eq(ApplicationTypeProperty, type));
            }
    
            _query.Add(or);
            return this;
        }
    
        public ApplicationsQueryBuilder WithProperties()
        {
            _withProperties = true;
            return this;
        }
    
        public ApplicationsQueryBuilder WithProperty(string name)
        {
            _query.CreateCriteria("ApplicationProperties")
                .Add(Restrictions.Eq("Name", name));
            _filteredByCollection = true;
            return this;
        }
        ...
    
        public ICriteria Create(ISession session)
        {
            if (_withProperties && _filteredByCollection)
            {
                _query.SetProjection(Projections.Id());
                return session.CreateCriteria<Application>()
                    .SetFetchMode("ApplicationProperties", FetchMode.Eager);
                    .Add(Subqueries.PropertyIn("Id", _query));
            }
            else if (_withProperties)
            {
                return _query.GetExecutableCriteria(_session)
                    .SetFetchMode("ApplicationProperties", FetchMode.Eager);
            }
            else
            {
                return _query.GetExecutableCriteria(session);
            }
        }
    }