1
votes

I have an issue with Fluent NHibernate where automapping isn't picking up the entitites in my web project DLL. Normally I store all entities in a separate assembly and this has always worked. However, this project is rather small so I'm trying to keep it all in one project. However, when I call AutoMap.AssemblyOf<MyEntityType>(), no mappings are created. I'm wondering if this is because the entity lives in the web project assembly which gets loaded from Temporary ASP.NET files folder and not the actual folder where the project lives on disk. Is it a permissions issue or something? I'm not sure where to start debugging...

Example entity:

namespace MyProject.Entities 
{
    public class Letter : EntityBase
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Company { get; set; }
        public string Address1 { get; set; }
        public string Address2 { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string Zip { get; set; }
        public string Country { get; set; }
        public string Interest { get; set; }
        public string Section1 { get; set; }
        public string Section2 { get; set; }
        public string Section3 { get; set; }
        public string LetterText { get; set; }
        public int StepNumber { get; set; }
    }
}    

Relevant boostrap code:

   private static ISessionFactory GetSessionFactory()
    {
        var database = MsSqlConfiguration.MsSql2005
            .ConnectionString(Configuration.ConnectionString)
            .DefaultSchema(DEFAULT_SCHEMA)
            .AdoNetBatchSize(BATCH_SIZE);

        var mappings = AutoMap.AssemblyOf<Letter>()
            .Where(x => x.GetType() == typeof(Letter))
            .Conventions.Add
            (
                ConventionBuilder.Id.Always(x =>
                    x.GeneratedBy.HiLo(HILO_TABLE, HILO_COLUMN, HILO_MAX_LO)),
                ConventionBuilder.HasMany.Always(x => x.Cascade.AllDeleteOrphan()),
                Table.Is(o => Inflector.Pluralize(o.EntityType.Name)),
                PrimaryKey.Name.Is(o => "Id"),
                ForeignKey.EndsWith("Id"),
                DefaultLazy.Never(),
                DefaultCascade.All()
            );

        // ...

I changed the Where clause to look for the specific type instead of the namespace, but that didn't work either. the mappings object still ends up empty.

Also, the EntityBase class is an empty class but for one property 'Id' which is inherited by all entities.

EDIT: I moved the entities to their own assembly and am still having the problem, so it's not related to the location of the web project assembly. I'm still quite lost on this one. :(

4
Can you give an example of one of your entities and the code you're using to create the configuration/session factory?Daniel T.
I added the code exampleChris
What happens if you add virtual to all the properties? NHibernate requires all properties to be virtual.Daniel T.
The virtual keyword is only required if lazy loading, but I've set the convention to never lazy load. I'll try it and get back with you though.Chris
Yeah setting all props to virtual did nothingChris

4 Answers

2
votes

I found the problem. I was inheriting from EntityBase but did not have the IgnoreBase call. I assumed since EntityBase was not in the namespace I was qualifying for the mappings that I did not need it, but apparently, if you don't explicitly ignore the base class, even if it's it another namespace, the mapping will fail.

New bootstrap code looks like:

private static ISessionFactory GetSessionFactory()
{
    var database = MsSqlConfiguration.MsSql2005
        .ConnectionString(Configuration.ConnectionString)
        .DefaultSchema(DEFAULT_SCHEMA)
        .AdoNetBatchSize(BATCH_SIZE);

    var mappings = AutoMap.AssemblyOf<Letter>()
        .Where(x => x.GetType() == typeof(Letter))
        .IgnoreBase<EntityBase>()
        .Conventions.Add
        (
            ConventionBuilder.Id.Always(x =>
                x.GeneratedBy.HiLo(HILO_TABLE, HILO_COLUMN, HILO_MAX_LO)),
            ConventionBuilder.HasMany.Always(x => x.Cascade.AllDeleteOrphan()),
            Table.Is(o => Inflector.Pluralize(o.EntityType.Name)),
            PrimaryKey.Name.Is(o => "Id"),
            ForeignKey.EndsWith("Id"),
            DefaultLazy.Never(),
            DefaultCascade.All()
        );

    // ...
0
votes

Try this and let me know what happens:

var config = Fluently.Configure()
    .Mappings(m => m.AutoMappings.Add(AutoMap.AssemblyOf<Letter>()
        .Where(n => n.Name == "Letter"))
        .ExportTo(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Desktop) + "/mappings"))
    .BuildConfiguration();

var factory = config.BuildSessionFactory();

My completely uninformed guess is that you're not calling BuildConfiguration(), so NHibernate's not bothering to create the mappings.

0
votes

I created a new ASP.NET MVC application and added these two classes and this configuration:

public abstract class Entity
{
    public int Id { get; set; }
}

public class Letter
{
    public string Name { get; set; }
}

// this is in Global.asax
protected void Application_Start()
{
    Fluently.Configure()
        .Database(SQLiteConfiguration.Standard.InMemory())
        .Mappings(m => m.AutoMappings.Add(AutoMap.AssemblyOf<Entity>()
            .Where(t => t.IsSubclassOf(typeof (Entity))))
            .ExportTo(*the desktop*))
        .BuildConfiguration();
}

This code was able to export the mappings successfully.

Edit:

Just saw your answer. Not sure why putting IgnoreBase() fixed the problem. That should only determine how the base class should be mapped for the subclass strategy. Oh well.

0
votes

I've found that the domain classes need to be in a separate assembly than the code that builds the ISessionFactory. Once you move your domain objects into a separate assembly everything should work just fine.

Out of curiosity, I tried the Conventions.Add idea from above and it never works. Meaning, I can take a domain class such as this:

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

With an AutoMapping routine such as that below:

public NHibernate.ISessionFactory Create()
        {
            var persistenceModel =
                AutoMap
                    .AssemblyOf<Person>();

            var ret =
                Fluently
                    .Configure()
                    .Database(MsSqlCeConfiguration.Standard.ShowSql().FormatSql().ConnectionString(ex => ex.FromConnectionStringWithKey("PersonSqlCe")))
                    .Mappings(x => x.AutoMappings.Add(persistenceModel))
                    .ExposeConfiguration(config =>
                        {
                            new SchemaExport(config).Create(true, true);

                            // DOC: workaround for identity column failures in SQLCE
                            config.SetProperty("connection.release_mode", "on_close");
                        })
                    .BuildSessionFactory();

            return ret;
        }

And everything works just fine. HOWEVER, when I change my domain class to look like this:

public class Person
    {
        public virtual int BAD { get; private set; }
        public virtual string FirstName { get; set; }
        public virtual string LastName { get; set; }
    }

And change my AutoMapping routine to comply with the new PrimaryKey property name expectation such as the code below:

public NHibernate.ISessionFactory Create()
        {
            var persistenceModel =
                AutoMap
                    .AssemblyOf<Person>();

            persistenceModel.Conventions.Add(PrimaryKey.Name.Is(x => "BAD"));

            var ret =
                Fluently
                    .Configure()
                    .Database(MsSqlCeConfiguration.Standard.ShowSql().FormatSql().ConnectionString(ex => ex.FromConnectionStringWithKey("PersonSqlCe")))
                    .Mappings(x => x.AutoMappings.Add(persistenceModel))
                    .ExposeConfiguration(config =>
                        {
                            new SchemaExport(config).Create(true, true);

                            // DOC: workaround for identity column failures in SQLCE
                            config.SetProperty("connection.release_mode", "on_close");
                        })
                    .BuildSessionFactory();

            return ret;
        }

... I get errors such as those below:

  ----> FluentNHibernate.Visitors.ValidationException : The entity 'Person' doesn't have an Id mapped. Use the Id method to map your identity property. For example: Id(x => x.Id).
    at FluentNHibernate.Cfg.FluentConfiguration.BuildSessionFactory()
    SampleImplementation.cs(28,0): at NHQS.Tests.PersonSampleSessionFactoryCreator.Create()
    SessionFactoryTests.cs(16,0): at

What gives? Seems the convention approach isn't wiring up?