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 to System.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.

+3


source to share


1 answer


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.

+3


source







All Articles