我需要使用 NHibernate 访问数百个具有相同模式集的 SQL 服务器。每个客户都有几个不同的数据库用于不同的功能。每个客户都在 DatabaseEntity 表和一个提供初始连接的全局表中存储他们自己的数据库连接字符串。我需要将其用于服务和网站功能。加载 NHibernate 以访问这些数据库所花费的时间令人无法接受。
使用本文 https://patrickhuber.wordpress.com/2011/10/25/change-connectionstring-nhibernate/我发现使用单个 SessionFactory 和可变连接会丢失二级缓存并产生其他问题。使用本文中的示例,我还尝试缓存配置并更改连接,但是 config.BuildSessionFactory() 仍然需要几秒钟。乘以 100 个客户数据库,这是一个问题。
另一种方法是使用延迟加载,仅在收到请求时加载客户连接信息。然而,这意味着客户在首次登录时需要等待几秒钟,这是 Not Acceptable 。
另一种选择是在开始时同时加载所有数据库信息。我尝试使用 await/async 在启动时使用 NHibernate 函数(例如 ListAsync() 和 Task.WaitAll)预加载数据库信息和 SessionFactory 对象,但这似乎不适用于 NHibernate(或者我遗漏了一些东西)。
下面是一些非常简化的异步代码,它在创建 var 任务时开始同步加载信息。请对这些选项中的任何一个提出替代方案或修复建议。
// Usage
IDictionary<long, IList<DatabaseEntity>> databaseList = await LoadDatabasesAsynchronously();
// Database entity contains connection information, schema and unique SessionFactory
// One database entity per customer should return a list of database entities per customer from the DatabaseEntity table
private static async Task<IDictionary<long, IList<DatabaseEntity>>> LoadDatabasesAsynchronously()
{
// Each database entity has it's own SessionFactory
IDictionary<long, IList<DatabaseEntity>> databaseDictionary = LoadDatabasesSynchronously(); // Key = Customer ID, Value = List of database entities in customer (one each to start with)
var tasks = databaseDictionary.ToDictionary(k => k.Key, v => GetDatabases(v.Value.First())); // Load NHibernate query tasks
Task.WaitAll(tasks.Select(kvp => kvp.Value).ToArray()); // Does not reach here
return tasks.ToDictionary(k => k.Key, v => v.Value.GetAwaiter().GetResult());
}
private static async Task<IList<DatabaseEntity>> GetDatabases(DatabaseEntity database)
{
using (TransactionRequired transaction = new TransactionRequired(database.Session))
{
return await database.Session.QueryOver<DatabaseEntity>().Where(x => x.Type == someType).ListAsync();
}
}
必需的是一个 IDictionary>,它基于一个同步加载的 IDictionary> 使用 NHibernate 同时填充,它从每个客户的一个 DatabaseEntity 连接开始。
最佳答案
您可以序列化/反序列化 nhibernate 配置。反序列化可以比配置构建更快。试试这个
private bool IsConfigurationFileValid
{
get
{
var assemblies = _configurationParameters.MappingAssemblies
.Where(mapAssembly => !string.IsNullOrEmpty(mapAssembly.Location))
.ToList();
if (assemblies.Count == 0)
{
return false;
}
var serializedConfigFileInfo = new FileInfo(SerializedConfigurationFilePath);
return assemblies
.Select(mapAssembly => new FileInfo(mapAssembly.Location))
.Aggregate(true, (current, mapAssembly) => current & (mapAssembly.CreationTime <= serializedConfigFileInfo.CreationTime && mapAssembly.LastWriteTime <= serializedConfigFileInfo.LastWriteTime));
}
}
private void SaveConfigurationToFile(NHibernate.Cfg.Configuration configuration)
{
try
{
using (var file = File.Open(SerializedConfigurationFilePath, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite))
{
var bf = new BinaryFormatter();
bf.Serialize(file, configuration);
}
}
catch (Exception e)
{
_log.Fatal("Cant save serialized configuration", e);
}
}
private NHibernate.Cfg.Configuration LoadConfigurationFromFile()
{
if (!IsConfigurationFileValid)
return null;
try
{
using (var file = File.Open(SerializedConfigurationFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
var bf = new BinaryFormatter();
var configurationFromFile = bf.Deserialize(file) as NHibernate.Cfg.Configuration;
if (configurationFromFile == null)
return null;
var currentConnectionString = configurationFromFile.GetProperty(NHibernate.Cfg.Environment.ConnectionString);
return currentConnectionString == _configurationParameters.ConnectionString
? configurationFromFile
: null;
}
}
catch (Exception ex)
{
_log.Info("LoadConfigurationFromFile failed", ex);
return null;
}
}
关于c# - 在 Multi-Tenancy 环境中更快地加载 NHibernate,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56622962/