Reputation: 906
I am creating multiple session factories through code (not config file) The problem I'm having is that after the 20th session factory creation, I start getting an exception (MappingException), and I don't know why.
Regardless of the order, it goes bad after the 20th. Sessions are successfully created as long as they are in the first 20 of them.
MappingException message: Unique suffix 100_ length must be less than maximum 4 characters
any help is appreciated.
public static void AddPortfolioToConnectionstrings(string portfolio, string
connectionString)
{
var configuration = new Configuration()
.Configure()
.SessionFactoryName(portfolio)
.SetProperty("connection.connection_string", connectionString);
...
_portfolios.Add(portfolio, configuration.BuildSessionFactory());
}
Upvotes: 2
Views: 799
Reputation: 3394
As usual the error messages of NHibernate aren't helping anyone.
In my case I had this error:
MappingException message: Unique suffix 100_ length must be less than maximum 4 characters
After intensive research, the cause of this error was that the connection string had the wrong server address, and then wrong credentials supplied.
About your question: having 20 destination databases to connect to is unusual, but it can be done. With the implementation I show you below, I use 12 session factories, but make sure you run your code in a 64-bit process space, otherwise it will quickly eat up the memory available to a 32-bit process.
The only thing you need to look out for is that you need to have a custom Session factory builder that you bind as a singleton. A lightweight version of my implementation looks like this:
public interface ISessionFactoryBuilder
{
IDictionary<string, ISessionFactory> SessionFactories { get; }
}
public IDictionary<string, ISessionFactory> SessionFactories { get; private set; }
private readonly IConfigurationManager _configurationManager;
public SessionFactoryBuilder(IConfigurationManager configurationManager)
{
this._configurationManager = configurationManager;
this.SessionFactories = this.BuildSessionFactories();
}
private IDictionary<string, ISessionFactory> BuildSessionFactories()
{
var sessionFactories = new Dictionary<string, ISessionFactory>(StringComparer.InvariantCultureIgnoreCase);
var connectionStrings = this._configurationManager.GetConnectionStrings();
if (connectionStrings.Count == 0)
throw new ConfigurationErrorsException("No connection descriptions can be found!");
foreach (ConnectionStringSettings item in connectionStrings)
if (item.Name != "LocalSqlServer" && item.Name != "OraAspNetConString")
sessionFactories.Add(item.Name, this.InitializeSessionFactory(item.ConnectionString, item.ProviderName));
return sessionFactories;
}
private class Connectiontypes
{
public string Db_type { get; set; }
public FluentConfiguration Configuration { get; set; }
}
private ISessionFactory InitializeSessionFactory(string connectionString = "", string providerName = "")
{
Trace.WriteLine($"{connectionString}");
List<SessionFactoryBuilder.Connectiontypes> conntypes = new List<SessionFactoryBuilder.Connectiontypes> {
new SessionFactoryBuilder.Connectiontypes
{
Db_type = "System.Data.SqlClient",
Configuration = Fluently.Configure().Database(MsSqlConfiguration.MsSql2005.ConnectionString(connectionString).ShowSql()
.Dialect<XMsSql2005Dialect>()) },
new SessionFactoryBuilder.Connectiontypes
{
Db_type = "System.Data.OracleDataClient",
Configuration = Fluently.Configure().Database(OracleDataClientConfiguration.Oracle10
.ConnectionString(connectionString).Provider<NHibernate.Connection.DriverConnectionProvider>()
.Driver<NHibernate.Driver.OracleManagedDataClientDriver>()
.Dialect<XOracle10gDialect>().ShowSql())
},
new SessionFactoryBuilder.Connectiontypes
{
Db_type = "System.Data.MySQLDataClient", Configuration = Fluently.Configure()
.Database(MySQLConfiguration.Standard.ConnectionString(connectionString).ShowSql())
}
};
FluentConfiguration fluentConfiguration = conntypes.Find(x => x.Db_type == providerName).Configuration;
fluentConfiguration.ExposeConfiguration(x =>
{
x.SetProperty("command_timeout", "120");
});
#if DEBUG
fluentConfiguration.ExposeConfiguration(x =>
{
x.SetInterceptor(new SqlStatementInterceptor());
});
#endif
var mappings = fluentConfiguration.Mappings(m =>
{
m.FluentMappings.AddFromAssemblyOf<UsersMap>();
});
var config = mappings.BuildConfiguration();
foreach (PersistentClass persistentClass in config.ClassMappings)
{
persistentClass.DynamicUpdate = true;
}
var sessionFactory = mappings
#if DEBUG
.Diagnostics(d => d.Enable(true))
.Diagnostics(d => d.OutputToConsole())
#endif
.BuildSessionFactory();
return sessionFactory;
}
public void Dispose()
{
if (this.SessionFactories.Count > 0)
{
foreach (var item in this.SessionFactories)
{
item.Value.Close();
item.Value.Dispose();
}
this.SessionFactories = null;
}
}
}
Then I bind this with NInject as:
Bind<ISessionFactoryBuilder>().To<SessionFactoryBuilder>().InSingletonScope().WithConstructorArgument("configurationManager", context => context.Kernel.Get<IConfigurationManager>());
Of course you have to execute this anywhere near your program startup, or as we call it the Composition Root.
The only thing missing here is the obvious implementation of the IConfigurationManager, which is just my custom wrapper around ConfigurationManager, that gets passed in to the constructor of SessionFactoryBuilder
.
This way you can have your Session factory builder built at application startup and never again (obviously until the appdomain gets restarted or so), therefore you won't have memory leaks or OutOfMemory exceptions for having that many SessionFactories hanging around and getting created.
Upvotes: 3