Fluent NHibernate - override table names - fluent-nhibernate

Application has many extension assemblies and they contain mappings for their classes. I need to add prefix to all (base, join, many-to-many, ...) table names in those mappings.
E.g.
Assembly: ~/bin/Extensions/Foo.dll
Original table: Page
New table: Ext_Foo_Page
Assembly: ~/bin/Extensions/Bar.dll
Original table: Page
New table: Ext_Bar_Page
What's the most straightforward way to do that?
I tried this
public class TableNameConvention : IClassConvention, IJoinedSubclassConvention, IHasManyToManyConvention
{
private string getPrefix()
{
return "Ext_Test_";
}
public void Apply(FluentNHibernate.Conventions.Instances.IClassInstance instance)
{
instance.Table(getPrefix() + instance.TableName);
}
public void Apply(FluentNHibernate.Conventions.Instances.IJoinedSubclassInstance instance)
{
instance.Table(getPrefix() + instance.TableName);
}
public void Apply(FluentNHibernate.Conventions.Instances.IManyToManyCollectionInstance instance)
{
instance.Table(getPrefix() + instance.TableName);
}
}
but it doesn't change table names even though those methods are executed.
EDIT - The configuration
var sb = new StringBuilder();
var sw = new StringWriter(sb);
var cfg = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(b => b.Server(#".\SQLEXPRESS").Database("test123").Username("sa").Password("...")))
.Mappings(m => m.FluentMappings.AddFromAssembly(assembly).Conventions.Add<TableNameConvention>().ExportTo(sw))
.ExposeConfiguration(c => { new SchemaUpdate(c).Execute(false, true); })
.BuildSessionFactory();
var xml = sb.ToString();

with FNH 1.2.0.712
it works with
var model = new PersistenceModel();
model.Add(typeof(EntityMap));
model.Conventions.Add<TableNameConvention>();
model.WriteMappingsTo(Console.Out);
but not with
m.FluentMappings.Add(typeof(EntityMap)).Conventions.Add<TableNameConvention>().ExportTo(Console.Out)
it's not called in the second example, maybe bug. but the following works
.Mappings(m =>
{
m.AutoMappings.Add(() => new AutoPersistenceModel().Conventions.Add<TableNameConvention>());
m.FluentMappings.Add(typeof(EntityMap)).ExportTo(Console.Out);
})

The following syntax works for me:
return Fluently.Configure()
.Database(...)
.Mappings(m =>
{
m.AutoMappings.Add(
AutoMap.AssemblyOf<EntityMap>(new ImporterAutomappingConfiguration())
.Conventions.Add<TableNameConvention>());
})
.BuildSessionFactory();

Related

NHibernate Fluent Add external Assembly mappings

I have a project with my mappings and entities stored in other class libraries and NHibernate layers in another project. In my testing project I would like to add these mapping via fluently configure... Mappings... via assebly and not individually. In my code below you can see I added just one entity.. But I would like to configure it to scan my other assemblies. I am sure I am just missing the obvious here.. any pointers would be greatly appreciated...
[Test]
public void Can_generate_schemaFluently()
{
var cfg = new Configuration();
cfg.Configure();
Configuration configuration = null;
ISessionFactory SessionFactory = null;
ISession session = null;
SessionFactory = Fluently.Configure(cfg)
*** WOULD LIKE TO ADD MY ASSEBLIES and autoscan for objects instead ***
.Mappings(m => m.FluentMappings
.Add(typeof(StudentEOMap))
)
.ExposeConfiguration(x => configuration = x)
.BuildSessionFactory();
session = SessionFactory.OpenSession();
object id;
using (var tx = session.BeginTransaction())
{
var result = session.Get<StudentEO>(1541057);
tx.Commit();
Assert.AreEqual(result.StudId, 1541057);
}
session.Close();
}
AutoMapping
If you want to filter through types, you can use the IAutomappingConfiguration and derive from DefaultAutomappingConfiguration like this:
public class StandardConfiguration : DefaultAutomappingConfiguration
{
public override bool ShouldMap(Type type)
{
// Entity is the base type of all entities
return typeof(Entity).IsAssignableFrom(type);
}
}
You can also use DefaultAutomappingConfiguration if you have no need to filter. But my further example uses the StandardConfiguration.
Change your configuration like this, to populate your types to FluentNHibernate:
SessionFactory = Fluently.Configure(cfg)
.Mappings(m => MapMyTypes(m))
.ExposeConfiguration(x => configuration = x)
.BuildSessionFactory();
And the MapMyTypes method should look like this:
private void MapMyTypes(MappingConfiguration m)
{
m.AutoMappings.Add(AutoMap.Assemblies(new StandardConfiguration(),
Assembly.GetAssembly(typeof(Entity)),
Assembly.GetAssembly(typeof(OtherAssemblyEntity)))
);
}
You can add multiple Assemblies and all get filtered through the StandardConfiguration.
Edit
FluentMappings
It seems that i misread your question. To add mappings you can use a similar method to achieve that but without a IAutomappingConfiguration.
Just change the MapMyTypes method to:
private void MapMyTypes(MappingConfiguration m)
{
m.FluentMappings.AddFromAssembly(Assembly.GetAssembly(typeof(EntityMap)));
}
Combine
You can also combine the FluentMapping and the AutoMapping like this:
private Action<MappingConfiguration> MapMyTypes()
{
return m =>
{
MapFluent(m);
MapAuto(m);
};
}

Servicestack NHibernate Auth Repo No CurrentSessionContext configured

I have the following configuration:
_container = new WindsorContainer ();
var factory = new SessionFactoryManager().CreateSessionFactory();
_container.Register(Component.For<NHibernate.ISessionFactory>().Instance(factory));
And then elsewhere I have:
var authRepo = new NHibernateUserAuthRepository (_container.Resolve<NHibernate.ISessionFactory>());
_container.Register (Component.For<IAuthRepository>().Instance(authRepo));
public class SessionFactoryManager
{
public ISessionFactory CreateSessionFactory()
{
try {
var autoMap = AutoMap.AssemblyOf<Artist>()
.Where(t => typeof(Entity).IsAssignableFrom(t))
.UseOverridesFromAssemblyOf<LocationMappingOverride>();
return Fluently.Configure()
.Database(PostgreSQLConfiguration.PostgreSQL82.ConnectionString(c => c.FromConnectionStringWithKey("ConnectionString")).AdoNetBatchSize(50))
.Mappings(m => m.AutoMappings.Add(autoMap))
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<ServiceStack.Authentication.NHibernate.UserAuthMap>())
.ExposeConfiguration(TreatConfiguration)
.BuildSessionFactory();
} catch (Exception ex) {
var m = ex;
}
return null;
}
protected virtual void TreatConfiguration(NHibernate.Cfg.Configuration configuration)
{
configuration.SetProperty("generate_statistics", "true");
configuration.SetProperty("current_session_context_class", "thread");
var update = new SchemaUpdate(configuration);
update.Execute(false, true);
}
}
This all works for the rest of my app but when i try to use anything to do with the NH auth repo I get:
No CurrentSessionContext configured (set the property current_session_context_class)!
Anyone got any clues wha gwarn?
[UPDATE]
In my AppHost.Configure method I have added the following:
this.GlobalRequestFilters.Add ((req, res, vm) => {
CurrentSessionContext.Bind(container.Resolve<NHibernate.ISession>());
});
To no avail - I also have no idea how i would dispose of that :p
Also I am confused as ISessionFactory is injected in like every other part of my app:
https://github.com/ServiceStack/ServiceStack/blob/master/src/ServiceStack.Authentication.NHibernate/NHibernateUserAuthRepository.cs#L20
So surely that should just work?
Yes, you need to explicitly bind the current session to the context:
CurrentSessionContext.Bind(session);
After you built the session. Don't forget to dispose of it too when you no longer need it.

Interceptors with StructureMap and Fluent NHibernate

I have successfully configured StrctureMap and FluentNHibernate to work together with a parameter-less constructor. What I need to do now is set the interceptor with parameters.
The following code below works well in my MVC application:
protected void Application_Start()
{
//Code removed from here to save space...
ObjectFactory.Initialize(x => {
x.For<IInterceptor>().Singleton().Use(context => new MyInterceptor());
x.For<ISessionFactory>().Singleton().Use(context => CreateSessionFactory(context.GetInstance<IInterceptor>()));
x.For<ISession>().HttpContextScoped().Use(context => context.GetInstance<ISessionFactory>().OpenSession());
x.For<IAuditDao>().Use<AuditDao>().Ctor<ISessionFactory>().Is(context => context.GetInstance<ISessionFactory>());
});
}
public static ISessionFactory CreateSessionFactory(IInterceptor interceptor)
{
return Fluently.Configure()
.Database(MySQLConfiguration
.Standard
.ConnectionString(c => c.FromConnectionStringWithKey("MySqlConnectionString")))
.Mappings(m => { m.FluentMappings.Add<AuditItemMap>(); })
.ExposeConfiguration(config => new SchemaUpdate(config).Execute(true, true))
.ExposeConfiguration(config => config.SetInterceptor(interceptor))
.BuildSessionFactory();
}
What I now need to do is pass an instance of AuditDao into the the interceptor. At the moment 'MyInterceptor' which inherits from the NHibernate 'EmptyInterceptor' has two constructors:
public class MyInterceptor : EmptyInterceptor
{
private IAuditDao AuditDao;
public MyInterceptor()
{
}
public MyInterceptor(IAuditDao auditDao)
{
AuditDao = auditDao;
}
}
Now obviously an instance of AuditDao needs an instance of SessionFactory and SessionFactory would need an instance of AuditDao.
How do I solve this circular reference problem?

AutoMapping with FluentMapping doesn't quite seem to work for me

I'm a n00b. Here's what I want to do:
Use AutoMapping to configure every property between the model -> table. Then I would like to override 2 specific items in a fluent map. The two items are: Id & Table name.
So my Maps look like this:
public class BillMasterMap : ClassMap<BillMaster>
{
public BillMasterMap()
{
Table("BILLMAST");
Id(x => x.SYSKEY);
}
}
And my factory settings look like this:
public static ISessionFactory SessionFactory(string connectionString)
{
if (_sessionFactory == null)
{
_sessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005.ShowSql()
.ConnectionString(c => c.Is(connectionString)).Cache(c => c.UseQueryCache().ProviderClass<HashtableCacheProvider>()))
.Mappings(m =>
m.AutoMappings.Add(AutoMap.AssemblyOf<BillMaster>()
.Where(x => x.Namespace.EndsWith("Entities"))))
.Mappings(m =>
m.FluentMappings.AddFromAssemblyOf<BillInvoiceMap>())
.BuildSessionFactory();
}
return _sessionFactory;
}
The problem is that FNH finds the Id override for BillMaster, but not for BillInvoice which looks like this (identical it seems)
public class BillInvoiceMap : ClassMap<BillInvoice>
{
public BillInvoiceMap()
{
Id(x => x.SYSKEY);
Table("BILLINV");
}
}
I've gotten around the problem by configuring my automapping with the Setup() as shown below:
.Mappings(m =>
m.AutoMappings.Add(AutoMap.AssemblyOf<BillMaster>()
.Setup(s => s.FindIdentity = property => property.Name == "SYSKEY")
.Where(x => x.Namespace.EndsWith("Entities"))))
But I would like to combine auto & fluent as other tables don't use "SYSKEY" as their Id column.
Thoughts? Is this a FNH bug?
Got it working.
1.) I have to setup AutoMapping with the Setup method I described above
2.) Additionally I have to setup fluentmappings with the Id method
when both are setup like this, then it works for me.
So
public static ISessionFactory SessionFactory(string connectionString)
{
if (_sessionFactory == null)
{
_sessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005.ShowSql()
.ConnectionString(c => c.Is(connectionString)).Cache(
c => c.UseQueryCache().ProviderClass<HashtableCacheProvider>()))
.Mappings(m =>
{
m.FluentMappings.AddFromAssemblyOf<BaseEntity>();
m.AutoMappings.Add(AutoMap.AssemblyOf<BaseEntity>()
.Setup(s => s.FindIdentity = property => property.Name == "SYSKEY")
.Where(x => x.Namespace.EndsWith("Entities")));
})
.BuildSessionFactory();
}
return _sessionFactory;
}
And the map
public class BillInvoiceMap : ClassMap<BillInvoice>
{
public BillInvoiceMap()
{
Table("BILLINV");
Id(x => x.SYSKEY);
}
}

Fluent NHibernate and PostgreSQL, SchemaMetadataUpdater.QuoteTableAndColumns - System.NotSupportedException: Specified method is not supported

I'm using fluentnhibernate with PostgreSQL. Fluentnhibernate is last version. PosrgreSQL version is 8.4.
My code for create ISessionFactory:
public static ISessionFactory CreateSessionFactory()
{
string connectionString = ConfigurationManager.ConnectionStrings["PostgreConnectionString"].ConnectionString;
IPersistenceConfigurer config = PostgreSQLConfiguration.PostgreSQL82.ConnectionString(connectionString);
FluentConfiguration configuration = Fluently
.Configure()
.Database(config)
.Mappings(m =>
m.FluentMappings.Add(typeof(ResourceMap))
.Add(typeof(TaskMap))
.Add(typeof(PluginMap)));
var nhibConfig = configuration.BuildConfiguration();
SchemaMetadataUpdater.QuoteTableAndColumns(nhibConfig);
return configuration.BuildSessionFactory();
}
When I'm execute code at line SchemaMetadataUpdater.QuoteTableAndColumns(nhibConfig); throw error: System.NotSupportedException: Specified method is not supported. Help me, please! I'm very need for solution.
Best regards
Try this:
public static ISessionFactory CreateSessionFactory()
{
string connectionString = ConfigurationManager.ConnectionStrings["PostgreConnectionString"].ConnectionString;
IPersistenceConfigurer config = PostgreSQLConfiguration.PostgreSQL82.ConnectionString(connectionString);
FluentConfiguration configuration = Fluently
.Configure()
.Database(config)
.Mappings(m =>
m.FluentMappings.Add(typeof(ResourceMap))
.Add(typeof(TaskMap))
.Add(typeof(PluginMap)));
configuration.ExposeConfiguration(x => x.SetProperty("hbm2ddl.keywords", "auto-quote"));
return configuration.BuildSessionFactory();
}
SchemaMetadataUpdater.QuoteTableAndColumns(nhibConfig);
configuration.ExposeConfiguration(x => x.SetProperty("hbm2ddl.keywords", "auto-quote"));
I tried above both. It didn't work for me with latest Fluent NHibernate(5f7adcd) and latest postgresql 8.4. Those two are probably on mute by Fluent NHibernate. If you use
NHibernate and HBM without Fluent, it'd worked for you.
In order to explicitly ask Fluent NHibernate to generate Quoted Identifier for table and column,
I monkey patched two files in Fluent NHibernate source to force it to work for postgresql.
(If you don't need the same build for other databases)
namespace: FluentNHibernate.MappingModel.Output
Add "Quote" to table name at XmlClassWriter.cs
if (classMapping.HasValue(x => x.TableName))
classElement.WithAtt("table", new System.Text.StringBuilder().Append("\"").Append(classMapping.TableName).Append("\"").ToString());
Add "Quote" to column name at XmlColumnWriter.cs
if (columnMapping.HasValue(x => x.Name))
element.WithAtt("name", new System.Text.StringBuilder().Append("\"").Append(columnMapping.Name).Append("\"").ToString());
This works like charm so far. Get source at http://github.com/jagregory/fluent-nhibernate
and build your own with above updates.
Create your custom naming convention, override the column name convention to include quote.
var fluentConfig = Fluently.Configure(new Configuration().SetNamingStrategy(PostgreNamingStragegy.Instance))
internal class PostgreNamingStragegy: INamingStrategy
{
private static readonly INamingStrategy ImprovedNamingStrategy = NHibernate.Cfg.ImprovedNamingStrategy.Instance;
private static PostgreNamingStragegy_postgreNamingStrategy;
public static INamingStrategy Instance
{
get { return _postgreNamingStrategy?? (_postgreNamingStrategy= new PostgreNamingStragegy()); }
}
protected PostgreNamingStragegy()
{
}
public string ClassToTableName(string className)
{
return ImprovedNamingStrategy.ClassToTableName(className);
}
public string ColumnName(string columnName)
{
return "\"" + columnName + "\"";
}
public string LogicalColumnName(string columnName, string propertyName)
{
return ImprovedNamingStrategy.LogicalColumnName(columnName, propertyName);
}
public string PropertyToColumnName(string propertyName)
{
return ImprovedNamingStrategy.PropertyToColumnName(propertyName);
}
public string PropertyToTableName(string className, string propertyName)
{
return ImprovedNamingStrategy.PropertyToTableName(className, propertyName);
}
public string TableName(string tableName)
{
return ImprovedNamingStrategy.TableName(tableName);
}
}