我很难在我的asp.net-mvc网站上获得二级缓存和事务,我认为这与我如何设置会话管理有关。
基本上我有以下几类:
- NhibernateRepository
- 会话管理器
我正在使用Unity IOC Container:
this.RegisterType<IRepository, NHibernateRepository>(new PerResolveLifetimeManager());
this.RegisterType<ISessionManager, SessionManager>(new PerResolveLifetimeManager());
NhibernateRepository类的Session属性如下所示
public NHibernateRepository(UserModel userModel, ISessionManager sessionManager)
{
UserModel = userModel;
SessionManager = sessionManager;
}
public ISession Session
{
get
{
using (_lock.WaitToRead())
{
if (_session != null) return _session;
}
using (_lock.WaitToWrite())
{
if (_session != null) return _session;
_session = SessionManager.GetSession(UserModel == null ? "Task" : UserModel.FullName);
return _session;
}
}
}
会话管理器类如下所示:
public class SessionManager : ISessionManager
{
private static readonly ResourceLock _lock = new OneManyResourceLock();
public static ISessionFactory Factory { get; set; }
public ISession GetSession(string userName)
{
ISession session = GetSessionFactory().OpenSession(new AuditInterceptor(userName));
return session;
}
private static ISessionFactory GetSessionFactory()
{
using (_lock.WaitToRead())
{
if (Factory != null) return Factory;
}
using (_lock.WaitToWrite())
{
if (Factory != null) return Factory;
string connectionString = ConfigurationManager.ConnectionStrings["DomainConnection"].ConnectionString;
Factory = FluentlyConfigureFactory(connectionString, false);
return Factory;
}
}
private static ISessionFactory FluentlyConfigureFactory(string connectionString, bool showSql)
{
MsSqlConfiguration databaseConfiguration = MsSqlConfiguration.MsSql2005
.ConnectionString(c => c.Is(connectionString))
.Dialect<SparcMsSqlDialect>()
.UseOuterJoin()
.UseReflectionOptimizer();
if (showSql)
{
databaseConfiguration.ShowSql();
}
databaseConfiguration.Raw("generate_statistics", showSql.ToString());
FluentConfiguration configuration = Fluently.Configure().Database(databaseConfiguration);
return configuration
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<ApplicationMap>().Conventions.Add(typeof(Conventions)))
.ExposeConfiguration(
c => {
c.SetProperty("cache.provider_class", "NHibernate.Caches.SysCache.SysCacheProvider, NHibernate.Caches.SysCache");
c.SetProperty("cache.use_second_level_cache", "true");
c.SetProperty("cache.use_query_cache", "true");
c.SetProperty("expiration", "86400");
})
.BuildSessionFactory();
}
有人认为这有什么根本性的错误吗?从谷歌搜索中,我看到了关于如何使用nhibernate设置asp.net-mvc的各种不同意见(在beginRequest中添加事务并在endRequest上提交等),但我找不到让它与二级缓存等一起工作的规范方法,这似乎是具有高可扩展性等的最佳实践
根据我所读到的内容,我尝试将交易添加到该代码中,但现在我似乎收到了这个错误:
Initializing[ (one of my domain objects) #1]-Could not initialize proxy - no Session.
所以我恢复了那个代码。基本上,我希望在这一点上有一个使用任何二级缓存的最佳实践,即asp.net-mvc.
我喜欢使用内存缓存类的实现,它实际上为您处理对象锁,我已经为nHibernate实现了一个2级缓存的自定义模块,您可以通过进行一些小配置来插入,因为您需要实现ICache接口。
public class NHibernateCache2 : ICache
{
private static readonly IInternalLogger Log = LoggerProvider.LoggerFor(typeof(NHibernateCache2));
private readonly string _region;
private string _regionPrefix;
private readonly MemoryCache _cache;
private TimeSpan _expiration;
private CacheItemPriority _priority;
// The name of the cache key used to clear the cache. All cached items depend on this key.
private readonly string _rootCacheKey;
private bool _rootCacheKeyStored;
private static readonly TimeSpan DefaultExpiration = TimeSpan.FromSeconds(300);
private static readonly string DefauktRegionPrefix = string.Empty;
private const string CacheKeyPrefix = "NHibernate-Cache:";
public NHibernateCache2():this("nhibernate", null)
{
}
public NHibernateCache2(string region):this(region, null)
{
}
/// There are two (2) configurable parameters:
/// expiration = number of seconds to wait before expiring each item
/// priority = a numeric cost of expiring each item, where 1 is a low cost, 5 is the highest, and 3 is normal. Only values 1 through 5 are valid.
/// All parameters are optional. The defaults are an expiration of 300 seconds and the default priority of 3.
public NHibernateCache2(string region, IDictionary<string, string> properties)
{
_region = region;
_cache = MemoryCache.Default;
Configure(properties);
_rootCacheKey = GenerateRootCacheKey();
StoreRootCacheKey();
}
/// Defines property in order to get the region for the NHibernate's Cache.
public string Region
{
get { return _region; }
}
/// Obtains a expiration value that indicates the time in seconds after which an object is automatically
/// evicted from the cache.
public TimeSpan Expiration
{
get { return _expiration; }
}
/// Obtains a priority value that indicates the likelihood that an object of that region evicts
/// another already cached object of a lower priority region.
public CacheItemPriority Priority
{
get { return _priority; }
}
private void Configure(IDictionary<string, string> props)
{
if (props == null)
{
if (Log.IsWarnEnabled)
{
Log.Warn("configuring cache with default values");
}
_expiration = DefaultExpiration;
_priority = CacheItemPriority.Default;
_regionPrefix = DefauktRegionPrefix;
}
else
{
_priority = GetPriority(props);
_expiration = GetExpiration(props);
_regionPrefix = GetRegionPrefix(props);
}
}
private static string GetRegionPrefix(IDictionary<string, string> props)
{
string result;
if (props.TryGetValue("regionPrefix", out result))
{
Log.DebugFormat("new regionPrefix :{0}", result);
}
else
{
result = DefauktRegionPrefix;
Log.Debug("no regionPrefix value given, using defaults");
}
return result;
}
private static TimeSpan GetExpiration(IDictionary<string, string> props)
{
TimeSpan result = DefaultExpiration;
string expirationString;
if (!props.TryGetValue("expiration", out expirationString))
{
props.TryGetValue(NHibernate.Cfg.Environment.CacheDefaultExpiration, out expirationString);
}
if (expirationString != null)
{
try
{
int seconds = Convert.ToInt32(expirationString);
result = TimeSpan.FromSeconds(seconds);
Log.Debug("new expiration value: " + seconds);
}
catch (Exception ex)
{
Log.Error("error parsing expiration value");
throw new ArgumentException("could not parse 'expiration' as a number of seconds", ex);
}
}
else
{
if (Log.IsDebugEnabled)
{
Log.Debug("no expiration value given, using defaults");
}
}
return result;
}
private static CacheItemPriority GetPriority(IDictionary<string, string> props)
{
CacheItemPriority result = CacheItemPriority.Default;
string priorityString;
if (props.TryGetValue("priority", out priorityString))
{
result = ConvertCacheItemPriorityFromXmlString(priorityString);
if (Log.IsDebugEnabled)
{
Log.Debug("new priority: " + result);
}
}
return result;
}
private static CacheItemPriority ConvertCacheItemPriorityFromXmlString(string priorityString)
{
if (string.IsNullOrEmpty(priorityString))
{
return CacheItemPriority.Default;
}
var ps = priorityString.Trim().ToLowerInvariant();
if (ps.Length == 1 && char.IsDigit(priorityString, 0))
{
// the priority is specified as a number
int priorityAsInt = int.Parse(ps);
if (priorityAsInt >= 1 && priorityAsInt <= 6)
{
return (CacheItemPriority)priorityAsInt;
}
}
else
{
/// change for your own priority settings
switch (ps)
{
case "abovenormal":
return CacheItemPriority.Default;
case "belownormal":
return CacheItemPriority.Default;
case "default":
return CacheItemPriority.Default;
case "high":
return CacheItemPriority.Default;
case "low":
return CacheItemPriority.Default;
case "normal":
return CacheItemPriority.Default;
case "notremovable":
return CacheItemPriority.NotRemovable;
}
}
Log.Error("priority value out of range: " + priorityString);
throw new IndexOutOfRangeException("Priority must be a valid System.Web.Caching.CacheItemPriority; was: " + priorityString);
}
private string GetCacheKey(object key)
{
return String.Concat(CacheKeyPrefix, _regionPrefix, _region, ":", key.ToString(), "@", key.GetHashCode());
}
/// Gets an object that exist in the second level cache of NHibernate by the specified key.
///A unique identifier for the cache entry to get.
///Returns an entry from the NHibernate's Cache.
public object Get(object key)
{
if (key == null)
{
return null;
}
string cacheKey = GetCacheKey(key);
if (Log.IsDebugEnabled)
{
Log.Debug(String.Format("Fetching object '{0}' from the cache.", cacheKey));
}
object obj = _cache.Get(cacheKey);
if (obj == null)
{
return null;
}
var de = (DictionaryEntry)obj;
if (key.Equals(de.Key))
{
return de.Value;
}
else
{
return null;
}
}
/// Adds a specific object inside the in the second level cache of NHibernate by using its key and its content.
/// A key value of an item from the second level cache of NHibernate.
/// Data for an entry of second level cache of NHibernate.
public void Put(object key, object value)
{
if (key == null)
{
throw new ArgumentNullException("key", "null key not allowed");
}
if (value == null)
{
throw new ArgumentNullException("value", "null value not allowed");
}
string cacheKey = GetCacheKey(key);
if (_cache[cacheKey] != null)
{
if (Log.IsDebugEnabled)
{
Log.Debug(String.Format("updating value of key '{0}' to '{1}'.", cacheKey, value));
}
// Remove the key to re-add it again below
_cache.Remove(cacheKey);
}
else
{
if (Log.IsDebugEnabled)
{
Log.Debug(String.Format("adding new data: key={0}&value={1}", cacheKey, value));
}
}
if (!_rootCacheKeyStored)
{
StoreRootCacheKey();
}
var cacheItemPolicy = new CacheItemPolicy()
{
AbsoluteExpiration = DateTime.Now.Add(_expiration),
SlidingExpiration = ObjectCache.NoSlidingExpiration,
Priority = _priority,
};
cacheItemPolicy.ChangeMonitors.Add(_cache.CreateCacheEntryChangeMonitor(new[] { _rootCacheKey }));
_cache.Add(
cacheKey,
new DictionaryEntry(key, value),
cacheItemPolicy);
}
/// Removes a cache entry from second level cache of NHibernate by a key.
/// A key value of an item from second level cache of NHibernate.
public void Remove(object key)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
string cacheKey = GetCacheKey(key);
if (Log.IsDebugEnabled)
{
Log.Debug("removing item with key: " + cacheKey);
}
_cache.Remove(cacheKey);
}
/// Removes an object/item from second level cache of NHibernate.
public void Clear()
{
RemoveRootCacheKey();
StoreRootCacheKey();
}
/// Generate a unique root key for all cache items to be dependant upon
private string GenerateRootCacheKey()
{
return GetCacheKey(Guid.NewGuid());
}
private void RootCacheItemRemoved(CacheEntryRemovedArguments arguments)
{
_rootCacheKeyStored = false;
}
private void StoreRootCacheKey()
{
_rootCacheKeyStored = true;
var policy = new CacheItemPolicy
{
AbsoluteExpiration = ObjectCache.InfiniteAbsoluteExpiration,
SlidingExpiration = ObjectCache.NoSlidingExpiration,
Priority = CacheItemPriority.Default,
RemovedCallback = RootCacheItemRemoved
};
_cache.Add(
_rootCacheKey,
_rootCacheKey,
policy);
}
private void RemoveRootCacheKey()
{
_cache.Remove(_rootCacheKey);
}
/// Clears the second level cache of NHibernate.
public void Destroy()
{
Clear();
}
public void Lock(object key)
{
// Do nothing
}
public void Unlock(object key)
{
// Do nothing
}
/// Obtains the next timestamp value.
public long NextTimestamp()
{
return Timestamper.Next();
}
/// Defines property in order to get the sliding expiration time for the second level cache of NHibernate.
public int Timeout
{
get { return Timestamper.OneMs * 60000; } // 60 seconds
}
/// Retrieves the name of NHibernate second level cache region.
public string RegionName
{
get { return _region; }
}
}
然后您需要定义一个ICacheProvider实现:
public class NHibernateCacheProvider2 : ICacheProvider
{
private static readonly Dictionary<string, ICache> Caches;
private static readonly IInternalLogger Log;
static NHibernateCacheProvider2()
{
Log = LoggerProvider.LoggerFor(typeof(NHibernateCacheProvider2));
Caches = new Dictionary<string, ICache>();
}
/// Builds a new SysCache through the region and a collection of properties.
/// regionName: The name of the cache region.
/// properties: Configuration settings.
/// returns A new instance of NHibernateCache by using a region of the cache.
public ICache BuildCache(string regionName, IDictionary<string, string> properties)
{
if (regionName == null)
{
regionName = string.Empty;
}
ICache result;
if (Caches.TryGetValue(regionName, out result))
{
return result;
}
// create cache
if (properties == null)
{
properties = new Dictionary<string, string>(1);
}
if (Log.IsDebugEnabled)
{
var sb = new StringBuilder();
sb.Append("building cache with region: ").Append(regionName).Append(", properties: ");
foreach (KeyValuePair<string, string> de in properties)
{
sb.Append("name=");
sb.Append(de.Key);
sb.Append("&value=");
sb.Append(de.Value);
sb.Append(";");
}
Log.Debug(sb.ToString());
}
return new NHibernateCache2(regionName, properties);
}
public long NextTimestamp()
{
return Timestamper.Next();
}
public void Start(IDictionary<string, string> properties) { //your impl it's not necessary }
public void Stop() { }
}
如果您正在使用fluent nhibernate,您可以使用以下配置进行注册:
Fluently.Configure().Database(MsSqlConfiguration.MsSql2008.
ConnectionString(builder => builder.FromConnectionStringWithKey(connectionStringKey)))
.ExposeConfiguration(c =>{c.SetProperty("show_sql", "true");}).
Cache(builder =>builder.ProviderClass<NHibernateCacheProvider2().
UseSecondLevelCache().UseQueryCache())
我希望这能帮助