Using EF DbContext
wrapped in interface(s), dependency injected per web request, to make sure the entire request deals with the same context. Also have a custom RoleProvider
which consumes the DbContext
by interface to customize authorization services.
Until now I have been using service locator pattern to resolve the DbContext
instance in the custom RoleProvider
's no-arg constructor. This has caused some minor issues because the RoleProvider
is singletonish, so it may hold onto a DbContext
indefinitely whereas other requests may want to dispose of it during Application_EndRequest
.
I now have a solution based on this, though using a different ioc container than windsor. I can use DI to new up a custom RoleProvider
instance for each http request.
My question is, should I?
Having an open DbContext
hanging off the RoleProvider
seems wasteful. On the other hand, I know every MVC AuthorizeAttribute
hits the RoleProvider
(if it has a non-null Roles
property, which most of ours do) so I suppose it could be useful to already have a DbContext
in waiting.
The alternative would be to inject a different DbContext
for the RoleProvider
that is not per web request. This way the DbContext
s that live only for the web request can be disposed at the end, without affecting the singletony RoleProvider
.
Is either approach better, and why?
Update after comments
Steven, this is essentially what I did. The only difference is that I don't take a dependency on System.Web.Mvc.DependencyResolver
. Instead, I basically have the same exact 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's core API, and are in a different project than MVC. This way, that other project (along with the domain project) don't need to take a dependency on System.Web.Mvc
in order to compile against its DependencyResolver
.
Given that framework, swapping out Unity with SimpleInjector has been painless so far. Here is what the multipurpose singleton RoleProvider setup 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 CUD, there is only 1 method implemented in my CustomRoleProvider
:
public override string[] GetRolesForUser(string userName)
This is the only method used by MVC's AuthorizeAttribute
(and IPrincipal.IsInRole
), and from all other methods, I simply
throw new NotSupportedException("Only GetRolesForUser is implemented.");
Since there are no role CUD ops on the provider, I am not worried about transactions.