I'm trying to get Autofac dependency injection for ef core in a WPF Core 3.1 app but I cannot do any migrations.
I keep getting this error when trying to add migrations through CLI:
Unable to create an object of type 'ApplicationDbContext'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
Any suggestion?
Those are my methods:
Startup Class:
public partial class App : Application
{
public IServiceProvider ServiceProvider { get; private set; }
private void App_OnStartup(object sender, StartupEventArgs e)
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddTransient(typeof(MainWindow));
ServiceProvider = serviceCollection.BuildServiceProvider();
var bootstrapper = new Bootstrapper(ServiceProvider);
var container = bootstrapper.Bootstrap();
var mainWindow = container.Resolve<MainWindow>();
//var mainWindow = new MainWindow(
// new RecruitmentViewModel(new ContractorDataService()));
mainWindow.Show();
}
}
Bootstrapper which acts like a start-up
private readonly IServiceProvider _serviceProvider;
public Bootstrapper(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public IContainer Bootstrap()
{
var builder = new ContainerBuilder();
var config = new ConfigurationBuilder();
config.AddJsonFile("appsettings.json");
var configuration = config.Build();
builder.RegisterType<MainWindow>().AsSelf();
builder.RegisterType<RecruitmentViewModel>().AsSelf();
builder.RegisterType<ContractorDataService>().As<IContractorDataService>();
RegisterContext<ApplicationDbContext>(builder, configuration);
return builder.Build();
}
public void RegisterContext<TContext>(ContainerBuilder builder, IConfiguration configuration)
where TContext : DbContext
{
builder.Register(componentContext =>
{
var dbContextOptions = new DbContextOptions<TContext>(new Dictionary<Type, IDbContextOptionsExtension>());
var optionsBuilder = new DbContextOptionsBuilder<TContext>(dbContextOptions)
.UseApplicationServiceProvider(_serviceProvider)
.UseSqlServer(configuration.GetConnectionString("DefaultConnection"),
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();
}
Related
I'm building .NetCore console app which uses SimpleRabbit (RabbitMQ).
I am attempting to use HostBuilder() to this purpose (dependecy injection, configuration, services).
I create a SimpleInjector container in the Bootstrap class.
I would like to inject this container for use into HostBuilder.
As you can see below, the FooSubscriberService constructor is expecting a container.
I see HostBuilder exposes a ConfigureContainer() method which sounds like what I'm looking for.
I've looked around but I'm unclear as how to use this/inject the container.
Supporting Class Snippets
public class FooSubscriberService : IFooSubscriberService
{
Container container;
private readonly ILogger<FooSubscriberService> logger;
public FooSubscriberService(Container container, ILogger<DiscoverySubscriberService> logger)
{
this.container = container;
this.logger = logger;
}
....
}
public class QueueManagementService : BasicRabbitService, IQueueManagementService
{
ILogger _logger;
public QueueManagementService(RabbitConfiguration config, ILogger<QueueManagementService> _logger) : base(config)
{
this._logger = _logger;
}
....
}
Container Creation
public class Bootstrap
{
public static Container container;
public static void ConfigureServices(IConfigurationRoot configurationRoot)
{
container = new SimpleInjector.Container();
container.Options.ResolveUnregisteredConcreteTypes = false;
container.Options.EnableAutoVerification = false;
RabbitConfiguration rabbitConfiguration = new RabbitConfiguration();
configurationRoot.GetSection("RabbitConfiguration").Bind(rabbitConfiguration);
var runtimeModel = new RuntimeConfigurationModel();
var runtimeStatus = new RuntimeServicesStatus();
container.ConfigureServices(rabbitConfiguration, runtimeModel, runtimeStatus, searchJobStatus);
}
}
Code Snippet for initializing and running HostBuilder.
Bootstrap.ConfigureServices(configurationRoot);
var container = Bootstrap.container;
var builder = new HostBuilder()
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
})
.ConfigureServices((context, services) =>
{
var config = context.Configuration;
services
.AddRabbitConfiguration(configurationRoot.GetSection("RabbitConfiguration"))
.AddSubscriberConfiguration(configurationRoot.GetSection("Subscribers"))
.AddPublisherServices()
.AddSubscriberServices()
.AddSingletonMessageHandler<FooSubscriberService>()
.AddSingleton<IQueueManagementService, QueueManagementService>()
.AddSingleton<IHostedService, FooConsoleService>();
});
// ??? .ConfigureContainer<Container>( => { }); ???
await builder.RunConsoleAsync();
What is the method for injecting my Container into HostBuilder()?
Are there additional NuGet packages I need to use?
I'm implementing a DI container in a .NET Core WPF app.
The below setup works fine, but I don't know how to inject an interface in any of my view model's constructor.
In the MainWindow I want to instantiate two or more pages with their respective dependencies (side menu item click). How do I get the dependency container in order to resolve those dependencies?
public partial class App : Application
{
private readonly ServiceProvider _serviceProvider;
private IConfiguration _configuration { get; set; }
public App()
{
InitializeConfiguration();
var serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection);
_serviceProvider = serviceCollection.BuildServiceProvider();
}
private void InitializeConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
_configuration = builder.Build();
}
private void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IConfiguration>(s => _configuration);
services.AddTransient<IRepository, Repository>();
services.AddTransient<IDeliveryService, DeliveryService>();
services.AddSingleton<MainWindow>();
}
private void OnStartup(object sender, StartupEventArgs e)
{
var mainWindow = _serviceProvider.GetService<MainWindow>();
mainWindow.Show();
}
}
Many thanks!
I nuget the xunit.DependencyInjection package and created my construction with my interfaces. The test case compiles but when I run xunits it does not execute my constructor dependency injection.
public class TestSuite{
IARepository _aRepository;
IBRepository _bRepository;
public TestSuite(IARepository aRepository, IBRepository bRepository)
{
_aRepository = aRepository;
_bRepository = bRepository;
}
}
The GitHub suggests that constructor injection is possible:
https://github.com/pengweiqhca/Xunit.DependencyInjection/tree/master/Xunit.DependencyInjection.Test
Startup.cs
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(System.IO.Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", false, true)
.Build();
var connectionString = configuration.GetConnectionString("A_DbCoreConnectionString");
services.AddDbContext<AContext>(options1 => options1.UseSqlServer(connectionString));
connectionString= configuration.GetConnectionString("B_DbCoreConnectionString");
services.AddDbContext<BContext>(options2 => options2.UseSqlServer(connectionString));
services.AddTransient<IARepository, ARepository>();
services.AddTransient<IBRepository, BRepository>();
}
}
A and B Repository.cs
public class ARepository :IARepository
{
public AContext _dbContext;
public ARepository(AContext dbContext)
{
_dbContext = dbContext;
}
...
}
public class BRepository :IBRepository
{
public BContext _dbContext;
public BRepository(BContext dbContext)
{
_dbContext = dbContext;
}
...
}
I was able to get the dependency injection to work in xunit once I added the startup.cs code
Solution startup.cs file in your XUnit Project:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(System.IO.Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", false, true)
.Build();
var connectionString = configuration.GetConnectionString("A_DbCoreConnectionString");
services.AddDbContext<AContext>(options1 => options1.UseSqlServer(connectionString));
connectionString= configuration.GetConnectionString("B_DbCoreConnectionString");
services.AddDbContext<BContext>(options2 => options2.UseSqlServer(connectionString));
services.AddTransient<IARepository, ARepository>();
services.AddTransient<IBRepository, BRepository>();
}
}
In .Net 6 and later Startup.cs dont work automatically.
The minimun setup required is to create a ServiceCollection and build it to a ServiceProvider:
public class EfRepositoryTests
{
private ServiceProvider services;
public EfRepositoryTests()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddDbContext<BloggingContext>(ServiceLifetime.Singleton);
serviceCollection.AddSingleton<EfRepository<Blog>>();
this.services = serviceCollection.BuildServiceProvider();
}
[Fact]
public async Task CreateAsync()
{
var ctx = this.services.GetRequiredService<BloggingContext>();
var repository = this.services.GetRequiredService<EfRepository<Blog>>();
//...
}
}
I am trying to use IHttpClientFactory in my solution instead of just instances of HttpClient.
startup.cs:
services.AddHttpClient("Test", client =>
{
client.BaseAddress = new Uri("http://localhost:57863");
client.Timeout = new TimeSpan(0, 0, 30);
client.DefaultRequestHeaders.Clear();
});
and in my services in need of a HttpClient:
private readonly Uri _clusterLinuxUri;
private readonly IHttpClientFactory _clientFactory;
public LiasseService(ConfigSettings settings, IHttpClientFactory clientFactory)
{
_clusterLinuxUri = new Uri($"{settings.LinuxClusterEndpoint}");
_clientFactory = clientFactory;
}
public async Task<LiasseDetails> CreateLiasseAsync(LiasseCreate liasseData)
{
using (var response = await _clientFactory.CreateClient("Test")
.PostAsJsonAsync($"{_clusterLinuxUri}{_createPath}", liasseData))
{
await response.CheckHttpError($"{nameof(CreateLiasseAsync)} - error in CL");
var detailsList = await response.Content.ReadAsAsync<LiasseDetailsList>();
return detailsList.Details.FirstOrDefault();
}
}
The part I haven't figured out is how to inject it in Autofac.
program.cs
private static void Main()
{
try
{
var builder = new ContainerBuilder();
builder.RegisterModule(new GlobalAutofacModule());
builder.RegisterServiceFabricSupport();
builder.RegisterStatelessService<FacadeCore>("xxx.FacadeCoreType");
using (builder.Build())
{
ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(FacadeCore).Name);
Thread.Sleep(Timeout.Infinite);
}
}
catch (Exception e)
{
ServiceEventSource.Current.ServiceHostInitializationFailed(e.ToString());
throw;
}
}
public class GlobalAutofacModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<ConfigSettings>();
builder.RegisterType<PaymentService>().As<IPaymentService>();
builder.RegisterType<MailerService>().As<IMailerService>();
builder.RegisterType<LiasseService>().As<ILiasseService>();
builder.RegisterType<AnalyseFinanciereService>().As<IAnalyseFinanciereService>();
builder.RegisterType<ApimService>().As<IApimService>();
builder.RegisterType<UserRepository>().As<IUserRepository>();
builder.RegisterType<ApplicationProcessRepository>().As<IApplicationProcessRepository>();
builder.RegisterType<LiasseRepository>().As<ILiasseRepository>();
builder.RegisterType<CustomUserIdProvider>().As<IUserIdProvider>();
}
}
Am I supposed to create some custom client that implements IHttpClientFactory to be able to inject it? How should I do this? Any examples? Thanks.
Please see Interface documentation here
So to answer your question:
1) Using IServiceCollection from 'ConfigureServices' method call .AddHttpClient()
2) Create new Autofac container builder and populate it with IServiceCollection mentioned above
3) From ConfigureServices method return new AutofacServiceProvider
public IServiceProvider ConfigureServices(IServiceCollection services)
{
...
services.AddHttpClient();
var containerBuilder = new ContainerBuilder();
containerBuilder.Populate(services);
var container = containerBuilder.Build();
return new AutofacServiceProvider(container);
}
P.S.
Make sure to add - Autofac.Extensions.DependencyInjection nuget package, in order to be able to use AutofacServiceProvider class.
Apart from doing using a WebHostBuilder based solution similar to what you get when creating a vanilla Asp.Net Core Service Fabric project, you can alternatively just do this
public class HttpClientModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.Register(_ =>
{
var services = new ServiceCollection();
services.AddHttpClient();
var containerBuilder = new ContainerBuilder();
containerBuilder.Populate(services);
var container = containerBuilder.Build();
return new AutofacServiceProvider(container);
})
.As<IServiceProvider>()
.SingleInstance();
builder.Register(ctx =>
{
var scope = ctx.Resolve<IComponentContext>();
var provider = scope.Resolve<IServiceProvider>();
var factory = provider.GetService<IHttpClientFactory>();
return factory.CreateClient();
}).As<HttpClient>();
}
}
It's .NET Core 2.0 console application, using DI how can I pass parameters to constructor.
RabbitMQPersistentConnection class needs to pass parameter on constructer
RabbitMQPersistentConnection(ILogger logger, IConnectionFactory connectionFactory, IEmailService emailService);
My instance
var _emailService = sp.GetRequiredService();
not working like this when I initialize it as service
Program.cs
public static class Program
{
public static async Task Main(string[] args)
{
// get App settings
var builder = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
IConfigurationRoot configuration = builder.Build();
//Initilize Service Collection
#region Initilize Service Collection
var serviceProvider = new ServiceCollection()
.AddLogging()
.AddEntityFrameworkSqlServer().AddDbContext<EmailDBContext>(option => option.UseSqlServer(configuration.GetConnectionString("connection_string")))
.AddSingleton<IEmailConfiguration>(configuration.GetSection("EmailConfiguration").Get<EmailConfiguration>())
.AddScoped<ISMTPService, SMTPService>()
.AddScoped<IEmailService, EmailService>()
.BuildServiceProvider();
#endregion
.ConfigureServices((hostContext, services) =>
{
services.AddLogging();
services.AddHostedService<LifetimeEventsHostedService>();
services.AddHostedService<TimedHostedService>();
services.AddEntityFrameworkSqlServer();
services.AddScoped<IRabbitMQPersistentConnection, RabbitMQPersistentConnection>(sp =>
{
var logger = sp.GetRequiredService<ILogger<RabbitMQPersistentConnection>>();
var _emailService = sp.GetRequiredService<IEmailService>(); // Not Working. :(
var _rabbitMQConfiguration = configuration.GetSection("RabbitMQConfiguration").Get<RabbitMQConfiguration>();
var factory = new ConnectionFactory()
{
HostName = _rabbitMQConfiguration.EventBusConnection
};
if (!string.IsNullOrEmpty(_rabbitMQConfiguration.EventBusUserName))
{
factory.UserName = _rabbitMQConfiguration.EventBusUserName;
}
if (!string.IsNullOrEmpty(_rabbitMQConfiguration.EventBusPassword))
{
factory.Password = _rabbitMQConfiguration.EventBusPassword;
}
return new RabbitMQPersistentConnection(logger, factory, _emailService);
});
})
.Build();
await host.RunAsync();
}
}
RabbitMQPersistentConnection.cs
public class RabbitMQPersistentConnection : IRabbitMQPersistentConnection
{
private readonly IConnectionFactory _connectionFactory;
EventBusRabbitMQ _eventBusRabbitMQ;
IConnection _connection;
IEmailService _emailService;
private readonly ILogger _logger;
bool _disposed;
public RabbitMQPersistentConnection(ILogger<RabbitMQPersistentConnection> logger, IConnectionFactory connectionFactory, IEmailService emailService)
{
_connectionFactory = connectionFactory ?? throw new ArgumentNullException(nameof(connectionFactory));
_emailService = emailService;
_logger = logger;
}
}
You are creating your own IServiceProvider in your "Initilize Service Collection" region, which is a different instance of IServiceProvider to the one that you're using here:
var _emailService = sp.GetRequiredService<IEmailService>();
The registrations you've made in your region are simply being thrown away. In order to resolve this, you can just pull those registrations into your HostBuilder.ConfigureServices callback function:
.ConfigureServices((hostContext, services) =>
{
services.AddLogging();
services.AddHostedService<LifetimeEventsHostedService>();
services.AddHostedService<TimedHostedService>();
services.AddEntityFrameworkSqlServer();
services.AddDbContext<EmailDBContext>(option => option.UseSqlServer(configuration.GetConnectionString("connection_string")));
services.AddSingleton<IEmailConfiguration>(configuration.GetSection("EmailConfiguration").Get<EmailConfiguration>());
services.AddScoped<ISMTPService, SMTPService>();
services.AddScoped<IEmailService, EmailService>();
services.AddScoped<IRabbitMQPersistentConnection, RabbitMQPersistentConnection>(sp =>
{
var logger = sp.GetRequiredService<ILogger<RabbitMQPersistentConnection>>();
var _emailService = sp.GetRequiredService<IEmailService>();
var _rabbitMQConfiguration = configuration.GetSection("RabbitMQConfiguration").Get<RabbitMQConfiguration>();
var factory = new ConnectionFactory()
{
HostName = _rabbitMQConfiguration.EventBusConnection
};
if (!string.IsNullOrEmpty(_rabbitMQConfiguration.EventBusUserName))
{
factory.UserName = _rabbitMQConfiguration.EventBusUserName;
}
if (!string.IsNullOrEmpty(_rabbitMQConfiguration.EventBusPassword))
{
factory.Password = _rabbitMQConfiguration.EventBusPassword;
}
return new RabbitMQPersistentConnection(logger, factory, _emailService);
});
})