0

Я хочу настроить регистрацию в контейнере Unity, который используется ASP.NET Web API 2 на основе свойств HTTP-запроса. Например, запрос на /api/database1/values должен привести к конфигурации контейнера Unity с параметром IDbContext, настроенным для базы данных1, тогда как запрос /api/database4/values получит IDbContext, настроенный для базы данных4.Конфигурация контейнера Unity по запросу в промежуточном ПО OWIN

Я до сих пор использовал UnityHierarchicalDependencyResolver в качестве преобразователя зависимостей, поэтому типы, зарегистрированные с HierarchicalLifetimeManager, сохраняются только для срока службы запроса. Это хорошо работает для получения типов разрешено за запрос. Но как их получить зарегистрировано за запрос с использованием промежуточного программного обеспечения OWIN.

В моем промежуточном слое, вызов System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IUnityContainer)) получает экземпляр IUnityContainer, но это же контейнера для всех запросов, включая любые регистрации предыдущих запросов.

По инкапсуляции UnityHierarchicalDependencyResolver с моей собственной реализацией IDependencyResolver Я вижу, что IDependencyResolver.BeginScope не вызывается до тех пор, пока в процессе не будет много. Таким образом, проблема заключается в том, что дочерний контейнер не создается до тех пор, пока веб-API не просыпается, после того, как мое промежуточное ПО вызовет Next(..).

Есть ли способ, с помощью которого я могу расширить сферу действия своего зависимого преобразователя, чтобы начать раньше? Есть ли какая-то другая стратегия, которую мне не хватает. В случае, если это имеет значение, я размещаю в IIS, но предпочитаю подход промежуточного программного обеспечения OWIN.

Update

Это не ответ, и это слишком большой для комментария, но после того, как изо всех сил, чтобы решить эту проблему с Unity я решил перейти на Autofac и все это просто встало на свои места.

Autofac Owin пакеты (Autofac.Mvc5.Owin, Autofac.Owin, Autofac.WebApi2.Owin) делает его мертвым простой в использовании Autofac внутри трубопровода Owin и обеспечить надлежащее управление временем жизни в ASP.NET MVC и Web API. Это недостающее звено.

Я не мог найти способ перенастроить контейнер по запросу, но он по крайней мере позволил настроить фабрику по запросу (так что да, @Haukinger и @alltej, вы были правы, чтобы вставлять ., что направление

Так я зарегистрировать завод, как:

builder.RegisterType<DataDependencyFactory>().InstancePerRequest(); 

и зарегистрировать метод создания, что завод, как:

builder 
    .Register(c => c.Resolve<DataDependencyFactory>().CreateDataDependency()) 
    .As<IDataDependency>() 
    .InstancePerRequest(); 

регистрируя завод этот путь особенно полезен, потому что нисходящие иждивенцы не должны знать о фабрике. Мне это нравится, потому что моим иждивенцам не нужна фабрика, им нужен экземпляр. Контейнер сгибается до потребностей моих иждивенцев, а не наоборот :)

Затем, в части промежуточного программного обеспечения OWIN, я разрешаю завод и устанавливаю свойство на нем в соответствии с свойствами запроса. Последующее разрешение IDataDependency в контроллере MVC или Web API или что-то еще в конвейере OWIN получит экземпляр, настроенный в соответствии с свойством на заводе.

+0

FWIW, я считаю, что я не пытаюсь сделать что-то действительно смешное, так как документация Autofac подтверждает эту концепцию. http://docs.autofac.org/en/latest/faq/per-request-scope.html#implementing-custom-per-request-semantics _ ** вы можете реализовать настраиваемый механизм, который обеспечивает возможность регистрации и разрешения зависимостей по каждому запросу **. Хотя я бы предпочел придерживаться Unity, если это тяжелая битва, я бы рассмотрел альтернативный контейнер IoC, такой как Autofac. – Snixtor

+0

Считаете ли вы фабрику для вашего IDBContext, которая создает контекст и получает соответствующие свойства запроса в качестве параметра? Таким образом, вам нужна только одна регистрация для завода. – Haukinger

+0

Завод по-прежнему нуждается в конфигурации для каждого запроса. Это означает, что ему нужно будет сразу узнать о соединении (не идеальном) или понадобится какой-то контекст соединения, который вводится через контейнер IoC, который возвращает меня обратно к квадрату. Или что-то мне не хватает? – Snixtor

ответ

1

Основываясь на URL-адресе api ("/ api/database4/values"), я предлагаю создать атрибут фильтра (например, DbIdFilter), чтобы вы могли повторно использовать атрибут фильтра для других методов контроллера, которые следуют аналогичному URL-адресу/сегмент, как это показано ниже:

[HttpGet] 
[DbIdFilter] 
[Route("{databaseId}/values")] 
public IHttpActionResult GetValues() 
{ 
    return Ok(); 
} 


[HttpGet] 
[DbIdFilter] 
[Route("{databaseId}/products")] 
public IHttpActionResult GetProducts() 
{ 
    return Ok(); 
} 

во-первых, создать атрибут фильтра:

public class DbIdFilterAttribute : ActionFilterAttribute 
{ 
    private readonly string _routeDataId; 
    private const string defaultRouteName = "databaseId"; 
    public DbIdFilterAttribute():this(defaultRouteName) 
    {} 

    public DbIdFilterAttribute(string routeDataId) 
    { 
     _routeDataId = routeDataId; 
    } 

    public override void OnActionExecuting(HttpActionContext actionContext) 
    { 

     var routeData = actionContext.Request.GetRouteData(); 

     var dbId = routeData.Values[_routeDataId] as string; 

     //here we create the db instance at the filter level. 

     DbInstanceFactory.RegisterDbInstance(dbId); 


    } 

} 

Далее, создайте экземпляр фабрики, которая будет регистрировать/разрешить экземпляр дб во время выполнения:

public class DbInstanceFactory : IDbInstanceFactory 
{ 
    public static IDbInstance RegisterDbInstance(string databaseId) 
    { 
     var factory = UnityConfig.GetConfiguredContainer().Resolve<IDbInstanceFactory>(); 
     return factory.CreateInstance(databaseId); 
    } 

    public IDbInstance CreateInstance(string databaseId) 
    { 
     var container = UnityConfig.GetConfiguredContainer(); 
     //container.RegisterType<IDbInstance, DbInstance>(); 

     container.RegisterType<IDbInstance, DbInstance>(new InjectionConstructor(databaseId)); 
     var dbInstance = container.Resolve<IDbInstance>(); 
     return dbInstance; 
    } 

    public IDbInstance GetInstance() 
    { 
     var container = UnityConfig.GetConfiguredContainer(); 
     var dbInstance = container.Resolve<IDbInstance>(); 
     return dbInstance; 
    } 
} 

public interface IDbInstanceFactory 
{ 
    IDbInstance CreateInstance(string databaseId); 
    IDbInstance GetInstance(); 
} 

Регистрация этот класс фабрики в UnityConfig.cs (или там, где вы в настоящее время зарегистрировать типы):

container.RegisterType<IDbInstanceFactory, DbInstanceFactory> 
    (new ContainerControlledLifetimeManager()); 

Он зарегистрирован ContainerControlledLifetimeManager, поскольку этот завод не должен быть по запросу.

Так что базовый класс DbInstance ниже (для ясности), который принимает параметр в конструкторе (этот параметр может быть строка подключения или имя соединение):

public class DbInstance : IDbInstance 
{ 
    public string DbId { get; } 

    public DbInstance(string databaseId) 
    { 
     DbId = databaseId; 
    } 
} 

public interface IDbInstance 
{ 
    string DbId { get; } 
} 

В классе контроллера, вы можете использовать это так:

.... 
private IDbInstanceFactory _dbFactory; 

public MyController(IDbInstanceFactory dbFactory) 
{ 
    _dbFactory = dbFactory; 
} 

// Alternate, if you want to use property injection instead of constructor injection 
//[Dependency] 
//public IDbInstanceFactory DbFactory { get; set; } 

[HttpGet] 
[DbIdFilter] 
[Route("{databaseId}/test")] 
public IHttpActionResult Test() 
{ 
    var db = _dbFactory.GetInstance(); 

    return Ok(db.DbId); 
} 

... 
+0

Это хорошо в теории, но имеет проблемы. ** 1. ** «ActionFilterAttribute.OnActionExecuting» выполняется * после * контроллеров построены, поэтому регистрация зависимостей в нем не будет работать. ** 2. ** Состояние в 'ActionFilterAttribute' не является потокобезопасным. http://stackoverflow.com/a/8937793/855363, https://msdn.microsoft.com/en-us/library/system.web.http.filters.actionfilterattribute(v=vs.118).aspx "Любой члены экземпляра не гарантируют безопасность потоков ». ** 3. ** Это фильтр действий, а не промежуточное ПО OWIN, к чему я стремился. – Snixtor

+0

Приведенный выше код был протестирован, и он смог зарегистрировать и разрешить зависимость. – alltej

+0

Чтобы быть более конкретным, вы не можете регистрировать зависимости конструктора контроллера в фильтре действий. Это не шоу-стоппер, но это не идеально. Другие проблемы, которые я поднял, - это шоу-стопперы. – Snixtor

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