NHibernate Fluent Add external Assembly mappings - nhibernate

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);
};
}

Related

Fluent NHibernate table naming convention not working

I have the following convention which I load into my FNH config
public class TableNameConvention : IClassConvention, IClassConventionAcceptance
{
public void Accept(IAcceptanceCriteria<IClassInspector> criteria)
{
criteria.Expect(x => x.TableName, Is.Not.Set);
}
public void Apply(IClassInstance instance)
{
var tableName = instance.EntityType.Name.Pluralise();
instance.Table(tableName);
}
}
I do not specify table names on any of my mappings, yet this convention is not applied. I'm using Fluent NHibernate 1.4.1.1. Can anyone spot anything I might have done wrong?
UPDATE
The conventions are loaded in the following manner:
public static NHibernate.Cfg.Configuration BuildConfiguration()
{
var connectionStringName = "mydb";
return Fluently.Configure(new NHibernate.Cfg.Configuration())
.Database(MsSqlConfiguration
.MsSql2008.ConnectionString(c => c.FromConnectionStringWithKey(connectionStringName))
.Dialect<MsSql2008Dialect>()
.AdoNetBatchSize(50))
.Mappings(m =>
{
m.FluentMappings.AddFromAssemblyOf<Profile>();
m.FluentMappings.Conventions.Add(DefaultLazy.Always(), DynamicUpdate.AlwaysTrue(), DynamicInsert.AlwaysTrue());
m.FluentMappings.Conventions.AddFromAssemblyOf<HiLoConvention>();
})
.ExposeConfiguration(config => config.SetProperty(NHibernate.Cfg.Environment.CurrentSessionContextClass, typeof(ManagedWebSessionContext).AssemblyQualifiedName))
.ExposeConfiguration(HiLoConvention.CreateScript)
.ExposeConfiguration(RunSchemaUpdate)
.BuildConfiguration();
}
All conventions sit in the same assembly and namespace as the HiLoConvention referenced above in the .AddFromAssembly() method call.
UPDATE 2:
The problem is in the Accept() method, because if I remove this method (and also the IClassConventionAcceptance interface from the class declaration) then the convention is applied. I have also tried this expectation to no avail
criteria.Expect(x => string.IsNullOrEmpty(x.TableName))
The original code worked with Fluent 1.2.1...
This question is old, but perhaps this can help someone else:
I assume you wanted to set the convention on each entity, unless a table name was specified explicitly in the map. So to achieve that, you can simply do the following:
public class TableNameConvention : IClassConvention
{
public void Apply(IClassInstance instance)
{
var tableName = instance.EntityType.Name.Pluralise();
instance.Table(tableName);
}
}
This will apply the convention on all entities, unless TableName was specified explicitly in the map.
Have you tried
m.FluentMappings.ConventionDiscovery.AddFromAssemblyOf<HiLoConvention>()
in place of
m.FluentMappings.Conventions.AddFromAssemblyOf<HiLoConvention>()

Fluent NHibernate - override table names

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();

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);
}
}

Id property not populated

I have an identity mapping like so:
Id(x => x.GuidId).Column("GuidId")
.GeneratedBy.GuidComb().UnsavedValue(Guid.Empty);
When I retrieve an object from the database, the GuidId property of my object is Guid.Empty, not the actual Guid (the property in the class is of type System.Guid). However, all of the other properties in the object are populated just fine.
The database field's data type (SQL Server 2005) is uniqueidentifier, and marked as RowGuid.
The application that is connecting to the database is a VB.NET Web Site project (not a "Web Application" or "MVC Web Application" - just a regular "Web Site" project). I open the NHibernate session through a custom HttpModule. Here is the HttpModule:
public class NHibernateModule : System.Web.IHttpModule
{
public static ISessionFactory SessionFactory;
public static ISession Session;
private static FluentConfiguration Configuration;
static NHibernateModule() {
if (Configuration == null) {
string connectionString = cfg.ConfigurationManager.ConnectionStrings["myDatabase"].ConnectionString;
Configuration = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005.ConnectionString(cs => cs.Is(connectionString)))
.ExposeConfiguration(c => c.Properties.Add("current_session_context_class", "web"))
.Mappings(x => x.FluentMappings.AddFromAssemblyOf<LeadMap>().ExportTo("C:\\Mappings"));
}
SessionFactory = Configuration.BuildSessionFactory();
}
public void Init(HttpApplication context) {
context.BeginRequest += delegate {
Session = SessionFactory.OpenSession();
CurrentSessionContext.Bind(Session);
};
context.EndRequest += delegate {
CurrentSessionContext.Unbind(SessionFactory);
};
}
public void Dispose() {
Session.Dispose();
}
}
The strangest part of all, is that from my unit test project, the GuidId property is returned as I would expect. I even rigged it to go for the exact row in the exact database as the web site was hitting. The only differences I can think of between the two projects are
The unit test project is in C#
Something with the way the session is managed between the HttpModule and my unit tests
The configuration for the unit tests is as follows:
Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005.ConnectionString(cs => cs.Is(connectionString)))
.Mappings(x => x.FluentMappings.AddFromAssemblyOf<LeadDetailMap>());
I am fresh out of ideas. Any help would be greatly appreciated.
Thanks
I had a similar problem when in the mapping attribute Name was not set. You can try to save your fluent mappings into Xmls and check them.

NHibernate questions - modifying this example for Fluent NHibernate

I'm new to NHibernate...
I have been following this NHibernate Tutorial from Gabriel Schenker :
http://nhforge.org/wikis/howtonh/your-first-nhibernate-based-application.aspx
However, this tutorial uses hbm files. I would like to know - what do I need to do to modify the hepler class below (which creates a session factory) so that it uses my ClassMap files instead of hbm?
Also, is this the best way to deal with factory creation? How often will the factory be created in this example - once per request? (I'm not really sure I understand the lifetime of _sessionFactory in this case).
Thank you!
public class NHibernateHelper
{
private static ISessionFactory _sessionFactory;
private static ISessionFactory SessionFactory
{
get
{
if(_sessionFactory == null)
{
var configuration = new Configuration();
configuration.Configure();
configuration.AddAssembly(typeof(Product).Assembly);
_sessionFactory = configuration.BuildSessionFactory();
}
return _sessionFactory;
}
}
public static ISession OpenSession()
{
return SessionFactory.OpenSession();
}
}
Session factory usually should only be created once (using the singleton pattern) for the lifetime of the app.
And here is sample code for creating the SessionFactory with Fluent Nhibernate:
var mssqlConfig = MsSqlConfiguration
.MsSql2008
.ConnectionString(c => c.Is(connectionstring))
.UseOuterJoin()
.ProxyFactoryFactory("NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle");
var sessionFactory = Fluently.Configure()
.Database(mssqlConfig)
.Mappings(m => m.FluentMappings.AddFromAssembly(typeof(Product).Assembly))
.BuildSessionFactory();
Not using Fluent Config (from the top of my head, syntax might not be exact):
var config = new NHibernate.Cfg.Configuration().Configure();
var model = new PersistenceModel();
model.Configure(config);
model.AddMappingsFromAssembly(typeof(Product).Assembly);
var sessionFactory = config.BuildSessionFactory();