Установите тип зарегистрированной службы во время выполнения в фильтре действий / обработчике сообщений.
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 в зависимости от фабрики функций внутри каждого отдельного действия или поместить поведение в базовый контроллер, где я проверяю заголовок, но я действительно предпочел бы фильтр действий, так как он может быть просто глобально зарегистрирован ОДИН раз, но для каждый новый контроллер я должен подкласс базового контроллера.
Пример базового контроллера с псевдокодом, потому что base.Request имеет значение null, что требует другого обходного пути / исправления...
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.
Таким образом, я не хочу добавлять регистрации в мой фильтр / обработчик и обновлять / собирать контейнер снова.
1 ответ
Я думаю, что вы не должны использовать 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
заголовок присутствует.
Тогда зависимость разрешается как ExternalSystem
,
когда SetInternalSystem
заголовок присутствует.
Тогда зависимость разрешается как InternalSystem
,