I have following interfaces, abstract classes etc.
public interface IAggregateRootMapping<T> : IAggregateDefinition where T : AggregateRoot
{
IEnumerable<Expression<Func<T, object>>> IncludeDefinitions { get; }
}
public abstract class AggregateRootMapping<T> : IAggregateRootMapping<T> where T : AggregateRoot
{
public abstract IEnumerable<Expression<Func<T, object>>> IncludeDefinitions { get; }
}
public class OrderAggregateRootMapping : AggregateRootMapping<Order>
{
public override IEnumerable<Expression<Func<Order, object>>> IncludeDefinitions
{
get
{
return new Expression<Func<Order, object>>[] {
order => order.Supplier
};
}
}
}
I use those in another class like this:
public class Repository<TAggregateRoot> : IRepository<TAggregateRoot> where TAggregateRoot : AggregateRoot
{
private readonly AggregateRootMapping<TAggregateRoot> _aggregateRootMapping;
public Repository(AggregateRootMapping<TAggregateRoot> aggregateRootMapping)
{
_aggregateRootMapping = aggregateRootMapping;
}
Do something...
}
How do I use the dependency injection of ASP.NET Core so that on runtime the matching class is injected?
For example if the AggregateRoot type class is Order than for the Repository class the OrderAggregateRootMapping class should be injected.
How do I use the ServiceCollection in ConfigureServices of the Startup class in .NET Core to accomplish this?
The dependency injection that comes by default is very basic. If you want to start wiring up rules based on generics, you will need to use a different implementation.
But, what you're after is still possible if you're willing to code the dependencies one by one.
In your Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<AggregateRootMapping<Order>, OrderAggregateRootMapping>();
services.AddScoped<Repository<Order>>();
// Add framework services.
services.AddMvc();
}
And then you can use your Repository class by injecting it into a controller, for example.
In ValuesController.cs
[Route("api/[controller]")]
public class ValuesController : Controller
{
private Repository<Order> _repository;
public ValuesController(Repository<Order> repository)
{
_repository = repository;
}
}
ValuesController will then receive an instance of Repository<Order> which will have been created with a OrderAggregateRootMapping.
Related
I'm working on an ASP.NET Core 6.0 Web API project in C#. I'm relatively new to this and dependency injection. So far I've used DI without much problem but I'm struggling to figure out how to pass an argument to a dependent class that is injected.
I have a set of services (ServiceA and ServiceB), which are inherited from a base class which is also a BackgroundService.
The parent class (BaseService) has a the DataProcessor class as a dependency. This processor class requires a constructor argument which indicates the ID of the service (which is available as a property on the corresponding service class).
My classes:
public abstract class BaseService : BackgroundService
{
public abstract string ID { get; }
protected DataProcessor dp;
protected BaseService(DataProcessor _dp)
{
dp = _dp;
}
}
public class ServiceA : BaseService
{
public override string ID => "SA";
public ServiceA(DataProcessor _dp): base(_dp)
{
}
}
public class ServiceB : BaseService
{
public override string ID => "SB";
public ServiceA(DataProcessor _dp): base(_dp)
{
}
}
public class DataProcessor
{
private string serviceID;
public DataProcessor(string _serviceID)
{
serviceID = _serviceID;
}
}
This is how I configure this on the startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<DataProcessor>();
// Add Service A
services.AddSingleton<ServiceA>();
services.AddHostedService<ServiceA>(p =>
{
return (ServiceA)p.GetServices<BaseService>().Where(a => a.GetType() == typeof(ServiceA)).First();
});
// Add Service B
services.AddSingleton<ServiceB>();
services.AddHostedService<ServiceB>(p =>
{
return (ServiceB)p.GetServices<BaseService>().Where(a => a.GetType() == typeof(ServiceB)).First();
});
}
The question is: how do I pass the ID property of the Service class to the DataProcessor class?
Note: I've simplified the code and setup for the purpose of the question.
I have tried by creating an Initialize(string ID) method on the DataProcessor class and calling that within the constructor of my BaseService class. This works but I don't like it as a solution, as I have to manually call this Initialize method.
I've searched a lot and read several similar questions/posts, but really couldn't figure it out.
It does seem like a relatively common request, so might be that I'm not using the DI correctly here or that I have the wrong expectation.
Thank you!
I need to inject dependency in Startup.cs
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddTransient<IAppService, AppService>();
//how to inject for the rest
}
}
to achieve the line below:
new AppService(new CacheRepository(new ConfigRepository()))
instead of below or others
new AppService(new ConfigRepository())
Decorator pattern with multiple implementation below:
public class ConfigRepository : IRepository
{
public async Task<IEnumerable<Data>> ReadDataAsync()
{
//...
}
}
public class CacheRepository : IRepository
{
private readonly IRepository _pository;
public CacheConfigRepository(IRepository repository)
{
_pository = repository;
}
public async Task<IEnumerable<Data>> ReadDataAsync()
{
//...
}
}
Environment:
.Net Core 2.2, Azure Functions
Update
Answer:
Thanks #Timo for providing the link below
https://github.com/khellang/Scrutor
How to overwrite a scoped service with a decorated implementation?
For decorating a service with a decorated cached service you can use the Scuter.
Example:
public class MyService : IMyService
{
private readonly IRepository _repository;
public MyService(IRepository repository)
{
_repository = repository;
}
public async Task<IEnumerable<Data>> ReadDataAsync()
{
//...
}
}
The decorated cache service:
public class MyCacheService : IMyService
{
private readonly IMyService _myService;
private readonly ICacheRepository _cacheRepository;
public MyCacheService(IMyService myService, ICacheRepository cacheRepository)
{
_myService = myService;
_cacheRepository = cacheRepository;
}
public async Task<IEnumerable<Data>> ReadDataAsync()
{
var cachedKey = "SomeKey";
(isCached,value) = await _cacheRepository.ReadDataAsync();
if (isCached)
retrun value;
var result = await _myService.ReadDataAsync();
return result;
}
}
Startup.cs:
services.AddSingelton<IMyService, MyService>();
services.Decorate<IMyService, MyCacheService>();
Note that in this example I've added a different interface ICacheRepository.
Contrary to popular belief, the decorator pattern is fairly easy to implement using the built-in container.
By using the extension methods in the linked answer, registering decorators becomes as simple as this:
public void ConfigureServices(IServiceCollection services)
{
// First add the regular implementation
services.AddTransient<IRepository, ConfigRepository>();
// Wouldn't it be nice if we could do this...
services.AddDecorator<IRepository>(
(serviceProvider, wrappedRepository) => new CacheConfigRepository(wrappedRepository));
// ...or even this?
services.AddDecorator<IRepository, CacheConfigRepository>();
}
I have the following problem and I currently have a solution using conditional dependency injection. I have read that this is a bad idea, such as here in the SimpleInjector docs. I have read a large number of posts now and have seen various things suggesting using Strategy, Factory patterns, etc. What I am really looking for is some specifics - i.e. an example of some code - about how to solve without conditional injection. I need more that "use a factory". Here's a simplified version of my code. This is in an MVC web app, thus the controllers.
public abstract class QAControllerBase : Controller
{
protected readonly QABusinessLayer _blQA;
public QAControllerBase(QABusinessLayer bl)
{
_blQA = bl;
}
[HttpGet]
public ActionResult PPR(string accession, string site)
{
var m = _blQA.popPPRViewModel(accession);
return View(m);
}
}
public class QASouthController : QAControllerBase
{
public QASouthController([QASouthBinding] QABusinessLayer bl) : base(bl)
{
}
// some actions that are specific to South
}
public class QANorthController : QAControllerBase
{
public QANorthController([QANorthBinding] QABusinessLayer bl) : base(bl)
{
}
// some actions that are specific to North
}
public abstract class QABusinessLayer
{
protected readonly IFullBaseRepo _repo;
public QABusinessLayer(IFullBaseRepo repo)
{
_repo = repo;
}
public abstract PPRViewModel popPPRViewModel(string accession);
protected PPRViewModel DoSomeCommonStuff(PPRViewModel model)
{
...
return model;
}
}
public class SouthBusinessLayer: QABusinessLayer
{
public SouthBusinessLayer([QASouthBinding] IFullBaseRepo repo) : base(repo)
{
}
public override PPRViewModel popPPRViewModel(string accession)
{
var m = new PPRViewModel();
// do some stuff that is specific to South
DoSomeCommonStuff(m);
return m;
}
}
public class NorthBusinessLayer : QABusinessLayer
{
public NorthBusinessLayer([QANorthBinding] IFullBaseRepo repo) : base(repo)
{
}
public override PPRViewModel popPPRViewModel(string accession)
{
var m = new PPRViewModel();
// do some stuff that is specific to North
DoSomeCommonStuff(m);
return m;
}
}
and here is the Ninject binding code that is pertinent:
kernel.Bind<QABusinessLayer>()
.To<SouthBusinessLayer>()
.WhenTargetHas<QASouthBinding>()
.InRequestScope();
kernel.Bind<QABusinessLayer>()
.To<NorthBusinessLayer>()
.WhenTargetHas<QANorthBinding>()
.InRequestScope();
The QASouthBinding and QANorthBinding are just simple attributes. I am not asking for Ninject specific example. Any code sample as to how this might be handled without using conditional or context based injection as I am now.
Make your QABusinessLayer class abstract.
Change your startup configuration to:
kernel
.Bind<SouthBusinessLayer>()
.To<SouthBusinessLayer>()
.InRequestScope();
kernel
.Bind<NorthBusinessLayer>()
.To<NorthBusinessLayer>()
.InRequestScope();
Change your controller constructors to accept a concrete business layer type:
public class QANorthController : QAControllerBase
{
public QANorthController(NorthBusinessLayer businessLayer) : base(businessLayer)
{
}
}
public class QASouthController : QAControllerBase
{
public QASouthController(SouthBusinessLayer businessLayer) : base(businessLayer)
{
}
}
Few things:
If Ninject auto-binds concrete types to the same type, then you don't need to manually configure the dependencies during startup.
You may want to use an interface rather than just passing your concrete BusinessLayer type.
My current Autofac config is working to resolve my ApiControllers in a WebApi.
Where I'm struggling is, I'm attempting to create a 'BaseApiController' with generic constructor parameters, but getting exception:
No constructors on type 'Service`1[WorldRegion]' can be found with the constructor finder 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder'.
Here is the code structure:
public interface IEntityKey { }
public class WorldRegion : IEntityKey { }
public interface IRepository<T> where T : IEntityKey { }
public interface IService<T> where T : IEntityKey { }
public abstract class Service<T> : IService<T> where T : IEntityKey { }
public interface IWorldRegionService : IService<WorldRegion> { }
public class WorldRegionService : Service<WorldRegion>, IWorldRegionService
{
private readonly IRepository<WorldRegion> _repository;
}
WORKING API Controller:
public class WorldRegionsController : BaseApiController
{
private readonly IWorldRegionService _worldRegionService;
private readonly ICultureService _cultureService;
public WorldRegionsController(IWorldRegionService worldRegionService, ICultureService cultureService)
{
_worldRegionService = worldRegionService;
_cultureService = cultureService;
}
}
WORKING Autofac config:
public static void Register(HttpConfiguration config, IAppBuilder app)
{
var builder = new ContainerBuilder();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
RegisterTypes(builder);
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
}
public static void RegisterTypes(ContainerBuilder builder)
{
// Context
builder.RegisterType<DataContext>().As<IDataContext>().InstancePerRequest();
// UOW
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerRequest();
// Repositories
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>)).InstancePerRequest();
// Services
builder.RegisterType<CultureService>().As<ICultureService>().InstancePerRequest();
builder.RegisterType<WorldRegionService>().As<IWorldRegionService>().InstancePerRequest();
}
Here is the generic ATTEMPT:
// BaseApiController
public abstract class BaseApiController<T> : ApiController where T : IEntityKey
{
protected readonly IService<T> _service;
protected readonly ICultureService _cultureService;
public BaseApiController(IService<T> service, ICultureService cultureService)
{
_service = service;
_cultureService = cultureService;
}
}
// ApiController
public class WorldRegionsController : BaseApiController<WorldRegion>
{
public WorldRegionsController(
IService<WorldRegion> service, ICultureService cultureService)
: base(service, cultureService) {}
}
// Added to Autofac config
builder.RegisterGeneric(typeof(Service<>)).As(typeof(IService<>)).InstancePerRequest();
// Removed
builder.RegisterType<WorldRegionService>().As<IWorldRegionService>().InstancePerRequest();
With this change, I'm getting the exception message noted above (in yellow). I think I'm missing something in the Autofac config, just not sure what. Maybe include/register/add 'WorldRegion' somehow.
How should I register my types ?
Your controller expect a IService<WorldRegion>. Autofac find the following registration for this service :
builder.RegisterGeneric(typeof(Service<>)).As(typeof(IService<>)).InstancePerRequest();
So it tries to create a Service<WorldRegion> which is not possible because Service<T> is an abstract class.
Don't forget that a IWorldRegionService is a IService<WorldRegion> but a IService<WorldRegion> is not a IWorldRegionService.
You don't want to register a generic service but want to register all children as a IService<T>, you can do this by using the RegisterAssemblyTypes method with the AsClosedTypedOf
builder.RegisterAssemblyTypes(this.GetAssemblies())
.AsClosedTypesOf(typeof(IService<>));
Read Autofac documentation - Assembly scanning for more information and how to properly implement the GetAssemblies method in IIS.
At first, I tried removing the abstract from Service<T>, that didn't work, same error. Then I read the Autofac docs and searched around, and I found something similar to Cyril's answer that works:
builder.RegisterAssemblyTypes(typeof(Service<>).Assembly)
.Where(t => t.Name.EndsWith("Service")).AsImplementedInterfaces();
I tried Cyril's implementation of builder.RegisterAssemblyTypes() but this.GetAssemblies() wasn't available. I had to use the full path and this also works:
builder.RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies())
.AsClosedTypesOf(typeof(IService<>));
To note, using either builder.RegisterTypes(), the Service<T> can still be abstract. And just to test, I changed WorldRegionService to abstract, and that DID NOT work.
// THIS DOES NOT WORK..!!!! Must remove 'abstract' from this class.
public abstract class WorldRegionService {}
So both above builder.RegisterTypes() work.
I want to use different interface in different controller.
public interface IMessenger {
Id {get; set;}
void Send();
}
I have two class implement two same interface.
public class SmsSender : IMessenger {
public Id {get; set;}
public void Send() {
//logic here
}
}
public class MailSender : IMessenger {
public Id {get; set;}
public void Send() {
//logic here
}
}
Two Controllers:
public class HomeController : Controller {
private readonly IMessenger _messenger;
public HomeController(IMessenger messenger) {
_messenger = messenger;
}
}
public class Home2Controller : Controller {
private readonly IMessenger _messenger;
public HomeController(IMessenger messenger) {
_messenger = messenger;
}
}
Autofaq setup:
builder.RegisterType<MailSender>().As<IMessenger>().InstancePerLifetimeScope();
builder.RegisterType<SmsSender>().As<IMessenger>().InstancePerLifetimeScope();
How can I get SmsSender in HomeController and MailSender in Home2Controller?
When you register your component you can tell Autofac which dependency to choose using the WithParameter method.
builder.RegisterType<Service>()
.As<IService>()
.WithParameter((pi, c) => pi.ParameterType == typeof(IDependency),
(pi, c) => new XDependency());
In order to avoid the new XDependency code and let Autofac create the dependency, you can resolve a named registration.
builder.RegisterType<XDependency>().Named<IDependency>("X");
builder.RegisterType<YDependency>().Named<IDependency>("Y");
and resolve it using c.ResolveNamed<IDependency>("X")
Another solution would be to let the component choose which dependency it wants using IIndex<TKey, TService>. To use this, you have to register your IDependency as named registrations and inject IIndex<String, IDependency>.
public class Service
{
public Service(IIndex<String, IDependency> dependencies)
{
this._dependency = dependencies["X"];
}
private readonly IDependency _dependency;
}
For more information you can have a look at the FAQ on the autofac documentation : How do I pick a service implementation by context
You cannot. Autofac won't be able to figure out the differences. Better to separate them into two different interfaces, inject the one you need into controllers. From the design point of view the dependency is clearer as well. If there is a need, both interfaces can implement IMessenger.