I have an ASP.NET MVC 4 application which was required to use a pre-existing membership model of users/roles. The way I did this was to implement a custom ASP.NET RoleProvider to manage access, this uses Entity Framework repositories to read user data from the database. The method to read user roles is shown below, but all the method implementations follow this pattern:
public class OurRoleProvider : RoleProvider
{
private IUserRepository _userRepository;
public OurRoleProvider() : this(Container.Resolve<IUserRepository>())
{
}
public OurRoleProvider(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public override string[] GetRolesForUser(string username)
{
var user = _userRepository.GetUserByUserName(username);
if (user.Roles.IsNullOrEmpty())
return new string[0];
return user.Roles.Select(r => r.RoleName).ToArray();
}
}
I have now come across the problem described in this post. Because a single instance of a RoleProvider is re-used for the lifetime of the application, and all other functionality creates it's own per-request DbContext to persist data, changes made to a User profile are not reflected by the RoleProvider until a restart in the application, because it's underlying DbContext is not being refreshed. This means you can remove a User from a Role, and they will still have access to that Role's functionality until an app restart.
I have tried creating a new repository instance within the RoleProvider methods, i.e. in GetRoleForUser():
var user = _userRepository.GetUserByUserName(username);
becomes
var userRepository = Container.Resolve<IUserRepository>();
var user = userRepository.GetUserByUserName(username);
This fixes the issue but breaks unit tests which don't use the DI container and inject a mock repository via the constructor. There would be a lot of unit tests to re-write.
I'd like to stick with a custom RoleProvider if possible to make use of features such as the Authorize
atrribute. What I really need to do is re-instantiate the RoleProvider on a per-request basis OR force the EF repository to always update from the database. So far I haven't found a way to do this. Is this possible? Or is there a better solution?