I have a .net core 2.1 console app. I'm building the container as below:
public IServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
// Register services here.
// Unity container interface:
var containerFactory = new ServiceProviderFactory(null);
IUnityContainer container = containerFactory.CreateBuilder(services);
IServiceProvider serviceProvider = containerFactory.CreateServiceProvider(container);
return serviceProvider;
}
I need to provide constructor arguments to some of the services registered in the container. However, I do not want to use something like options pattern. Instead, I would like to access the Unity functionality with something like below:
var container = (UnityContainer)serviceProvider;
var foo = container.Resolve<IFoo>(new ParameterOverrides<Foo> { "name": "myFoo" });
The above approach is not working and throw the error:
Unable to cast object of type 'Unity.Microsoft.DependencyInjection.ServiceProvider' to type 'Unity.UnityContainer'.
Is there a way to do this or am I restricted to IServiceProvider interface even though I'm using Unity implementation?
EDIT:
So, I can't find a way to switch from IServiceProvider to IUnityContainer with something like a cast. So, I changed the above code a bit to return Unity container instead:
public IServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
// Register services here.
// Unity container interface:
var containerFactory = new ServiceProviderFactory(null);
IUnityContainer container = containerFactory.CreateBuilder(services);
return container;
}
Now, I can use the usual Unity DI in the .Net Core App.
You should use NugetPackage which integrates Unity with ServiceProvider provided by .NET Core.
https://github.com/unitycontainer/microsoft-dependency-injection
Related
I am trying to use dependency injection in a .NET Core Console App.
There are a lot of articles about it, but did not find one that fixes my problem.
I am trying to use services original from an aspnet core web app, thats why i have the WebHost.
My main problem is to create an instance of my own class, all the dependency seems to work, and my console app starts up.
I have this code in my Program class:
static void Main(string[] args)
{
var host = WebHost.CreateDefaultBuilder(args)
.UseKestrel(options => options.AddServerHeader = false)
.UseStartup<Startup>()
.Build();
var services = new ServiceCollection().AddLogging();
var container = new Container();
var serviceProvider = container.GetInstance<IServiceProvider>();
This code will not compile due to this error:
'Container' does not contain a definition for 'GetInstance'
How can i create an instance of my custom class App which has this implementation:
public class App
{
private readonly IProductService _productService;
public App(IProductService productService)
{
_productService = productService;
}
}
You don't even need to create your own ServiceCollection or ServiceProvider in this scenario - You can just use the IWebHost's Services property that you already have:
var app = host.Services.GetService<App>();
WebHost.CreateDefaultBuilder already adds the logging services, so there's no need to do that either.
Note: I'm assuming that you've registered your App and IProductService types in Startup.ConfigureServices.
I have no idea what a "Container" is in your setting, but you normally create a service provider by calling BuildServiceProvider on the ServiceCollection.
var provider = services.BuildServiceProvider();
var instance = provider.GetService<App>();
You will need to register both App and whatever IProductService you want with the services collection first though.
I am working on a new chat bot using Azure Bot Service and QnAMaker. We are using BotBuilder middleware, including custom middleware, to tailor the bot behavior.
One of the middlewares will be calling an Azure function and I would like to use the new HttpClientFactory feature with the custom middleware - but this requires dependency injection.
How can I use dependency injection in BotBuilder middleware like you do with regular .NET Core middleware?
When you look at the bot configuration in the Startup.cs, you can see how it requires you to new up all of the bot dependencies:
services.AddHttpClient<MyFunctionClient>(client =>
{
client.BaseAddress = new Uri(mySettings.GetValue<string>("myFunctionUrl"));
client.DefaultRequestHeaders.Add("x-functions-key", mySettings.GetValue<string>("myFunctionKey"));
});
services.AddBot<QnAMakerBot>(options =>
{
options.CredentialProvider = new ConfigurationCredentialProvider(Configuration);
options.ConnectorClientRetryPolicy = new RetryPolicy(
new BotFrameworkHttpStatusCodeErrorDetectionStrategy(),
3,
TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds(20),
TimeSpan.FromSeconds(1));
var middleware = options.Middleware;
middleware.Add(new ConversationState<ChatLog>(new MemoryStorage()));
middleware.Add(new MyCustomMiddleware()); // <- I want to inject a typed HttpClient here
//... etc. ....
Is there a different way to configure the bot that allows for dependency injection?
If MyCustomMiddleware requires a typed HttpClient in its constructor, I have to create a new instance right here, so I don't get the benefit of the DI and the configuration I just set up.
While I am not a fan of service locator pattern, the current design of the bot configuration is not very dependency injection friendly.
Using the nature of how the bot middleware are setup but having to provide a new instance during startup, I came up with the following work around.
public class BotMiddlewareAdapter<TMiddleware> : IMiddleware
where TMiddleware : IMiddleware {
private readonly Lazy<TMiddleware> middleware;
public BotMiddlewareAdapter(IServiceCollection services) {
middleware = new Lazy<TMiddleware>(() =>
services.BuildServiceProvider().GetRequiredService<TMiddleware>());
}
public Task OnTurn(ITurnContext context, MiddlewareSet.NextDelegate next) {
return middleware.Value.OnTurn(context, next);
}
}
It takes the IServiceCollection as an explicit dependency and defers the creation of the service provider and eventual resolution of the actual middleware in a factory delegate.
It can then be implemented using
middleware.Add(new BotMiddlewareAdapter<MyCustomMiddleware>(services));
When the adapter is invoked it will lazy resolve the intended middleware on initial call and then invoke it.
In fact you can take this another step further and convert it to an extension method
public static class BotBuilderMiddlewareExtension {
public static void Add<TMiddleware>(this IList<IMiddleware> middleware, IServiceCollection services)
where TMiddleware : IMiddleware {
middleware.Add(new BotMiddlewareAdapter<TMiddleware>(services));
}
}
Which simplifies the setup to
middleware.Add<MyCustomMiddleware>(services);
I'm using ASP.NET Core and Autofac. Almost everything is registered as per lifetime scope ("per request"). So my database context DbContext is the same instance throughout a request.
However I have a singleton which also depends on DbContext. To avoid a captive dependency, it is injected as Func<Owned<DbContext>>, which means a new DbContext instance each time.
The problem is I need the same instance, as everywhere else during the request, not a new one.
I want to avoid a captive dependency bug, but I also want the same instance. Is that possible via tagging or a custom registration?
From the comments the least "architectural" painful approach may be by creating your own Scoped<T> class which will resolve the DbContext from current HttpContext
// Use an interface, so we don't have infrastructure dependencies in our domain
public interface IScoped<T> where T : class
{
T Instance { get; }
}
// Register as singleton too.
public sealed class Scoped<T> : IScoped<T> where T : class
{
private readonly IHttpContextAccessor contextAccessor;
private HttpContext HttpContext { get; } => contextAccessor.HttpContext;
public T Instance { get; } => HttpContext.RequestServices.GetService<T>();
public Scoped(IHttpContextAccessor contextAccessor)
{
this.contextAccessor = contextAccessor ?? throw new ArgumentNullException(nameof(contextAccessor));
}
}
Register it as
// Microsoft.Extensions.DependencyInjection
services.AddSingleton(typeof(IScoped<>), typeof(Scoped<>);
// Autofac
containerBuilder.RegisterType(typeof(Scoped<>))
.As(typeof(IScoped<>));
Then inject this into your validator service.
public class CustomerValidator: AbstractValidator<Customer>
{
private readonly IScoped<AppDbContext> scopedContext;
protected AppDbContext DbContext { get } => scopedContext.Instance;
public CustomValidator(IScoped<AppDbContext> scopedContext)
{
this.scopedContext = scopedContext ?? throw new ArgumentNullException(nameof(scopedContext));
// Access DbContext via this.DbContext
}
}
This way you can inject any scoped service w/o further registrations.
Additional notes
Autofac is considered a "conformer" (see docs) DI and integrates well with ASP.NET Core and Microsoft.Extensions.DependencyInjection.
From the documentation
public IServiceProvider ConfigureServices(IServiceCollection services)
{
// Add services to the collection.
services.AddMvc();
// Create the container builder.
var builder = new ContainerBuilder();
// Register dependencies, populate the services from
// the collection, and build the container. If you want
// to dispose of the container at the end of the app,
// be sure to keep a reference to it as a property or field.
builder.RegisterType<MyType>().As<IMyType>();
builder.Populate(services);
this.ApplicationContainer = builder.Build();
// Create the IServiceProvider based on the container.
return new AutofacServiceProvider(this.ApplicationContainer);
}
There a few subtle differences to the default usage of Startup class and Microsoft.Extensions.DependencyInjection container.
ConfigureServices isn't void anymore, it returns IServiceProvider. This will tell ASP.NET Core to use the returned provider instead of DefaultServiceProvider from Microsoft.Extensions.DependencyInjection.
We return the Autofac container adapter: new AutofacServiceProvider(this.ApplicationContainer) which is the root container.
This is important to make ASP.NET Core use the container everywhere in ASP.NET Core, even inside middlewares which resolve per request dependencies via HttpContext.RequestedServices.
For that reasons you can't use .InstancePerRequest() lifetime in Autofac, because Autofac isn't in control of creating scopes and only ASP.NET Core can do it. So there is no easy way to make ASP.NET Core use Autofac's own Request lifetime.
Instead ASP.NET Core will create a new scope (using IServiceScopeFactory.CreateScope()) and use a scoped container of Autofac to resolve per-request dependencies.
I want to integrate Autofac to my API. Solution is split on several projects so that everything stays decoupled. I have set up my configure services like this:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
// Add framework services.
...
...
// Autofac
var builder = new ContainerBuilder();
builder.RegisterType<RouteRepository>().As<IRouteRepository>();
builder.Populate(services);
ApplicationContainer = builder.Build();
return new AutofacServiceProvider(ApplicationContainer);
}
However now with this code integrated, my API won't start anymore. If I start it in debug mode, I get no errors, but I don't get response either.
API landing route is pretty straightforward:
public IActionResult GetIndex()
{
return Ok("You are seeing this because controller is working!");
}
Also, what might be connected to the problem is that RouteRepository takes one variable as an argument in the constructor and I don't know where can I define what will be passed through? There is no config file by default.
If you're saying that you have one dependency for your RouteRepository, then you have to notify Autofac container how to resolve that:
// singletone
builder.RegisterInstance(new TaskRepository())
.As<ITaskRepository>();
// or instance based creation
builder.Register(c => new LogManager(DateTime.Now))
.As<ILogger>();
Or Autofac couldn't resolve your type.
In Ninject I can get object needed for interface by using class WebContainerManager
Ninject definition:
var logManager = new LogManagerAdapter();
container.Bind<ILogManager>().ToConstant(logManager);
Ninject usage:
var log = WebContainerManager.Get<ILogManager>().GetLog(typeof(WebApiApplication));
My question is how to do the same in Autofac, to get needed class for interface?
UPDATE 1: Im using WebAPi 2, not MVC.
If you need access to Autofac container from the class that was resolved by Autofac itself, then you can specify dependency on IComponentContext that is automatically provided by Autofac.
Example:
public void SomeComponent(IComponentContext context)
{
this.context = context;
}
...
// somewhere inside SomeComponent
context.Resolve<ILogManager>();
If your code is running inside ASP.Net environment, then you most probably set its DependencyResolver, thus you can always access it like:
DependencyResolver.Current.GetService<ILogManager>();
but as it is already mentioned in other comments, Service Locator is an anti-pattern that should be avoided.
In order to integrate autofac container with standard MVC dependency resolution mechanism you need to:
install Autofac.Mvc5 nuget package
set DependencyResolver with the following code
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
And in case you don't mind having explicit dependency on Autofac in your application code you can access global Autofac resolver reference the same way you use Ninject WebContainerManager:
var log = AutofacDependencyResolver.Current.Resolve<ILogManager>().GetLog(typeof(WebApiApplication));
You can create your builder.
var builder = new ContainerBuilder();
// Usually you're only interested in exposing the type
// via its interface:
builder.RegisterType<SomeType>().As<IService>();
// However, if you want BOTH services (not as common)
// you can say so:
builder.RegisterType<SomeType>().AsSelf().As<IService>();
Then you will be able to build your IoC:
IContainer Container = builder.Build();
And a simple example of How to get resource from container:
// Create the scope, resolve your IService,
// use it, then dispose of the scope.
using (var scope = Container.BeginLifetimeScope())
{
var writer = scope.Resolve<IService>();
writer.DoSomething();
}