How can I use Fluent NHibernate Automapping with multiple Lists of the same type in an Entity?
Asked Answered
C

1

6

It appears that NHibernate cannot automap more than one IList of a given type in an entity.

Consider the following two entities (based on the Examples.FirstProject sample code that is included with the Fluent NHibernate source code).

public class Employee
{
    public virtual int Id { get; private set; }
    public virtual string FirstName { get; set; }
    public virtual string LastName { get; set; }
}

public class Store
{
    public virtual int Id { get; private set; }
    public virtual IList<Employee> Staff { get; set; }
    public virtual IList<Employee> Managers { get; set; }
}

This seems to be a perfectly valid object model - each store has several staff employees and several manager employees.

But when I automap, the Staff and Managers lists are stored in the Employee table,all with the same foreign key.

Employee Table

Id FirstName LastName Store_id 
3  Daisy     Harrison   1 
4  Jack      Torrance   1 
5  Sue       Walkters   1 
6  Tom       Tommorow   1 
7  Dick      Diggler    1 

The net result is that when the data is read back out of the database, both Staff and Managers lists are populated with every row in the table.

This looks like a bug in Automapping to me, but I'm fairly new to NHibernate in any form, and don't fully know it's limitations yet.

In any case, how can I make NHibernate treat the two lists as distinct?

If possible, I'd appreciate an Automapping code fragment that directly addresses the sample code I've provided (e.g. something like "put this exact override in the .Mappings section of your CreateSessionFactory").

This is because I'm only somewhat familiar with Automapping, and not at all familiar with the older ways of doing things, which means I can't "fill in the blanks" very well yet.

But if you only have time to point me in the right direction, that would be helpful too.

Here's my CreateSessionFactory code, to give some context:

    private static ISessionFactory CreateSessionFactory()
    {
        ISessionFactory sessionFactory = null;

        const string autoMapExportDir = "AutoMapExport";
        if( !Directory.Exists(autoMapExportDir) )
            Directory.CreateDirectory(autoMapExportDir);

        try
        {
            var autoPersistenceModel = 
                AutoMap.AssemblyOf<Product>()
                        .Where(t => t.Namespace == "Examples.FirstProject.Entities")
                        .Conventions.Add( DefaultCascade.All() )
                ;

            sessionFactory = Fluently.Configure()
                .Database(SQLiteConfiguration.Standard
                              .UsingFile(DbFile)
                              .ShowSql()
                         )
                .Mappings(m => m.AutoMappings.Add(autoPersistenceModel)
                                             .ExportTo(autoMapExportDir)
                         )
                .ExposeConfiguration(BuildSchema)
                .BuildSessionFactory()
                ;
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }

        return sessionFactory;
    }
Crudity answered 20/11, 2009 at 21:7 Comment(0)
C
5

Paul Batum answered my question here, and provided a standalone working example here (click the Download button after you navigate to the linked page).

The following code is copied from his answer. The key point is in the StoreMap class at the end of the listing, which sets up an override with a Where clause that uses the IsManager property in Employee.

Note that (at least with v. 1.0.0.594) there is one big gotcha with Automapping - the mapping class (e.g. StoreMap) cannot be in the same Namespace as the domain class (e.g. Store)!

Otherwise, NHibernate will throw "NHibernate.MappingException: (XmlDocument)(2,4): XML validation error: ..." , with absolutely no indication of what or where the real problem is.

This is probably a bug that may be fixed in later versions of Fluent NHibernate.

public class Employee 
{ 
    public virtual int Id { get; private set; } 
    public virtual string FirstName { get; set; } 
    public virtual string LastName { get; set; } 
    public virtual bool IsManager { get; set; } 
} 


public class Store 
{ 
    public virtual int Id { get; private set; } 
    public virtual IList<Employee> Staff { get; private set; } 
    public virtual IList<Employee> Managers { get; private set; } 


    public Store() 
    { 
        Staff = new List<Employee>(); 
        Managers = new List<Employee>(); 
    } 


    public void AddManager(Employee employee) 
    { 
        employee.IsManager = true; 
        this.Managers.Add(employee); 
    } 


    public void AddStaff(Employee employee) 
    { 
        this.Staff.Add(employee); 
    } 


} 

Here is the mapping override for store:

// Must be in different Namespace from class Store!!!
public class StoreMap : IAutoMappingOverride<Store> 
{ 
   public void Override(AutoMapping<Store> mapping) 
   { 
       mapping.HasMany(x => x.Managers) 
           .Cascade.All() 
           .Where("(IsManager = 1)"); 
       mapping.HasMany(x => x.Staff) 
           .Cascade.All() 
           .Where("(IsManager = 0)"); 
   } 
} 
Crudity answered 25/11, 2009 at 21:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.