FluentNHibernate Automapping not generating mappings - fluent-nhibernate

I'm attempting to use Fluent NHibernate auto mappings for the first time. It appears that the code I'm using isnt generating any mappings. It has been pretty much copy-pasted from the Auto mapping wiki page.
var mappings = AutoMap
.AssemblyOf<MvcBugs.Model.Project>();
mappings.WriteMappingsTo("c:\\temp\\mappings");
var sessionFactory = Fluently.Configure()
.Mappings(m => m.AutoMappings.Add(mappings))
.Database(SQLiteConfiguration.Standard.InMemory())
.ExposeConfiguration(c => { new SchemaExport(c)
.SetOutputFile("c:\\temp\\schema.sql")
.Create(false, true); })
.BuildSessionFactory();
(also would someone create an auto-mapping tag, I'm too new or something)

this appears to be a bug. Removing the line:
mappings.WriteMappingsTo("c:\\temp\\mappings");
Makes the mappings get set up correctly.

Related

Fluent Nhibernate - ClassMaps in multiple, separate assemblies

Consider the following Fluent configuration;
FluentConfiguration config = Fluently.Configure();
config.Database(FluentNHibernate.Cfg.Db.MsSqlConfiguration.MsSql2008.ConnectionString(ConfigurationManager.ConnectionStrings[dbKey].ConnectionString));
// MemberMap is in the same assembly as this class, contains
// maps for member and role entities as part of a default
// membership service provider
MappingAssemblies.Add(typeof(MemberMap).Assembly);
foreach (Assembly mappingAssembly in MappingAssemblies)
{
// For each assembly that has been added to MappingAssemblies
// we add to the current mapping configuration. This allows
// me to drop this helper into any solution and it provide
// standardized membership abilities AS WELL AS manage
// the solutions unique ClassMaps
config.Mappings(m =>
m.FluentMappings.AddFromAssembly(mappingAssembly)
);
}
if (exportSchema)
{
config.ExposeConfiguration(cfg =>
{
new SchemaExport(cfg)
.Create(true, true);
}
);
}
_sessionFactory = config.BuildSessionFactory();
This logic is held within a static class I call from within my Global.asax on application startup. The startup configuration will look something similar to;
Database.MappingAssemblies.Add(typeof(PageContentMap).Assembly);
// This is the method detailed above
Database.FluentConfigureSessionFactory("MySolutionsDb", true);
So the idea is that I have packaged my Member and Role entity objects into the same assembly as the Database helper object so that any solution I care to create can instantly gain my standardized membership abilities as well as being able to simply create its own solution specific ClassMaps and add them to the configuration object.
The issue is, the familiar call to;
config.Mappings(m =>
m.FluentMappings.AddFromAssembly(mappingAssembly)
);
only seems to be able to deal with a single assembly. Doesn't matter what is added to the list, only the last assembly added will be mapped. As an alternative to the above, I have tried holding a reference to MappingConfiguration (which is what 'm' stands for in the config.Mappings(m => ) ) but this did not work either. It is obvious that such a call to m.FluentMappings.AddFromAssembly or indeed any of the FluentMappings.Add methods will overwrite what is previously there but surely there is a way of getting this done ? It doesn't seem like that much of a 'weird' requirement.
Old question, but I managed to solve it after looking at this, so I'll try to answer it. This is how I did (the .ForEach() is an extension from NHibernate.Linq):
config.Mappings(m => MappingAssemblies.ForEach(a => m.FluentMappings.AddFromAssembly(a)))
I had to do it for auto-mapped stuff as well, and there the syntax is a bit different. I have an interface that marks all classes I want to automap:
config.Mappings(m =>
m.AutoMappings.Add(AutoMap.Assemblies(MappingAssemblies.ToArray())
.Where(x => x.GetInterfaces().Contains(typeof(IAutoMappedEntity)))))
Also, I don't have a "MappingAssemblies" that I set manually, I took the lazy approach of just including all my assemblies, so my config looks like this (using SQLite, this is from a test project):
var MappingAssemblies = AppDomain.CurrentDomain.GetAssemblies()
.Where(a => a.FullName.StartsWith("MyCompanyName."));
Configuration configuration;
var sessionFactory = Fluently.Configure()
.Database(SQLiteConfiguration.Standard.InMemory())
// This adds fluent mappings
.Mappings(m => MappingAssemblies.ForEach(a => m.FluentMappings.AddFromAssembly(a)))
// This adds automapped classes using specific configuration
.Mappings(m => m.AutoMappings.Add(AutoMap.Assemblies(new MyAutomapConfiguration(), MappingAssemblies)))
// This adds automapped classes that just use my magic interface
.Mappings(m => m.AutoMappings.Add(AutoMap.Assemblies(MappingAssemblies.ToArray()).Where(x => x.GetInterfaces().Contains(typeof(IAutoMappedEntity)))))
.ExposeConfiguration(cfg => configuration = cfg)
.BuildSessionFactory();

property filter with fluent nHibernate automapping

i'm trying to create a filter, using fluent nH (1.2) automapping with nH 2.1.2.
I've followed the example here, but I keep getting the exception:
filter-def for filter named 'DateFilter' was never used to filter classes nor collections..
the filter class:
public class DateFilter : FilterDefinition
{
public DateFilter()
{
WithName(Consts.FilterConsts.DATE_FILTER)
.AddParameter("date", NHibernate.NHibernateUtil.DateTime)
.WithCondition("DATEPART(dayofyear,EntityTime) = DATEPART(dayofyear, :date)")
;
}
}
and in the mapping override:
mapping.HasMany(x => x.Stuff)
.LazyLoad()
.ReadOnly()
.ApplyFilter<DateFilter>();
here's my configuration code.
Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.DefaultSchema("dbo") //set default schema to enable full-qualified queries
.AdoNetBatchSize(batchSize > 0 ? batchSize : 1)
.UseReflectionOptimizer()
.ConnectionString(c => c.FromConnectionStringWithKey(connectionStringKey))
.Cache(c => c.UseQueryCache()
.ProviderClass(
isWeb ? typeof(NHibernate.Caches.SysCache2.SysCacheProvider).AssemblyQualifiedName //in web environment- use sysCache2
: typeof(NHibernate.Cache.HashtableCacheProvider).AssemblyQualifiedName //in dev environmet- use stupid cache
))
)
.Mappings(m => m.AutoMappings.Add(
AutoMap.AssemblyOf<Domain.Entity>(cfg) //automapping the domain entities
.IncludeBase<Domain.SomethingBase>() //ensure that although SomethingBase is a base class, map it as well. this enables us to store all Something sub-classes in the same table
.IncludeBase<Domain.OrOtherBase>() //create a table for the abstract 'OrOtherBase' class
.UseOverridesFromAssemblyOf<MappingOverrides.MappingOverride>()
.Conventions.Add(DefaultCascade.All()) //make sure that all saves are cascaded (i.e when we save a zone, its queues are saved as well)
.Conventions.AddFromAssemblyOf<IdGenerationWithHiLoConvention>()
))
.Mappings(m => m.FluentMappings.Add(typeof(DateFilter)));
if I move the line before the automapping part, I get the exception:
NHibernate.MappingException: filter-def for filter named 'DateFilter' was not found.
can anybody tell me what I'm doing wrong?
OK, so I figured this out. When you add the mappings separately like that, they end up in different mappings and it will either complain that you never use the filter or complain that it can't find the filter, because it's not looking both places. The solution is to add it directly to the automap, so in your case like:
//other stuff up here
.Mappings(m => m.AutoMappings.Add(() => {
var a = AutoMap.AssemblyOf<Domain.Entity>(cfg)
.IncludeBase<Domain.SomethingBase>() //and also cascades and conventions and stuff
a.Add(typeof(DateFilter));
return a;
}));
Kinda gross because .Add() isn't fluent, but it does work.

How to export hbm xml files using s#arparchitecture with fluent mappings

This question was asked before but the answers all show how to export the hbm files from fluentnhibernate. We are using S#arpArchitecture which wraps fluent. I am able to export the schema but what I really want is the xml files to troubleshoot errors. I've done this using FNH before but adding S#arp to the mix has complicated things where I cannot figure it out.
I've found this question asked on several forums, but I can't find one that shows how to get the mapping files.
Here is how I do it in one of my projects:
[TestMethod]
public void CreateSchema()
{
var mappingOutput = ConfigurationManager.AppSettings["xmlMappingOutputDirectory"];
var sqlOutput = ConfigurationManager.AppSettings["sqlOutputDirectory"];
Configuration cfg = new Configuration().Configure();
var persistenceModel = new PersistenceModel();
persistenceModel.AddMappingsFromAssembly(Assembly.Load("ProjectName.Data"));
persistenceModel.Configure(cfg);
persistenceModel.WriteMappingsTo(mappingOutput);
new SchemaExport(cfg).SetOutputFile(sqlOutput).Create(true, false);
}
You will need to set the two keys in the your app config or provide values directly for them.
http://wiki.fluentnhibernate.org/Fluent_configuration#Exporting_mappings
In the Mappings call, you can do the following:
.Mappings(m =>
{
m.FluentMappings
.AddFromAssemblyOf<YourEntity>()
.ExportTo(#"C:\your\export\path");
m.AutoMappings
.Add(/* ... */)
.ExportTo(#"C:\your\export\path");
})
As it turns out that only works if you're not using automapping. Here's the solution if you're using automapping:
public void CanGenerateMappingFiles()
{
DirectoryInfo directoryInfo = new DirectoryInfo("../../../../db/mappings");
if (!directoryInfo.Exists)
directoryInfo.Create();
Configuration cfg = new Configuration().Configure();
var autoPersistenceodel = new AutoPersistenceModelGenerator().Generate();
autoPersistenceodel.Configure(cfg);
autoPersistenceodel.AddMappingsFromAssembly(Assembly.Load("TrackerI9.Data"));
autoPersistenceodel.WriteMappingsTo(directoryInfo.FullName);
}
You'll have to make sure that your configuration is set up correctly and that you choose an appropriate location for the directory, but otherwise this should work. It did for me.

Custom naming conventions in Fluent NHibernate AutoMapping

according to this Post it is possible to change the naming convention from "[TableName]_id" to "[TableName]ID". However when I saw the code, I wasn't able to do it with my rather new (about 6 weeks old) version of Fluent NHibernate.
Original code:
var cfg = new Configuration().Configure();
var persistenceModel = new PersistenceModel();
persistenceModel.addMappingsFromAssembly(Assembly.Load("Examinetics.NHibernate.Data"));
persistenceModel.Conventions.GetForeignKeyNameOfParent = type => type.Name + "ID";
persistenceModel.Configure(cfg);
factory = cfg.BuildSessionFactory();
I came up with this:
PersistenceModel model = new PersistenceModel();
model.AddMappingsFromAssembly(assemblyType.Assembly);
model.ConventionFinder.Add(
ConventionBuilder.Reference.Always(part
=> part.ColumnName(part.EntityType.Name + part.Property.Name)));
configuration.ExposeConfiguration(model.Configure);
return configuration.BuildConfiguration();
but this gives me the original Table, because EntityType is not the referenced Entity and I see no property to get the "parent" entity.
How can I do this?
See The answer about removing underscores in Ids using Fluent NHibernate Automapping conventions here.
A portion of the text, the tables and classes are in the link.
Solution:
Add a convention, it can be system wide, or more restricted.
ForeignKey.EndsWith("Id")
Code example:
var cfg = new StoreConfiguration();
var sessionFactory = Fluently.Configure()
.Database(/* database config */)
.Mappings(m =>
m.AutoMappings.Add(
AutoMap.AssemblyOf<Product>(cfg)
.Conventions.Setup(c =>
{
c.Add(ForeignKey.EndsWith("Id"));
}
)
.BuildSessionFactory();
Now it will automap the ShelfId column to the Shelf property in Product.

Combine Fluent and XML mapping for NHibnernate

I just fell in love with NHibernate and the fluent interface. The latter enables very nice mappings with refactoring support (no more need for xml files).
But nobody is perfect, so I am missing the many-to-any mapping in fluent. Does anybody know if it is already there? If so, a simple line of code would be nice.
But to stick to the header of the question, is there any way to combine fluent and normal NHibernate mapping.
Currently I use the following lines for my test setup WITH fluent, and the second code block for my test WITHOUT fluent (with XML mappings). How can I tell fluent to use fluent IF AVAILABLE and XML otherwise...
var cfg = new Configuration();
cfg.AddProperties(MsSqlConfiguration.MsSql2005.ConnectionString.Is(_testConnectionstring).ToProperties());
cfg.AddMappingsFromAssembly(typeof(CatMap).Assembly);
new SchemaExport(cfg).Create(true, true);
var persistenceModel = new PersistenceModel();
persistenceModel.addMappingsFromAssembly(typeof(CatMap).Assembly);
IDictionary<string, string> properties = MsSqlConfiguration.MsSql2005.UseOuterJoin().ShowSql().ConnectionString.Is(_testConnectionstring).ToProperties();
properties.Add("command_timeout", "340");
session = new SessionSource(properties, persistenceModel).CreateSession();
Without Fluent...
config = new Configuration();
IDictionary props = new Hashtable();
props["connection.provider"] = "NHibernate.Connection.DriverConnectionProvider";
props["dialect"] = "NHibernate.Dialect.MsSql2005Dialect";
props["connection.driver_class"] = "NHibernate.Driver.SqlClientDriver";
props["connection.connection_string"] = "Server=localhost;initial catalog=Debug;Integrated Security=SSPI";
props["show_sql"] = "true";
foreach (DictionaryEntry de in props)
{
config.SetProperty(de.Key.ToString(), de.Value.ToString());
}
config.AddAssembly(typeof(CatMap).Assembly);
SchemaExport se = new SchemaExport(config);
se.Create(true, true);
factory = config.BuildSessionFactory();
session = factory.OpenSession();
That's it...
Chris
PS: I really like this site, the GUI is perfect, and the quality of all articles is incredible. I think it will be huge :-) Have to register...
ManyToAny's currently aren't implemented (as of time of writing).
Regarding your setup for fluent and non-fluent mappings, you're almost there with your first example.
var cfg = MsSqlConfiguration.MsSql2005
.ConnectionString.Is(_testConnectionstring)
.ConfigureProperties(new Configuration());
cfg.AddMappingsFromAssembly(typeof(CatMap).Assembly); // loads hbm.xml files
var model = new PersistenceModel();
model.addMappingsFromAssembly(typeof(CatMap).Assembly); // loads fluent mappings
mode.Configure(cfg);
new SchemaExport(cfg).Create(true, true);
The main difference is that the SchemaExport is last. I assume your first example was actually loading the fluent mappings, but it'd already created the schema by that point.
You can do exactly what you want to do entirely within Fluent NHibernate.
The following code will use Fluent NHibernate syntax to fluently configure a session factory that looks for HBM (xml) mapping files, fluent mappings, and conventions from multiple possible assemblies.
var _mappingAssemblies = new Assembly[] { typeof(CatMap).Assembly };
var _autoPersistenceModel = CreateAutoPersistenceModel();
Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005.ConnectionString(_testConnectionstring))
.Mappings(m =>
{
foreach (var assembly in _mappingAssemblies)
{
m.HbmMappings.AddFromAssembly(assembly);
m.FluentMappings.AddFromAssembly(assembly)
.Conventions.AddAssembly(assembly);
}
m.AutoMappings.Add(_autoPersistenceModel );
})
.ExposeConfiguration(c => c.SetProperty("command_timeout", "340"))
.BuildSessionFactory();
There are many other options available to you as well: Fluent NHibernate Database Configuration
Mapping from Foo to Baa:
HasManyToMany< Baa > ( x => Baas )
.AsBag ( ) //can also be .AsSet()
.WithTableName ( "foobar" )
.WithParentKeyColumn ( "fooId" )
.WithChildKeyColumn ( "barId" ) ;
Check out the examples in ClassMapXmlCreationTester - they also show what the default column names are.