2017-01-28 1 views
0
 public class ActionFilterVersionAttribute : ActionFilterAttribute 
     { 
      public override void OnActionExecuting(HttpActionContext actionContext) 
      { 
       if (actionContext.Request.Headers.Any(x => x.Key == "SetInternalVersion")) 
       { 
    // determine somehow that the **InternalSystem implementation** should be resolved when the controller class is instantiated with the **ISystem constructor** parameter    
       } 
       else 
       { 
// determine somehow that the **ExternalSystem implementation** should be resolved when the controller class is instantiated with the **ISystem constructor** parameter 
       } 

       base.OnActionExecuting(actionContext); 
      } 
     } 

Я имею ExternalSystem/InternalSystem with the ISystem interface.Установите тип зарегистрированной службы во время выполнения в обработчик действия фильтра/сообщении

Как я могу сказать autofac впрыснуть ExternalSystem или InternalSystem в реализованным контроллер, как экземпляр ISystem в зависимости от строки значение, которое я передаю в ActionFilter или, возможно, обработчике сообщений.

Я знаю, что могу делать такие вещи, как:

builder.RegisterType<InternalSystem>().As<ISystem>().Keyed<ISystem>("Internal"); 

, где я могу использовать func<string,ISystem> фабрику для решения класса во время выполнения, но это не то, что я хочу сделать.

На самом деле мне нужно зарегистрировать ISystem внутри фильтра действий, но тогда мне нужно каким-то образом передать контейнер в фильтр, но это не то, что я хочу ... и его тоже не возможно.

// Action: returns external or internal value 
public string Get() 
{ 
    return resolvedISystem.Get(); 
} 

Конечно, я мог бы разрешить ISystem в зависимости от Func завода в пределах каждого одиночного действия или положить поведение в базовый контроллер, где я проверяю для заголовка, но я действительно предпочел бы действие фильтра, как это может быть просто глобально зарегистрировано ОДИН раз, но для каждого нового контроллера я должен подклассифицировать базовый контроллер.

Базовый образец контроллер с псевдо-кодом, потому что base.Request является нулевым, который нуждается в другой обходной путь/исправить ...

public class BaseController : ApiController 
    { 

     public BaseController(Func<string, ISystem> dataServiceFactory) 
     { 
      string system = base.Request.Headers.Any(x => x.Key == "SetInternalVersion") ? "internal" : "external"; 
      System = dataServiceFactory(system); 
     } 
     public ISystem System { get; set; } 
    } 

ОБНОВЛЕНИЕ контейнер также отмечен как OBSOLETE автором Autofac.

Таким образом, я не хочу добавлять регистрацию в свой фильтр/обработчик и обновлять/строить контейнер снова.

ответ

2

Я думаю, что вы не должны использовать ActionFilter. У вас есть зависимость от контроллера, которая должна быть правильно решена на основе информации, поступающей из запроса. Вот возможное решение. Вы можете использовать статическое свойство HttpContext.Current, чтобы извлечь заголовок запроса.

Системные классы:

public interface ISystem { } 
public class ExternalSystem : ISystem { } 
public class InternalSystem : ISystem { } 

SystemKeyProvider:

public enum SystemKey 
{ 
    External, 
    Internal 
} 

public interface ISystemKeyProvider 
{ 
    SystemKey GetSystemKey(); 
} 

public class SystemKeyProvider : ISystemKeyProvider 
{ 
    private const string HeaderKey = "SetInternalVersion"; 
    private readonly HttpRequest _request; 
    public SystemKeyProvider(HttpRequest request) 
    { 
     _request = request; 
    } 

    public SystemKey GetSystemKey() 
    { 
     return (_request.Headers[HeaderKey] != null) ? 
      SystemKey.Internal : 
      SystemKey.External; 
    } 
} 

контроллер конструктор:ValuesController(ISystem system)

Autofac контейнер РЕЕСТР ция:

var builder = new ContainerBuilder(); 
builder.Register(c => HttpContext.Current.Request).As<HttpRequest>().InstancePerRequest(); 
builder.RegisterType<SystemKeyProvider>().AsImplementedInterfaces(); 

// service registration 
builder.RegisterType<ExternalSystem>().Keyed<ISystem>(SystemKey.External); 
builder.RegisterType<InternalSystem>().Keyed<ISystem>(SystemKey.Internal); 
builder.Register(c => 
    c.ResolveKeyed<ISystem>(c.Resolve<ISystemKeyProvider>().GetSystemKey())) 
    .As<ISystem>(); 

builder.RegisterApiControllers(Assembly.GetExecutingAssembly()); 
GlobalConfiguration.Configuration.DependencyResolver = 
    new AutofacWebApiDependencyResolver(builder.Build()); 

В этом решении я создал класс SystemKeyProvider оболочку, которая отвечает за обеспечение соответствующего ключа для разрешения ISystem.


Демонстрация:

Когда нет SetInternalSystem заголовка нет.

No SetInternalSystem header is present. Fiddler

Тогда зависимость будет решена, как ExternalSystem.

Then dependency is resolved as ExternalSystem

Когда SetInternalSystem заголовок присутствует.

SetInternalSystem header is present

Тогда зависимость будет решена, как InternalSystem.

Then dependency is resolved as InternalSystem

+0

1.) Я пытался теперь обработчик сообщений обновляют контейнер, который работает, но это не должно быть сделано из-за устаревшее предупреждение. 2.) Я попробовал фабрику func в фильтре действий, добавляя разрешенную услугу к request.Properties bag 3.) Если ваше решение работает, технически это самое сложное, я не знал о регистрации текущего запроса, который действительно хорошенькая идея! – Pascal

+0

Отлично работает. Я по-прежнему поддерживаю этот ответ как хороший! Спасибо. – Pascal

+0

Хотя ответ верный, факт, что вы полагаетесь на «HttpContext.Current», немного шелушится, как если бы вы использовали Web API поверх OWIN, «HttpContext.Current» не установлен. Я бы предложил использовать метод расширения RegisterHttpRequestMessage' в 'ContainerBuilder'. Это регистрирует «HttpRequestMessage» с областью действия запроса на HTTP. Затем 'SystemKeyProvider' мог использовать его вместо' HttpContext'. –

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