У меня есть новый проект .NET Core Web API, который имеет следующую структуру проектов:
API -> Бизнес / Домен -> Инфраструктура
API очень тонкий, только методы API. Уровень Бизнес / Домен имеет всю мою бизнес-логику. И, наконец, на моем уровне инфраструктуры есть классы БД, использующие EF Core 2.0.
Я знаю, что с помощью встроенной в .NET Core инъекции зависимостей я могу добавить ссылку из проекта API в проект инфраструктуры, а затем добавить следующий код в файл StartUp.cs:
services.AddDbContext<MyContext>(options => options.UseSqlServer(connectionString));
Однако я хотел бы сохранить более традиционное разделение проблем. До сих пор я добавил модуль в свой уровень инфраструктуры, который пытается выполнить регистрацию следующим образом:
builder.Register(c =>
{
var config = c.Resolve<IConfiguration>();
var opt = new DbContextOptionsBuilder<MyContext>();
opt.UseSqlServer(config.GetSection("ConnectionStrings:MyConnection:ConnectionString").Value);
return new MyContext(opt.Options);
}).AsImplementedInterfaces().InstancePerLifetimeScope();
Однако DBContext не регистрируется. Любой класс, который пытается получить доступ к внедренному DBContext, не может разрешить параметр.
Есть ли способ зарегистрировать DBContext в отдельном проекте с помощью AuftoFac в проекте .NET Core Web API?
@AlexanderLeonov Спасибо за отзыв. Я обновил вопрос, добавив больше информации.
Это лучше. Похоже, мое предположение было правильным - вы должны регистрировать класс MyContext
напрямую, а не интерфейсы, которые он реализует.
В желаемом проекте вы можете создать метод расширения, который добавляет контекст в коллекцию
public static class MyDataExtensions {
public static IServiceCollection AddMyData(this IServiceCollection services) {
//...
services.AddDbContext<MyContext>(options => options.UseSqlServer(connectionString));
//...
}
}
с этим тогда в вашем запуске это просто вопрос вызова расширения, предоставленного из другого проекта
services.AddMyData();
//...other settings
Проект API является корнем композиции, поэтому ему в любом случае необходимо знать все соответствующие зависимости. По крайней мере, с этим расширением вам не нужно делать прямую ссылку на используемый контекст db,
Думаю, проблема в том, что вы пытаетесь зарегистрировать MyContext()
с помощью AsImplementedInterfaces()
. Обычно DbContext не регистрируется. Вы должны зарегистрировать и разрешить сам класс.
Вы были правы. Я изменил .AsImplementedInterfaces () на .AsSelf (), и теперь он работает. Спасибо!
Я использую Autofac для регистрации как HttpContextAccessor
, так и DbContext
.
builder
.RegisterType<HttpContextAccessor>()
.As<IHttpContextAccessor>()
.SingleInstance();
builder
.RegisterType<AppDbContext>()
.WithParameter("options", DbContextOptionsFactory.Get())
.InstancePerLifetimeScope();
DbContextOptionsFactory
public class DbContextOptionsFactory
{
public static DbContextOptions<AppDbContext> Get()
{
var configuration = AppConfigurations.Get(
WebContentDirectoryFinder.CalculateContentRootFolder());
var builder = new DbContextOptionsBuilder<AppDbContext>();
DbContextConfigurer.Configure(
builder,
configuration.GetConnectionString(
AppConsts.ConnectionStringName));
return builder.Options;
}
}
DbContextConfigurer
public class DbContextConfigurer
{
public static void Configure(
DbContextOptionsBuilder<AppDbContext> builder,
string connectionString)
{
builder.UseNpgsql(connectionString).UseLazyLoadingProxies();
}
}
Если я хочу изменить строку подключения на основе строки запроса запроса. Как получить доступ к HttpContext
внутри класса DbContextOptionsFactory
? Я не могу разрешить HttpContextAccessor
внутри DbContextOptionsFactory
, поскольку он не собирался, когда мы обращаемся к DbContextOptionsFactory
.
Еще одно простое решение для Autofac версии 4.8.1
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddControllersAsServices();
services.AddDbContext<MyContext>(options => options.UseSqlServer(Configuration.GetConnectionString("ConnectionStrings:MyConnection:ConnectionString")));
var builder = new ContainerBuilder();
builder.Populate(services);
//...
// Your interface registration
//...
builder.Build(Autofac.Builder.ContainerBuildOptions.None);
}
Вот реализация, которую я использую - она имитирует Регистрация EF Core 3.1 с Autofac 4.9.4. Обязательно отрегулируйте прицелы в соответствии с вашими требованиями.
public void RegisterContext<TContext>(ContainerBuilder builder)
where TContext : DbContext
{
builder.Register(componentContext =>
{
var serviceProvider = componentContext.Resolve<IServiceProvider>();
var configuration = componentContext.Resolve<IConfiguration>();
var dbContextOptions = new DbContextOptions<TContext>(new Dictionary<Type, IDbContextOptionsExtension>());
var optionsBuilder = new DbContextOptionsBuilder<TContext>(dbContextOptions)
.UseApplicationServiceProvider(serviceProvider)
.UseSqlServer(configuration.GetConnectionString("MyConnectionString"),
serverOptions => serverOptions.EnableRetryOnFailure(5, TimeSpan.FromSeconds(30), null));
return optionsBuilder.Options;
}).As<DbContextOptions<TContext>>()
.InstancePerLifetimeScope();
builder.Register(context => context.Resolve<DbContextOptions<TContext>>())
.As<DbContextOptions>()
.InstancePerLifetimeScope();
builder.RegisterType<TContext>()
.AsSelf()
.InstancePerLifetimeScope();
}
Также на будущее. «Это, однако, не работает». - это ничего не объясняет и не помогает нам понять вашу проблему, поэтому мы должны угадать, в чем ваша проблема. Вы должны написать, что именно происходит - сообщение об ошибке, возникшее исключение или что-то еще, что вы подразумеваете под «Это не работает». Это может привести к сбою вашей ОС, выключению компьютера или взрыву электростанции - мы этого не знаем и поэтому не можем дать содержательный совет.