3

Использование EF DbContext, завернутый в интерфейсы (интерфейсы), вложенные в запрос для каждого запроса, чтобы убедиться, что весь запрос имеет дело с одним и тем же контекстом. Также у вас есть пользовательский RoleProvider, который использует интерфейс DbContext для настройки служб авторизации.EF DbContext для веб-запроса + Пользовательский RoleProvider = RoleProvider для веб-запроса или singleton?

До сих пор я использовал шаблон локатора службы для разрешения экземпляра DbContext в пользовательском конструкторе no-arg RoleProvider. Это вызвало некоторые незначительные проблемы, поскольку RoleProvider является однотонным, поэтому он может удерживаться на DbContext бесконечно, тогда как другие запросы могут захотеть избавиться от него во время Application_EndRequest.

У меня теперь есть решение based on this, хотя я использую другой контейнер ioc, чем виндзор. Я могу использовать DI для создания настраиваемого экземпляра RoleProvider для каждого HTTP-запроса.

Вопрос в том, должен ли я?

Имея открытый DbContext, висит от RoleProvider кажется расточительным. С другой стороны, я знаю, что каждый MVC AuthorizeAttribute попадает в RoleProvider (если у него есть нулевое свойство Roles, что у большинства наших), я полагаю, что может быть полезно уже иметь DbContext в ожидании.

Альтернативой было бы ввести другой DbContext для RoleProvider, который не является веб-запросом. Таким образом, DbContext s, которые живут только для веб-запроса, могут быть расположены в конце, не затрагивая однотонный RoleProvider.

Является ли подход лучше, и почему?

Update после комментариев

Стивен, это по существу то, что я сделал. Единственное различие заключается в том, что я не беру зависимость от System.Web.Mvc.DependencyResolver. Вместо этого я в основном имеют точно такую ​​же вещь в моем собственном проекте, просто называется по-разному:

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 
     { 
      // ... 
     } 
    } 
} 

Эти классы являются частью ядра API проекта, и в другом проекте, чем MVC. Таким образом, для другого проекта (вместе с проектом домена) не нужно зависеть от System.Web.Mvc, чтобы скомпилировать его DependencyResolver.

Учитывая, что смена рамки Unity с SimpleInjector до сих пор была безболезненной. Вот что представляет собой многоцелевая установка SinglePort RoleProvider:

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) 
} 

Веб.конфигурации:

<roleManager enabled="true" defaultProvider="InjectedRoleProvider"> 
    <providers> 
     <clear /> 
     <add name="InjectedRoleProvider" type="MyApp.InjectedRoleProvider" /> 
    </providers> 
</roleManager> 

IoC контейнер:

Container.RegisterPerWebRequest<RoleProvider, CustomRoleProvider>(); 

Что касается жвачку, есть только один метод, реализованный в моем CustomRoleProvider:

public override string[] GetRolesForUser(string userName) 

Это единственный метод, используемый MVC-х AuthorizeAttributeIPrincipal.IsInRole), и из всех других методов я просто

throw new NotSupportedException("Only GetRolesForUser is implemented."); 

Поскольку на провайдере нет роли CUD ops, я не беспокоюсь о транзакциях.

+0

Можете ли вы реализовать свой собственный «RoleService», а не использовать «RoleProvider»? «RoleProvider» не очень дружелюбен к МОК. – Dismissile

+0

@ Disismile Я стараюсь держать все как можно проще. Я не знаю, с чего начать, если подключить RoleService к 'IPrincipal.IsInRole (string)'. Это не сложно было обернуть и контролировать свою жизнь, используя ссылку в моем вопросе. Теперь я хочу знать, какой срок жизни я должен использовать. – danludwig

+0

Выполняете ли вы проверку подлинности Windows или проверку подлинности с помощью форм? Вы используете встроенную базу данных членства ASP.net? Если вы используете либо Windows, либо стандартную базу данных членства, это, вероятно, не так просто. Если вы храните пользователей/роли в своей собственной схеме базы данных, то, вероятно, это не намного сложнее, чем запрашивать эту таблицу для конкретного пользователя, чтобы увидеть, в каких ролях они находятся. – Dismissile

ответ

3

Посмотрите на проект Griffin.MvcContrib. Он содержит MembershipProvider и RoleProvider, которые используют MVC DependencyResolver.

Вы можете настроить RoleProvider так:

<roleManager enabled="true" defaultProvider="MvcRoleManager"> 
    <providers> 
    <clear /> 
    <add name="MvcRoleManager" 
     type="Griffin.MvcContrib.Providers.Roles.RoleProvider, Griffin.MvcContrib" 
    /> 
    </providers> 
</roleManager> 

Он использует DependencyResolver класса System.Web.MVC так что вам нужно настроить IDependencyResolver реализацию для DI контейнера вы используете. С помощью простых Injector (и SimpleInjector.MVC3 integration NuGet package), вам необходимо добавить следующую конфигурацию в вашем Application_Start события:

container.RegisterAsMvcDependencyResolver(); 

Griffin.MvcContrib.Providers.Roles.RoleProvider принимает зависимость от IRoleRepository, которая определяется в той же сборке. Вместо того, чтобы реализовать полный провайдер роли теперь вы можете просто Реализовать IRoleRepository и зарегистрировать его в контейнере:

container.Register<IRoleRepository, MyOwnRoleRepository>(); 

Вы можете найти этот проект here на NuGet.

UPDATE

А теперь давайте ответим на вопрос:

Griffin.MvcContrib RoleProvider будет синглтон, и теперь вопрос переходит к IRoleRepository и его зависимостей, но вопрос действительно все еще остается.

Если все, что вы делаете, читается у поставщика ролей (никогда не обновляйте базу данных); в этом случае не имеет значения, какое время жизни вы выберете, если вы не используете одно и то же DbContext по потокам.

Однако, когда вы используете поставщика ролей для обновления базы данных, все становится по-другому. В этом случае я бы дал ему свой собственный контекст и дал ему явно зафиксировать его после каждой операции. Потому что, если вы этого не сделаете, кто собирается совершить эти изменения? При запуске в контексте обработчика команд (и особенно TransactionCommandHandlerDecorator) операция будет выполнена после того, как команда будет выполнена успешно и откатится при неудачной команде. Возможно, это нормально, если вы отмените команду. Но когда поставщик роли работает вне контекста обработчика команд, кто собирается его совершить? Я уверен, что вы сможете это решить, но я считаю, что вы в конечном итоге получаете систему, которую трудно понять, и она ослепляет других разработчиков, которые пытаются выяснить, почему эти изменения не совершили.

+0

+1 для информации, я рассмотрю этот проект. Но вопрос по-прежнему остается: лучше ли поддерживать SinglePost для RoleProvider или воссоздавать его для каждого контекста HTTP? Может быть, это даже не имеет значения.? – danludwig

+0

«Если все, что вы делаете, читается из поставщика ролей (никогда не обновляйте базу данных), в этом случае не имеет значения, какую жизнь вы выберете ...« Действительно? Скажем, я регистрирую 'DbContext' для каждого веб-запроса, а затем удаляю его во время' Application_EndRequest'. Если 'RoleProvider' является singleton и использует per-web-request' DbContext', он будет генерировать исключение во время второго и всех последующих запросов, потому что DbContext был удален после первого запроса. – danludwig

+0

Ну, конечно, это нарушит одно из первых правил DI: Служба не должна зависеть от службы, срок службы которой короче, чем ее собственное время жизни. Поставщик ролей всегда является одноэлементным, так как он настроен в файле конфигурации. В этом случае вы должны использовать абстрактный завод или локатор сервисов (что «DependencyResolver») для запроса зависимостей при каждом вызове метода. Таким образом, вы убедитесь, что не используете устаревшую зависимость.Но тем не менее, какое время жизни имеет зависимость 'DbContext', не имеет большого значения (пока вы не будете использовать один и тот же контекст по потокам). – Steven

 Смежные вопросы

  • Нет связанных вопросов^_^