2017-02-22 91 views
1

Я бы очень признателен за некоторые советы, чтобы помочь мне с кэшированием объектов. Вот мои потребности: я хочу фильтровать некоторые действия моих контроллеров в соответствии с ролями пользователей, хранящимися в базе данных SQL.MVC.Net - Кэширование пользовательского объекта для предоставления данных для приложения (запросить подведение итогов)

Что я сейчас делаю: когда выполняется первый запрос текущего пользователя, я генерирую объект с некоторыми свойствами, инициализированными SQL-запросами. Я храню этот объект в сеансе с помощью настраиваемого поставщика, и я использую собственный поставщик роли для обработки тегов фильтров авторизации. Если сессия истекает пользователь regenarated .. Это что-то вроде этого (упрощенно):

класса пользователя

Public class User 

    public property Login as string 
    public property IsAdmin as boolean 

    public sub Init(byval pLogin as string) 
     Login = pLogin 
     //Do some logic on database to provides roles.... 
     IsAdmin = dbReturnIsAdmin 
    end sub 

    public readonly property RolesList as string() 
     Get 
      Return New String() {If(IsAdmin, "UserIsAdmin", "")} 
     End get 
    end property 

End class 

поставщика пользовательского сеанса

Public Class SessionProvider 

    Private Const SESSION_USER As String = "SESSION_USER" 

    Public Shared Sub ReloadUser() 
     //'This instruction initiate user and load roles into an User class type object 
     HttpContext.Current.Session(SESSION_USER) = StructureService.GetInitializedUser(My.User.Name, UowProvider.StructureUow) 
    End Sub 

    Public Shared ReadOnly Property User() As Application.User 
     Get 
      //'If user doesn't exist so we create an user 

      If HttpContext.Current.Session(SESSION_USER) Is Nothing Then ReloadUser() 

      //'Return user 
      Return CType(HttpContext.Current.Session(SESSION_USER), Application.User) 
     End Get 
    End Property 

End Class 

поставщика Пользовательских ролей

Public Class AuthentifiedRoleProvider 
    Inherits RoleProvider 
    //Implement base role provider.... 

    Public Overrides Function GetRolesForUser(username As String) As String() 
     return SessionProvider.User.RolesList 
    End Function 

End Class 

Реализация - WebConfig

<system.web> ..... 
    <roleManager cacheRolesInCookie="false" defaultProvider="DefaultRoleProvider" enabled="true"> 
     <providers> 
      <clear /> 
      <add name="DefaultRoleProvider" type="AuthentifiedRoleProvider" /> 
     </providers> 
    </roleManager>  
    </system.web> 

Реализация - Контроллер

<Authorize(Roles:="UserIsAdmin")> 
    Public Function List_Items() As ActionResult 
     Return View() 
    End Function 

Он работает ...

Но, я интересно, если это действительно хороший способ для достижения этой цели , Поскольку у меня есть карта сайта в моем приложении, которая ссылается на действия контроллеров, пользователь sessionprovider (и http-сеанс кстати) запрашивается 4 или 5 раз каждую загрузку меню.

Итак, мои вопросы:

  • хранящий в сессию принесет проблемы с производительностью?
  • Есть ли лучший способ сохранить мой пользовательский объект в виде кеша (-> я думаю о system.runtime.cache).
  • В любом случае, как вы думаете, эта реализация хороша для достижения моих целей?

Большое спасибо!

ответ

0

Благодаря советам я изменил свой пользовательский провайдер сеанса, чтобы использовать кеширование. Он должен быть расширен, но пока я вижу изменение!

пользователь Session поставщик

Public Class UserProvider 


    Private Const USER_CACHE_PREFIX As String = "User|" 

    Private Shared Sub AddUserToCache(ByVal pLogin As String, ByVal pUser As Application.User) 
     Dim objCache As ObjectCache = MemoryCache.Default 
     objCache.Add(USER_CACHE_PREFIX & pLogin, pUser, New CacheItemPolicy With {.SlidingExpiration = TimeSpan.FromSeconds(20)}) 
    End Sub 

    Private Shared Function GetUserFromCache(ByVal pLogin As String) As Application.User 
     Dim objCache As ObjectCache = MemoryCache.Default 
     //Return cache if exists 
     If objCache.Contains(USER_CACHE_PREFIX & pLogin) Then 
      Return CType(objCache.GetCacheItem(USER_CACHE_PREFIX & pLogin).Value, Application.User) 
     Else 
      Return Nothing 
     End If 
    End Function 

    Public Shared Function ReloadUser(ByVal pLogin As String) As Application.User 
     Dim objCache As ObjectCache = MemoryCache.Default 
     Dim tmpLogin As String = My.User.Name 
     //Clear cache 
     If objCache.Contains(USER_CACHE_PREFIX & tmpLogin) Then objCache.Remove(USER_CACHE_PREFIX & pLogin) 
     Dim tmpUser As Application.User = StructureService.GetInitializedUser(pLogin, UowProvider.StructureUow) 
     AddUserToCache(tmpLogin, tmpUser) 
     return tmpUser 
    End Function 

    Public Shared ReadOnly Property User() As Application.User 
     Get 
      Dim tmpLogin As String = My.User.Name 
      //Try to get user from cache 
      Dim tmpUser As Application.User = GetUserFromCache(tmpLogin) 
      //If user is null then init and cache 
      If tmpUser Is Nothing Then 
       tmpUser = StructureService.GetInitializedUser(tmpLogin, UowProvider.StructureUow) 
       AddUserToCache(tmpLogin, tmpUser) 

      End If 

      //return user 
      Return tmpUser 
     End Get 
    End Property 

End Class 
1

В любом случае, как вы думаете, эта реализация хороша для достижения моих целей?

Возможно, нет.

  1. По какой-то причине вы связываете состояние сеанса с данными профиля пользователя. Состояние сеанса не имеет ничего общего с авторизацией/аутентификацией, и оно равно not recommended to use it for User Profile data, поскольку использование состояния сеанса (в любом практическом смысле) означает, что для вашего приложения потребуется два дополнительных сетевых запроса на HTTP-запрос. Кроме того, эти дополнительные запросы возникают независимо от того, зарегистрирован ли пользователь или нет.
  2. MvcSiteMapProvider не полагается на состояние сеанса.Он использует общий кэш для хранения узлов в ОЗУ и использует AuthorizeAttribute, чтобы определить, какие узлы отображать/скрывать для каждого запроса.

Если вы обнаружили, что запрашивают те же данные несколько раз в запросе, вы должны попытаться воспользоваться кэша запроса (HttpContextBase.Items) с помощью стандартного шаблона извлечения кэша, похожее на это:

Public Function GetSomeData() As ISomeData 
    Dim key As String = "SomeDataKey" 
    ' Me.cache refers to HttpContextBase.Items injected through 
    ' the constructor of this class and stored in a private field 
    Dim someData As ISomeData = TryCast(Me.cache(key), ISomeData) 
    If someData Is Nothing Then 
     ' The data is not cached, so look it up and populate the cache 
     someData = GetDataFromExternalSource() 
     Me.cache(key) = someData 
    End If 
    Return someData 
End Function 

Ввод метода, подобного этому, в службу, совместно используемую между компонентами, означает, что вам не нужно беспокоиться о повторном получении одних и тех же данных в запросе - в первый раз, когда он попадет в внешний источник, и каждое дополнительное время будет использовать кеш.

Кроме того, в MSDN:

Есть две основные причины для создания пользовательского поставщика ролей.

  1. Вам необходимо сохранить информацию о роли в источнике данных, который не поддерживается поставщиками роли, включенными в .NET Framework, такими как база данных FoxPro, база данных Oracle или другие источники данных.
  2. Вам необходимо управлять информацией о роли, используя схему базы данных, которая отличается от схемы базы данных, используемой поставщиками, которые поставляются с .NET Framework. Общим примером этого могут быть данные роли, которые уже существуют в базе данных SQL Server для компании или веб-сайта.

Так что, если вы не делаете одну из этих вещей, что может не быть правильным выбором либо. Большинство современных приложений могут использовать/расширять ASP.NET Identity для аутентификации пользователей, и все приложения MVC должны использовать AuthorizeAttribute (или подкласс) для авторизации.

System.Runtime.Cache.ObjectCacheMemoryCache) являются хорошими способами для кэширования данных, которые обычно не разделяет между пользователями. Вы можете использовать его для хранения пользовательских данных, если вы дизайн ключа кэша, чтобы включить уникальный идентификатор пользователя вместе с разделителем, что делает ключ уникальный ...

Dim key As String = UserId & "|UserProfile" 

Тем не менее, вы должны знать, что кэширование это путь не будет масштабироваться для нескольких серверов.

В любом случае, я рекомендую вам следовать совету в think twice about using Session State. MVC освободил нас от необходимости использовать состояние сеанса во многих случаях - и мы не должны использовать его, если это абсолютно необходимо.

+0

Согласно комментарию MSDN я падаю в втором случае схема базы данных ролевого хозяйствования не является стандартным и я не могу изменить это по нескольким причинам. В любом случае я был убежден, что хранение сеанса не является правильным подходом к хранению пользовательских данных. Поскольку данные не должны использоваться совместно с пользователями, я буду рассматривать объект или память. Спасибо за Ваш ответ –