MVC.Net - 缓存用户对象为应用程序提供数据(问总结)
MVC.Net - Caching user object to provide data for app (ask summed up)
我非常感谢一些建议,以帮助我进行对象缓存。
这是我的需求:我想根据存储在 SQL 数据库中的用户角色过滤我的控制器的一些操作。
我现在正在做的是:当当前用户的第一个请求完成时,我生成一个对象,其中包含一些由 SQL 查询初始化的属性。我正在使用自定义提供程序将此对象存储到会话中,并且我正在使用自定义角色提供程序来处理授权过滤器标签。如果会话过期用户被重新生成.. 就像那样(简化):
用户class
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)。
- 无论如何,您认为这个实现是否有助于实现我的目标?
非常感谢!
Anyway, do you think this implementation is a good to achieve my goals ?
可能不会。
- 出于某种原因,您将会话状态与用户配置文件数据相关联。会话状态与 Authorization/Authentication 无关,它是 not recommended to use it for User Profile data,因为使用会话状态(在任何实际意义上)意味着您需要为应用程序的每个 HTTP 请求发送 2 个额外的网络请求。此外,无论用户是否登录,这些额外的请求都会发生。
MvcSiteMapProvider
不依赖会话状态。它使用共享缓存将节点存储在 RAM 中,并使用 AuthorizeAttribute
来确定每个请求要 show/hide 的节点。
如果您发现每个请求多次请求相同的数据,您应该尝试通过使用标准的 请求缓存 (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:
There are two primary reasons for creating a custom role provider.
- You need to store role information in a data source that is not supported by the role providers included with the .NET Framework, such as a FoxPro database, an Oracle database, or other data sources.
- You need to manage role information using a database schema that is different from the database schema used by the providers that ship with the .NET Framework. A common example of this would be role data that already exists in a SQL Server database for a company or Web site.
因此,如果您没有做这两件事中的任何一件,那也可能不是正确的选择。大多数现代应用程序可以 use/extend ASP.NET Identity for user authentication and all MVC applications should use the AuthorizeAttribute(或其子类)进行授权。
System.Runtime.Cache.ObjectCache
(和MemoryCache
)是缓存用户之间通常不共享数据的好方法。如果您将缓存密钥设计为包含用户的唯一标识符以及使密钥唯一的分隔符,则可以将其用于用户数据...
Dim key As String = UserId & "|UserProfile"
也就是说,您应该知道这种缓存方式不会扩展到多个服务器。
无论如何,我建议您遵循 think twice about using Session State 中的建议。 MVC 使我们在许多情况下不必使用会话状态 - 除非绝对需要,否则我们不应该使用它。
多亏了建议,我已经将我的会话用户提供程序更改为使用缓存。它必须扩展,但现在我可以看到变化!
会话用户提供商
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
我非常感谢一些建议,以帮助我进行对象缓存。 这是我的需求:我想根据存储在 SQL 数据库中的用户角色过滤我的控制器的一些操作。
我现在正在做的是:当当前用户的第一个请求完成时,我生成一个对象,其中包含一些由 SQL 查询初始化的属性。我正在使用自定义提供程序将此对象存储到会话中,并且我正在使用自定义角色提供程序来处理授权过滤器标签。如果会话过期用户被重新生成.. 就像那样(简化):
用户class
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)。
- 无论如何,您认为这个实现是否有助于实现我的目标?
非常感谢!
Anyway, do you think this implementation is a good to achieve my goals ?
可能不会。
- 出于某种原因,您将会话状态与用户配置文件数据相关联。会话状态与 Authorization/Authentication 无关,它是 not recommended to use it for User Profile data,因为使用会话状态(在任何实际意义上)意味着您需要为应用程序的每个 HTTP 请求发送 2 个额外的网络请求。此外,无论用户是否登录,这些额外的请求都会发生。
MvcSiteMapProvider
不依赖会话状态。它使用共享缓存将节点存储在 RAM 中,并使用AuthorizeAttribute
来确定每个请求要 show/hide 的节点。
如果您发现每个请求多次请求相同的数据,您应该尝试通过使用标准的 请求缓存 (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:
There are two primary reasons for creating a custom role provider.
- You need to store role information in a data source that is not supported by the role providers included with the .NET Framework, such as a FoxPro database, an Oracle database, or other data sources.
- You need to manage role information using a database schema that is different from the database schema used by the providers that ship with the .NET Framework. A common example of this would be role data that already exists in a SQL Server database for a company or Web site.
因此,如果您没有做这两件事中的任何一件,那也可能不是正确的选择。大多数现代应用程序可以 use/extend ASP.NET Identity for user authentication and all MVC applications should use the AuthorizeAttribute(或其子类)进行授权。
System.Runtime.Cache.ObjectCache
(和MemoryCache
)是缓存用户之间通常不共享数据的好方法。如果您将缓存密钥设计为包含用户的唯一标识符以及使密钥唯一的分隔符,则可以将其用于用户数据...
Dim key As String = UserId & "|UserProfile"
也就是说,您应该知道这种缓存方式不会扩展到多个服务器。
无论如何,我建议您遵循 think twice about using Session State 中的建议。 MVC 使我们在许多情况下不必使用会话状态 - 除非绝对需要,否则我们不应该使用它。
多亏了建议,我已经将我的会话用户提供程序更改为使用缓存。它必须扩展,但现在我可以看到变化!
会话用户提供商
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