2014-04-17 5 views
0

Я изо всех сил пытаюсь получить кэширование второго уровня и транзакции, работающие на моем сайте asp.net-mvc, и я думаю, что это связано с тем, как у меня установлена ​​настройка управления сеансами.Каков рекомендуемый способ управления сеансом nhibernate в asp.net-mvc, который поддерживает кеш-сервер второго уровня и т. Д.?

В принципе у меня есть следующие классы:

  • NhibernateRepository
  • SessionManager

и я с помощью Unity Container МОК:

this.RegisterType<IRepository, NHibernateRepository>(new PerResolveLifetimeManager()); 
this.RegisterType<ISessionManager, SessionManager>(new PerResolveLifetimeManager()); 

Класс NhibernateRepository выглядит следующим образом с свойство сеанса

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

Класс Session Manager выглядит следующим образом:

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

Кто-нибудь видел ли что-то принципиально неправильно с этим? Из googling я вижу все разные мнения о том, как вы должны настроить asp.net-mvc с nhibernate (добавление транзакции в beginRequest и фиксация на endRequest и т. Д.), Но я не могу найти канонический способ заставить его работать с кэшированием второго уровня и т. Д. что, кажется, лучшая практика, имеющие высокую масштабируемость и т.д.

Я попробовал добавлять транзакции в этот код, указанный то, что я прочитал, но я, кажется, теперь будут получать эту ошибку:

Initializing[ (one of my domain objects) #1]-Could not initialize proxy - no Session. 

, так что я вернулся, что код. В основном я надеюсь, что на данный момент существует «передовая практика» для использования любого кеша второго уровня, транзакций в asp.net-mvc. ,

ответ

2

Мне нравится использовать реализацию memory cache class, которая практически обрабатывает блокировки объектов для вас, я внедрил специальный модуль для кеша уровня 2 для nHibernate, и вы можете подключиться, выполнив небольшую конфигурацию, для чего вам нужно реализация - это интерфейс 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()) 

Я надеюсь, что помогает

+0

Я смущен, почему вы напишете свою реализацию владельца, когда есть много, что существует o в коробке. Во всяком случае, я не уверен, что вы ответили на вопрос о том, как настроить репозиторий и sessionmanager – leora