Mixing DB configuration from file with Fluent NHibernate mapping - nhibernate

I stumbled upon the following problem: I wanted to configure the DB from config file but the mappings fluently (love it!) The configuration code looks like this:
var cfg = new Configuration();
cfg.Configure();
var fluentCfg = Fluently.Configure(cfg)
.Mappings(
m => m
.FluentMapping
.AddFromAssembly(Assembly.GetExecutingAssembly));
However the config file has a property:
<property name="proxyfactory.factory_class">
NHibernate.ByteCode.LinFu.ProxyFactoryFactory,
NHibernate.ByteCode.LinFu
</property>
and after the cfg.Configure(); all looks good the configuration points to the LinFu bytecode provider BUT after the third line I see the configuration changed to using Castle. I looked in the Fluent's code and I might be wrong but it looks like they are overriding this property in PersistenceConfiguration.cs(line 50) in the constructor of PersistenceConfiguration:
values[ProxyFactoryFactoryClassKey] = DefaultProxyFactoryFactoryClassName;
Does Fluent require Castle? Or may be I am doing something wrong or maybe this is just a bug?
Thank you.

I don't know if this is what you're looking for, but it might help you out. You can expose the configuration and make any changes that you need to, in code.
var cfg = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey("ConnectionStringName")).ShowSql())
.Mappings(m =>
{
m.FluentMappings.AddFromAssemblyOf<MapMarker>();
m.FluentMappings.Conventions.AddFromAssemblyOf<ConventionMarker>();
})
.ExposeConfiguration(x => x.SetProperty("proxyfactory.factory_class", "NHibernate.ByteCode.LinFu.ProxyFactoryFactory, NHibernate.ByteCode.LinFu"));

There's a ProxyFactoryFactory method chained off Configure.
Fluently.Configure()
.ProxyFactoryFactory(name);
If you're not on 1.2, I believe it's under the Database call (see RexM's answer).

Related

Nhibernate 3.2 Mapping By Code - Version (Concurrency)

I am trying to migrate a hbm.xml based nhibernate project to mapping by code. I am having problem getting the Version section working. In the hbm.xml i have:
< version name="Version" column="Version" type="Int32" unsaved-value="0"/>
I have tried with the following mapping:
Version(x => x.Version, m =>
{
m.Column(c =>
{
c.SqlType("Int32");
c.Name("Version");
});
m.Generated(VersionGeneration.Always);
m.UnsavedValue(0);
m.Insert(true);
m.Type(new NHibernate.Type.Int32Type());
});
But nothing seems to produce the same mapping as the original hbm.xml, they all end up without the type="Int32". Has anyone got any ideas how I can do this, or if its supported in 3.2?
Cheers
Update:
See my answer
Following Toni comment I have changed my mapping to:
Version(x => x.Version, mapper =>
{
mapper.Generated(VersionGeneration.Never);
mapper.UnsavedValue(0);
mapper.Type(new NHibernate.Type.Int32Type());
});
Which prove to work as my original hbm.xml
If the property Version is already type of int32 then that is not inserted into the hbm file. I think the type part is only written into the xml file if the actual types are different. Example (domain entity uses int32 but we want to map it using int64):
// in the domain entity
public int RowVersion{get;set;}
// Mapping
this.Version(x => x.RowVersion, mapper =>
{
mapper.Generated(VersionGeneration.Never);
mapper.UnsavedValue(0);
mapper.Type(new NHibernate.Type.Int64Type());
});
// Xml file
<version name="RowVersion" type="Int64" unsaved-value="0" />

Fluent NHibernate (1.2.0.712) export mappings to HBM not working / not respecting conventions

The HBM export function in Fluent NHibernate does not seem to work.
If I call FluentMappingsContainer.ExportTo, the generated mappings come out incorrect, and I get the following exception:
FluentNHibernate.Cfg.FluentConfigurationException: An invalid or incomplete configuration was used while creating a SessionFactory. Check PotentialReasons collection, and InnerException for more detail.
My configuration code looks like this:
MsSqlConfiguration database = MsSqlConfiguration.MsSql2008
.ConnectionString(GetConnectionString())
.Cache(c => c
.UseQueryCache()
.UseSecondLevelCache()
.ProviderClass<SysCacheProvider>()
);
database.ShowSql();
FluentConfiguration config = Fluently.Configure()
.Database(database)
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<Entity>()
.Conventions.AddFromAssemblyOf<Entity>());
config.ExposeConfiguration(x =>
{
x.SetProperty("hbm2ddl.keywords", "auto-quote");
x.SetInterceptor(new ServiceInterceptor());
});
config.ExposeConfiguration(x => { x.SetProperty("current_session_context_class", "thread_static"); });
// Configure HBM export path, if configured:
var path = Service.Config.HbmExportPath;
if (!String.IsNullOrEmpty(path))
config.Mappings(m => m.FluentMappings.ExportTo(path));
// Build session factory:
_sessionFactory = config.BuildSessionFactory();
Setting HbmExportPath in my configuration to null, app launches and runs without problems. As soon as I configure the export path (causing ExportTo to be called), the generated mappings cause an exception as described above.
Looking at the exported mappings, it appears my conventions are not being applied - for example, I have a foreign-key convention in place, using camel-case and "Id" suffix, but when I export the HBM files, primary keys are consistently named with an underscore and lowercase "_id", for example:
<class xmlns="urn:nhibernate-mapping-2.2" name="MyApp.Entities.Contact, MyApp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" table="`Contact`">
...
<bag name="Departments" table="ContactDepartment">
<key>
<column name="Contact_id" />
</key>
<many-to-many class="MyApp.Entities.Department, MyApp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
<column name="Department_id" />
</many-to-many>
</bag>
...
</class>
I had this problem with the previous version and with the current release of Fluent.
Any ideas?
After drilling through the Fluent source code (latest from Git repository), something looks odd to me.
The ExportTo() methods are defined twice - once by FluentConfiguration itself, and it does appear it's exporting the configuration files "too soon", resulting in an incomplete configuration, both at run-time (resulting in the above exception) and at export-time.
Oddly, the PersistenceModel type does have the capability to export the complete configuration, but this feature is not exposed. Instead, there are at least two other seemingly broken implementations of ExportTo().
To solve the problem, we need access to the PersistenceModel instance, which has the capability to write the complete configuration - fortunately, I found a way to do that:
// create a local instance of the PersistenceModel type:
PersistenceModel model = new PersistenceModel();
FluentConfiguration config = Fluently.Configure()
.Database(database)
.Mappings(m => m.UsePersistenceModel(model) // use the local instance!
.FluentMappings.AddFromAssemblyOf<Entity>()
.Conventions.AddFromAssemblyOf<Entity>());
// ...
var path = Service.Config.HbmExportPath;
_sessionFactory = config.BuildSessionFactory(); // completes the configuration
// now write out the full mappings from the PersistenceModel:
if (!String.IsNullOrEmpty(path))
model.WriteMappingsTo(path);
The HBM files are now being output correctly!

Getting "Named Query Not Know" Error on session.GetNamedQuery()

I am consistantly getting a "Named Query Not Known" MappingException when calling session.GetNamedQuery(). I'm using Fluent with NHibernate 3.0 and I have the query in a hbm.xml file. To make things simple, i have everything in the same assembly. I've set the Build Action on the xml file to "Embedded Resource".
My Configuration looks like this:
var nhConfig = Fluently.Configure()
.Database(SQLAnywhereConfiguration
.SQLAnywhere10
.ConnectionString("uid='dba'; pwd='sql'; dsn=db"))
.ExposeConfiguration(c => c.SetProperty("current_session_context_class", "thread_static"))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<Party>())
.BuildConfiguration();
var sessionFactory = nhConfig.BuildSessionFactory();
ISession session = sessionFactory.OpenSession();
CurrentSessionContext.Bind(session);
NHibernate.IQuery q = session.GetNamedQuery("GetFirstParty");
My GetFirstParty.hbm.xml file looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<query name="GetFirstParty">
<![CDATA[from Party p where p.CaseNumber = :CaseNumber]]>
</query>
</hibernate-mapping>
What am i missing here???
Please help.
Thanks,
Mike
You need to include HBM mappings in your fluent configuration:
var nhConfig = Fluently.Configure()
.Database(SQLAnywhereConfiguration
.SQLAnywhere10
.ConnectionString("uid='dba'; pwd='sql'; dsn=db"))
.ExposeConfiguration(c => c.SetProperty(Environment.CurrentSessionContextClass, "thread_static"))
.Mappings(m =>
{
m.FluentMappings.AddFromAssemblyOf<Party>();
m.HbmMappings.AddFromAssemblyOf<Party>();
})
.BuildConfiguration();

Registering fluent nhibernate mappings for all assemblies in an application

Starting with some code:
sessionFactory = Fluently.Configure(cfg)
.Mappings(m =>
{
List<Assembly> allAssemblies = new List<Assembly>();
string path = Assembly.GetExecutingAssembly().Location;
foreach (string dll in Directory.GetFiles(path, "*.dll"))
{
m.FluentMappings.AddFromAssembly(Assembly.LoadFile(dll));
}
})
.BuildSessionFactory();
I'm very new to both nhibernate and fluent nhibernate. The bit of code above looks like it should work, but to me it looks really ugly. Is there a neater way?
One problem I have is that the code that calls the above code is in a core assembly and is unable to make reference to some of the assemblies that need mapping as their assemblies reference the core assembly. So I can't just use a few calls to AddFromAssemblyOf<T>.
Is there a cleaner way to do this?
You should be managing your SessionFactory initialization from the application itself, so your original code would work just fine.
I handle this by creating an NH config base class that does what you were originally attempting to do. I then sublcass that from within my app and do all the bootstrapping there.
You could create a custom configuration node to put in your config files.
You would have something like the following:
<configSections>
<section name="fluentConfigurationsSection" type="MyCoreAssembly.FluentConfigurationsSection, MyCoreAssembly"/>
</configSections>
<fluentConfigurationsSection>
<fluentConfigurations>
<clear />
<add name="Assembly1" assembly="MyAssemblyNotReferencedByCoreAssembly.Mapping.Fluent"
<add name="Assembly2" assembly="AnotherAssemblyNotReferencedByCoreAssembly.Mapping.Fluent"
<add name="Assembly3" assembly="OneMoreAssemblyNotReferencedByCoreAssembly.Mapping.Fluent"
</fluentConfigurations>
</fluentConfigurationsSection>
Then your code could be changed to something like:
sessionFactory = Fluently.Configure(cfg)
.Mappings(m =>
{
foreach(var config in MethodToGetFluentConfigSectionItems())
{
//load each assembly in config file
m.FluentMappings.AddFromAssembly(Assembly.Load(config.Assembly);
}
})
.BuildSessionFactory();
To create the custom config section you could see here how to do it.
Hope this helps.

Fluent NHibernate caching with automapping

I'm trying to understand how to configure Fluent NHibernate to enable 2nd-level caching for queries, entities, etc... And at the same time use automapping. There is very little information online on how to do that. Sure it can be done when mapping the classes one by one... But how about automapping?
Here is my configuration code so far:
AutoPersistenceModel model = AutoMap.AssemblyOf<Seminar>()
.Where(t => t.Namespace == "[MY NAMESPACE]")
.Conventions.Add(DefaultCascade.All());
Configuration config = Fluently.Configure()
.Database
(
MsSqlConfiguration.MsSql2005
.ConnectionString(#"[MY CONNECTION STRING]")
)
.Mappings(m => m.AutoMappings.Add(model))
.BuildConfiguration();
_sessionFactory = config.BuildSessionFactory();
Thanks!
Assuming you've already downloaded a 2nd-level cache provider from the NHibernate Contribution project, you should be able to use the following to initialize the cache within your automappings setup.
Configuration config = Fluently.Configure()
.Database
(
MsSqlConfiguration.MsSql2005
.ConnectionString(#"[MY CONNECTION STRING]")
.Cache(c => c.UseQueryCache().ProviderClass<YourCacheProviderOfChoice>())
)
.Mappings(m => m.AutoMappings.Add(model))
.BuildConfiguration();
Selecting the queries you want to cache is simply a matter of calling SetCacheable(true) on your Criteria instance.
var query = session.CreateQuery("from Blog b where b.Author = :author")
.SetString("author", "Gabriel")
.SetCacheable(true);
var list = query.List<Blog>();
This is an epic blog post on NHibernate's first and second level caches, good reference material.
I've been struggling with this for a while and was surprised how little information is out there. This question is the best I could find and even here the accepted answer doesn't say how to enable entity caching. Here's what I've found out.
To enable second level cache:
Fluently.Configure()
.Database(/* your DB config */)
.Cache(c => c.UseSecondLevelCache().ProviderClass<CacheProviderClass>())
You can use both this and query cache:
Fluently.Configure()
.Database(/* your DB config */)
.Cache(c => c.UseSecondLevelCache()
.UseQueryCache()
.ProviderClass<CacheProviderClass>())
To enable per-entity caching:
.Mappings(m => m.AutoMappings.Add(AutoMap.AssemblyOf<Entity>()
.Conventions.Add(ConventionBuilder.Class.Always(c => c.Cache.ReadWrite()))
)
)
Of course, you can use ReadOnly() or NonStrictReadWrite() if you wish.