Как подключить контейнер Autofac к ASP. Идентификация NET 2.1
Я изучил новые функции новой версии ASP.NET Identity 2.1, и одним из ее усовершенствований являются новые функции IoC, интегрированные в промежуточное ПО OWIN.
Одно из предложений, которое я посмотрел в примерах, это следующее:
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
Это предложение получает делегат функции, который возвращает новый экземпляр реализации менеджера, представленный на примерах:
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options,
IOwinContext context)
{
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
Мне лично не нравится эта реализация, потому что я не могу использовать контейнер для ввода любой зависимости, которую я хочу для этих менеджеров.
Также есть "IdentityFactoryOptions" и "IOwinContext", которые "магически" вводятся в функцию, которую Im не удалось вытащить в мой контейнер IoC.
Есть ли у кого-нибудь лучшее обходное решение для этой реализации?
Ответы
Ответ 1
Я начинаю с установленной сборки MVC5 и использую AutoFac в качестве контейнера IoC. Похоже, я пытаюсь достичь аналогичной цели, как вы, поэтому позвольте мне объяснить, что я сделал. Как отказ от ответственности, я довольно новичок в использовании IoC и Identity.
Я считаю, что IOwinContext не нужен в роли IoC, если вы используете свой собственный - я переключился на регистрацию моего ApplicationUserManager с помощью AutoFac. Для этого я должен был:
Удалите строки CreatePerOwinContext из Startup.Auth, так как я зарегистрирую ApplicationDbContext
и ApplicationUserManager
в AutoFac.
//app.CreatePerOwinContext(ApplicationDbContext.Create);
//app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
Измените аргументы конструктора ApplicationUserManager и включите все из функции Create.
public ApplicationUserManager(IUserStore<ApplicationUser> store, IdentityFactoryOptions<ApplicationUserManager> options)
: base(store)
{
//all the code from the 'Create' function here, using `this` for `manager`
}
Установите AccountController для того, чтобы один конструктор принял ApplicationUserManager
в качестве аргумента и сломал свойство UserManager
, которое захватывает ApplicationUserManager
из OwinContext
.
private ApplicationUserManager _userManager; //every thing that needs the old UserManager property references this now
public AccountController(ApplicationUserManager userManager)
{
_userManager = userManager;
}
Зарегистрируйте все с помощью AutoFac, включая экземпляр IdentityFactoryOptions.
var x = new ApplicationDbContext();
builder.Register<ApplicationDbContext>(c => x);
builder.Register<UserStore<ApplicationUser>>(c => new UserStore<ApplicationUser>(x)).AsImplementedInterfaces();
builder.Register<IdentityFactoryOptions<ApplicationUserManager>>(c => new IdentityFactoryOptions<ApplicationUserManager>()
{
DataProtectionProvider = new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider("ApplicationName")
});
builder.RegisterType<ApplicationUserManager>();
Это грубое резюме. Возможно, я пропустил еще пару трюков, которые мне пришлось сделать по пути.
Ответ 2
Ответ Ben отвечает на общую идею, но он вручную создает экземпляр DbContext и использует этот экземпляр при регистрации остальных типов. IMO, это плохая идея (нельзя использовать один и тот же вечный контекст db для ВСЕХ запросов).
Комментарий Derek является большим улучшением, но он не передает контекст базы данных в хранилище пользователей, что приводит к ошибкам, таким как "Тип объекта ApplicationUser не является частью модели для текущего контекста".
Я включил свой код ниже, для справки - он действительно похож на Derek's.
builder.RegisterType<MyApplicationContext>().AsSelf().InstancePerRequest()
//...
builder.RegisterType<ApplicationUserManager>().AsSelf().InstancePerRequest();
builder.RegisterType<ApplicationSignInManager>().AsSelf().InstancePerRequest();
builder.Register(c => new UserStore<ApplicationUser>(c.Resolve<MyApplicationContext>())).AsImplementedInterfaces().InstancePerRequest();
builder.Register(c => HttpContext.Current.GetOwinContext().Authentication).As<IAuthenticationManager>();
builder.Register(c => new IdentityFactoryOptions<ApplicationUserManager>
{
DataProtectionProvider = new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider("Application")
});
Ответ 3
Для справки, как вы можете подключить все, используя Unity:
var container = new UnityContainer();
container.RegisterType<MyDbContext>(new InjectionConstructor("ConnectionStringName"));
container.RegisterType<IAuthenticationManager>(
new InjectionFactory(c => HttpContext.Current.GetOwinContext().Authentication));
container.RegisterType<IUserStore<ApplicationUser>, UserStore<ApplicationUser>>(
new InjectionConstructor(typeof(MyDbContext)));
container.RegisterType<IRoleStore<IdentityRole, string>, RoleStore<IdentityRole>>(
new InjectionConstructor(typeof(MyDbContext)));
container.RegisterType<IdentityFactoryOptions<ApplicationUserManager>>(new InjectionFactory(x =>
new IdentityFactoryOptions<ApplicationUserManager>
{
DataProtectionProvider = new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider("ApplicationName")
}));
container.RegisterType<ApplicationSignInManager>();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
Ответ 4
Мне удалось обходным путем, используя локатор сервисов autofac:
app.CreatePerOwinContext(() => DependencyResolver.Current.GetService<ApplicationUserManager>());
Да, это не достаточно хорошо, но в то же время мы можем использовать объект той же области действия, что и в процессе регистрации autofac.
Ответ 5
Теперь подробно описано Интеграция MVC5 Owin в Документах по AutoFac:
"
- Делайте все, что нужно для стандартной интеграции MVC - регистрируйте контроллеры, устанавливайте преобразователь зависимостей и т.д.
- Настройте приложение с базовой интеграцией Autofac OWIN.
- Добавьте ссылку на пакет Autofac.Mvc5.Owin NuGet.
-
В классе запуска приложения зарегистрируйте промежуточное ПО Autofac MVC после регистрации базового промежуточного программного обеспечения Autofac.
public class Startup
{
public void Configuration(IAppBuilder app)
{
var builder = new ContainerBuilder();
// STANDARD MVC SETUP:
// Register your MVC controllers.
builder.RegisterControllers(typeof(MvcApplication).Assembly);
// Run other optional steps, like registering model binders,
// web abstractions, etc., then set the dependency resolver
// to be Autofac.
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
// OWIN MVC SETUP:
// Register the Autofac middleware FIRST, then the Autofac MVC middleware.
app.UseAutofacMiddleware(container);
app.UseAutofacMvc();
}
}
"
У меня также есть обертка RoleManager, добавленная:
builder.RegisterType<RoleStore<IdentityRole>>().As<IRoleStore<IdentityRole, string>>();
в соответствии с SO answer