在支持二级缓存/事务等的asp.net-mvc中进行nhibernate会话管理的推荐方法是什么



我很难在我的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())

我希望这能帮助

最新更新