#c# #asp.net-web-api #dependency-injection #autofac
#c# #asp.net-веб-api #внедрение зависимостей #autofac
Вопрос:
Я пытаюсь настроить веб-службу, в которой у меня есть 2 контроллера, каждый из которых ссылается на репозиторий, который, в свою очередь, ссылается на контекст подключения к базе данных. Строка подключения, используемая в контексте, зависит от используемого контроллера. Контроллеры знают только о своих соответствующих репозиториях, репозитории знают только о контексте своей базы данных, контекст знает только о строке подключения.
Если бы был какой-то способ узнать, какой контроллер использовался в InstancePerApiRequest, я мог бы написать некоторую логику в корне композиции, чтобы сделать это, но это, похоже, невозможно.
Поскольку InstancePerApiControllerType работает только с поддерживаемыми службами, я не могу сделать, например
container.Register(c =>
new MyDBContext("my string"))
.InstancePerApiControllerType(typeof(DocumentController))
.As<MyBaseDBContext>();
container.Register(c =>
new MyDBContext("my other string"))
.InstancePerApiControllerType(typeof(WorkflowController))
.As<MyBaseDBContext>();
Это моя настройка:
var builder = new ContainerBuilder();
var config = GlobalConfiguration.Configuration;
builder.RegisterHttpRequestMessage(config);
builder.RegisterWebApiFilterProvider(config);
var container = builder.Build();
var configurator = new AutofacConfigurator();
configurator.Configure(builder);
var resolver = new AutofacWebApiDependencyResolver(container);
GlobalConfiguration.Configuration.DependencyResolver = resolver;
// controllers
container.RegisterApiControllers(typeof(DocumentController).Assembly);
container.RegisterApiControllers(typeof(WorkflowController).Assembly);
// wire up context string for each controller here
// document
container.Register(c =>
{
var connectionString = ConfigurationManager.AppSettings[AppSettingsNames.DatabaseConnection];
var newConnectionContext = new SqlServerConnectionContext(connectionString) { ProductID = productId };
newConnectionContext.Open();
return newConnectionContext;
}).InstancePerApiRequest()
.As<ISqlServerConnectionContext>()
.As<IConnectionContext>();
// workflow
container.Register(c =>
{
var connectionString = ConfigurationManager.AppSettings[AppSettingsNames.WorkflowDatabaseConnection];
var newConnectionContext = new SqlServerConnectionContext(connectionString) { ProductID = productId };
newConnectionContext.Open();
return newConnectionContext;
}).InstancePerApiRequest()
.As<ISqlServerConnectionContext>()
.As<IConnectionContext>();
Очевидно, что этот код всегда выдает мне вторую строку подключения, поскольку он просто перезаписывает первую конфигурацию.
Я начал изучать области действия, но не смог понять, как и где их настроить, чтобы все работало, если они будут работать для решения этой проблемы.
Ранее я решал эту проблему с Ninject, используя an IHttpControllerActivator
, который принимал контейнер в конструкторе, а затем использовал его для замены привязки объекта контекста базы данных с помощью правильной строки подключения. Проблема в том, что контекст базы данных был «волшебным» настроен с точки зрения корня композиции. Я надеюсь избежать этого с помощью Autofac, но, похоже, мне может понадобиться изучить аналогичное решение. По крайней мере, Autofac дал бы мне возможность иметь по одномуПробовал, не смог заставить его работать (пока?). IHttpControllerActivator
на контроллер, что сделало бы вещи немного более декларативными, вместо того, чтобы иметь там логику.
Есть ли лучший / более простой способ сделать это?
Обновить:
Возможно, эти более простые классы могли бы сделать структуру более понятной. В этом случае я хочу, чтобы RepositoryA получал connectionA и вводил его в контроллер A, а RepositoryB получал ConnectionB и вводился в ControllerB:
interface IController
{
string Go();
}
interface IRepository
{
string Connect();
}
interface IConnection
{
string ConnectionString { get; set; }
}
class ControllerA : IController
{
private IRepository _repository;
public ControllerA(IRepository repository)
{
_repository = repository;
}
public string Go()
{
return "Controller A" _repository.Connect();
}
}
class ControllerB : IController
{
private IRepository _repository;
public ControllerB(IRepository repository)
{
_repository = repository;
}
public string Go()
{
return "Controller B" _repository.Connect();
}
}
class RepositoryA : IRepository
{
private IConnection _connection;
public RepositoryA(IConnection connection)
{
_connection = connection;
}
public string Connect()
{
return String.Format("Repository A Connected to {0}", _connection.ConnectionString);
}
}
class RepositoryB : IRepository
{
private IConnection _connection;
public RepositoryB(IConnection connection)
{
_connection = connection;
}
public string Connect()
{
return String.Format("Repository B Connected to {0}", _connection.ConnectionString);
}
}
Комментарии:
1. Кажется немного странным, что у вас нет нескольких интерфейсов reposistory. Разве у каждого репозитория нет своих собственных методов для выполнения уникальной работы. Если нет, вы должны иметь возможность использовать именованные экземпляры. Я не использовал Ninject, но я бы предположил, что они поддерживают регистрацию и разрешение передачи имени в дополнение к типу интерфейса.
2. Мы делаем, но все они в конечном итоге зависят от IConnectionContext, который является конкретным подключением к базе данных. Это тот, с которым у меня возникли проблемы, потому что его нужно устанавливать для каждого запроса API, в зависимости от того, какой контроллер активен для этого запроса.
3. Итак, 1 класс репозитория будет использовать другую строку подключения в зависимости от вызывающего контроллера?
4. @Adam47 Точно. Все репозитории (в конечном итоге) принимают зависимость от IConnectionContext, благодаря которой они узнают, с какой базой данных взаимодействовать. IConnectionContext — это объект, который имеет строку подключения, и это объект, который должен изменять конфигурацию в зависимости от того, какой контроллер используется для текущего запроса. В конечном итоге мы могли бы получить объекты, отличные от IConnectionContext, но нам всегда нужно будет настраивать их в зависимости от того, какой контроллер Web API используется.