EF DbContext for web request + Custom RoleProvider = RoleProvider for web request or singleton?
Using EF DbContext
wrapped in interfaces (interfaces) nested in the request for each request to make sure the entire request deals with the same context. Also you have a custom RoleProvider
one that uses an interface DbContext
to configure authorization services.
So far I've used the service locator pattern to resolve the instance DbContext
in a custom RoleProvider
no-arg constructor . This caused some minor problems as it RoleProvider
is monochromatic, so it might be held DbContext
indefinitely, while other requests might want to get rid of it in time Application_EndRequest
.
I now have a solution based on this , albeit using a different ioc container than Windsor. I can use DI to create a custom instance RoleProvider
for every HTTP request.
My question is, should I?
Having an open DbContext
deviation from RoleProvider
seems wasteful. On the other hand, I know that every MVC AuthorizeAttribute
falls into RoleProvider
(if it has a non-null property Roles
, which we do in most of ours), so I suppose it would be useful to have DbContext
pending already .
An alternative would be to introduce another DbContext
for RoleProvider
, which is not web-based. This way, DbContext
those that live only for the web request can be removed at the end without affecting the singletony RoleProvider
.
Is the approach better, and why?
Update after comments
Stephen, this is essentially what I did. The only difference is that I do not accept addiction toSystem.Web.Mvc.DependencyResolver
. Instead, I basically have the same thing in my own project, just named differently:
public interface IInjectDependencies
{
object GetService(Type serviceType);
IEnumerable<object> GetServices(Type serviceType);
}
public class DependencyInjector
{
public static void SetInjector(IInjectDependencies injector)
{
// ...
}
public static IInjectDependencies Current
{
get
{
// ...
}
}
}
These classes are part of the project core API and are located in a different project besides MVC. Thus, for another project (along with a domain project) there is no need to take a dependency on System.Web.Mvc
in order to compile it with DependencyResolver
.
Given this structure, replacing Unity with SimpleInjector has been painless so far. This is what a multi-user installation of SinglePortider RoleProvider looks like:
public class InjectedRoleProvider : RoleProvider
{
private static IInjectDependencies Injector
{ get { return DependencyInjector.Current; } }
private static RoleProvider Provider
{ get { return Injector.GetService<RoleProvider>(); } }
private static T WithProvider<T>(Func<RoleProvider, T> f)
{
return f(Provider);
}
private static void WithProvider(Action<RoleProvider> f)
{
f(Provider);
}
public override string[] GetRolesForUser(string username)
{
return WithProvider(p => p.GetRolesForUser(username));
}
// rest of RoleProvider overrides invoke WithProvider(lambda)
}
Web.config:
<roleManager enabled="true" defaultProvider="InjectedRoleProvider">
<providers>
<clear />
<add name="InjectedRoleProvider" type="MyApp.InjectedRoleProvider" />
</providers>
</roleManager>
IoC container:
Container.RegisterPerWebRequest<RoleProvider, CustomRoleProvider>();
As for the CUD, there is CustomRoleProvider
only 1 method implemented in mine :
public override string[] GetRolesForUser(string userName)
This is the only method used by MVC AuthorizeAttribute
(s IPrincipal.IsInRole
) and of all the other methods I just
throw new NotSupportedException("Only GetRolesForUser is implemented.");
Since there is no CUD role on the provider, I am not worried about transactions.
source to share
Take a look at the Griffin.MvcContrib project . It contains implementation MembershipProvider
and RoleProvider
that use MVC DependencyResolver
.
You can set up RoleProvider
like this:
<roleManager enabled="true" defaultProvider="MvcRoleManager">
<providers>
<clear />
<add name="MvcRoleManager"
type="Griffin.MvcContrib.Providers.Roles.RoleProvider, Griffin.MvcContrib"
/>
</providers>
</roleManager>
It uses the System.Web.MVC class DependencyResolver
, so you need to customize the implementation IDependencyResolver
for the DI container you are using. With Simple Injector (and NuGet package for integrating SimpleInjector.MVC3 ) you need the following configuration in an event Application_Start
:
container.RegisterAsMvcDependencyResolver();
Griffin.MvcContrib.Providers.Roles.RoleProvider
takes a dependency on a IRoleRepository
, which is defined in the same assembly. Instead of implementing a full role provider, you can now simply implement IRoleRepository
and register it in your container:
container.Register<IRoleRepository, MyOwnRoleRepository>();
You can find this project here on NuGet.
UPDATE
And now answer the question:
Griffin.MvcContrib RoleProvider will be a singleton and now the question goes to IRoleRepository
and its dependencies, but the question really remains.
If everything you do is read from the role provider (never update the database); in this case it doesn't matter which lifetime you choose, as long as you don't reuse the same DbContext
across threads.
However, when you use the role provider to update the database, things are different. In this case, I would give it my own context and let it explicitly commit it after each operation. Because if you don't, who's going to make these changes? When working in the context of a command handler (and especially the TransactionCommandHandlerDecorator) the operation will be executed after the command is successful and rolled back when the command failed. It might be okay if you reverse the command. But when a role provider is operating outside the context of a command processor, who is going to commit it? I'm sure you can work it out, but I believe that you end up with a system that is difficult to understand and blinds other developers who are trying to figure out why those changes were not made.
source to share