Inject one service that uses methods from multiple interfaces - c#

I am trying to inject an instance of a service into my NameController. The service uses methods from multiple service classes, so I'm doing this with multiple interface inheritance.
With the code I provided, using _oneThreeService I am actually able to access all the methods that OneService.cs and TwoService.cs contain. However, when I run the application, I get an error that states: InvalidOperationException: Unable to resolve service for type 'ServiceClassLibrary.IOneThreeService' while attempting to activate 'Web.Controllers.NameController'.
In the ConfigureServices method, I have tried adding this line services.AddScoped<IOneThreeService, OneService, ThreeService>(); but that can't be done.
What I want is to be able to use methods from those two service classes by injecting only one service into the controller.
Startup.cs > ConfigureServices method:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IOneService, OneService>();
services.AddScoped<ITwoService, TwoService>();
services.AddScoped<IThreeService, ThreeService>();
services.AddControllersWithViews();
}
IOneThreeService.cs:
public interface IOneThreeService : IOneService, IThreeService
{
}
NameController.cs:
public class NameController : Controller
{
private readonly IOneThreeService _oneThreeService;
public NameController(IOneThreeService oneThreeService)
{
_oneThreeService = oneThreeService;
}
[HttpGet]
public IActionResult Index()
{
_oneThreeService.MethodFromOneService();
_oneThreeService.MethodFromTwoService();
return View();
}
}

It feels like you expect container to implement IOneThreeService for you based on fact the interface has no new methods and both base interfaces are registered in container - this functionality does not exist in any DI container I know, you need to register and implement such interface on your own.
Interfaces need implementation, just defining interface is not enough for compiler to find how it is implemented.
New interface also must be registered - there is no magic to find implementation of such interface.
Options:
write class that implements IOneThreeService completely and register it.
write class that takes implementations of IOneService and IThreeService, then implements IOneThreeService by forwarding calls to the corresponding service and register it (if constructor just takes interface the DI container will fill them baed on correspondingly registered base interfaces).
instead of creating the new interface you can just implement both interfaces on the same class and register same instance for both interfaces

Related

How to avoid Registering a service in startup.cs

In asp.net core we have to register service in order to use it but when services increases what we have to do?
just like the code below i just have ShopService what if i have more then 50 services.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddDbContext<SGAContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("SGAContext")));
services.AddScoped<DbContext, SGAContext>();
services.AddScoped<IShopService, ShopService>();
}
What is the best way to minimize this situation.
You can add your services using certain criteria and reflection, like below:
public static IServiceCollection AddScopedImplementations<T>(this IServiceCollection services)
{
var types = typeof(T).Assembly.GetTypes().Where(_ => !_.IsAbstract && _.IsClass && typeof(T).IsAssignableFrom(_));
foreach (var t in types)
services.AddScoped(t);
return services;
}
The idea of AddScopedImplementations method is to add all types that are implementing certain interface
I'm scanning the same assembly that has interface implemented within.
Then to get all types that implement interface Startup.ConfigureServices can be modified like
services.AddScopedImplementations<IConverterToDb>();
services.AddScopedImplementations<IMapperLink>();
It scans assembly that has IConverterToDb interface and registers all IConverterToDb implementations that are not abstract
You can use the same approach with interface-type registrations, like empty IScopedServiceToBeAdded or mark your service types with attribute to implement automatic services.AddScoped<IShopService, ShopService>() registrations
public interface IShopService : IAutoRegistration
{}
public interface ShopService : IShopService
{}
then you can scan for all interfaces derived from IAutoRegistration (like IShopService and use this list to find types that implement derived interfaces ShopService
then you can put found interface - class pairs to the services.AddScoped(tinterface, tclass)

dependency injection vs constructor overload

I am working on a .net core project where each controller has their own service as DI. All those services share some common stuff so I abstract a BaseService class:
public class BaseService
{
public BaseService(IHttpContextService srv)
{
...
}
}
This BaseService, or in another word, all the services need another service for the HTTP context.
Now I need to inherit from this BaseService if I need a new service for a new controller:
public class MyFirstService : BaseService
{
public MyFirstService(IHttpContextService srvHttp, AdditionalService srvAdditional) {}
}
In its constructor, I will need to specify the IHttpContextService again, and a few additional services this MyFirstService needs.
But this seems wrong because the child is a descendant and therefore should have the IHttpContextService naturally. Not to mention the child doesn't have a constructor with the same parameters as the ancestor which gives a compile error.
Is there a way to DI objects outside of the constructor? Or just get rid of the DI from .net core? Or I should change my design pattern?

Can't inject a delegate using ASP.NET Core DI

Say I've a MVC Core Controller like this:
public class SomeController
{
public SomeController(IConfiguration appConfig, Func<string> someDelegate)
{
}
}
Also, I'm using AutoFac to resolve injections. Object injections are working flawlessly while adding a delegate injection produces an ASP.NET Core exception which tells that Func<string> can't be injected because there's no component to inject with such type.
When I try to manually resolve SomeController using AutoFac I get the desired behavior.
Is there any way to support this scenario without using AutoFac to resolve controllers?
Controllers are not resolved via DI by default, they are constructed in the DefaultControllerFactory or so.
Update
Microsoft.Extensions.DependencyInjection doesn't support named components, discovery, auto registrations, decorators etc.
It's meant to be simple out of the box IoC and provide the base for DI for basic applications and offer easy way for 3rd party IoC containers (with advanced features such as auto discovery, decorators etc.) to be integrated (basically all they need is process the information in IServiceCollection and return their own implementation of IServiceProvider from Configure method).
Tag helpers, controllers and view components are different in this aspect as they have their own activators (the default one use activation utilities, which at some point further down the pipeline use the service provider). For that reasons AddControllersAsServices exists, because it replaces DefaultControllerActivator (which uses ActivationUtilities, see DefaultControllerActivator.cs) with ServiceBasedActivator (which uses IServiceProvider, see ServiceBasedControllerActivator).
Also see this related answer for details on how to resolve controllers, tag helpers and view components via DI.
var builder = services
.AddMvc()
.AddControllersAsServices() // this one for your case
.AddViewComponentsAsServices()
.AddTagHelpersAsServices();
I was just run into this issue myself so I thought I would share for future reference as I had one case where I wanted to resolve a delegate but including an additional library seemed like overkill.
Given the following defintions:
public interface ISomething { /*...*/ };
public interface ISomeService { /*...*/ }
public class SomeService : ISomeService { /*...*/ }
public class Something
{
public Something(ISomeService service, string key) { /*...*/ }
}
// I prefer using a delegate for readability but you
// don't have to use one
public delegate ISomething CreateSomething(string key);
The delegate can be registered like this:
var builder = services
.AddSingleton<ISomeService, SomeService>()
.AddTrasient<CreateSomething>(provider => key => new Something(provider.GetRequiredService<ISomeService>(), key));

Register both generic and specific implementation of an interface with ASP.NET Core DI

I've read the Armen Shimoon's article ASP.NET Core: Factory Pattern Dependency Injection and I've decided to solve my ASP.NET Core DI problem using the technique suggested by him.
I’ve got a generic interface:
public interface IItemRepository<out T> where T : BaseItem
and its generic implementation:
public class ItemRepository<T> : IItemRepository<T> where T : BaseItem
I register it with:
services.AddSingleton(typeof(IItemRepository<>), typeof(ItemRepository<>));
But for Currency I’ve got a specific implementation:
public class CurrencyRepository : ItemRepository<Currency>
(Curency is of the BaseItem type.) What I want is to register
CurrencyRepository
for
IItemRepository<Currency>
and
ItemRepository<T>
for all other items that implement BaseItem. I created a factory class to accomplish this:
public class ItemRepositoryFactory : IServiceFactory> where T : BaseItem
{
private readonly ApplicationDbContext _context;
public ItemRepositoryFactory(ApplicationDbContext context)
{
_context = context;
}
public ItemRepository Build()
{
if (typeof(T) == typeof(Currency))
return new CurrencyRepository(_context) as ItemRepository;
return new ItemRepository(_context);
}
}
but I don’t know how to register it with IServiceCollection. Or maybe I’m not on the right way at all?
You can't register it explicitly. ASP.NET Core DI is meant to be simple and offer out of box DI/IoC experience and easy for other DI/IoC containers to plugin.
So the built-in IoC doesn't offer Auto-Registrations, Assembly scanning, Decorators or registration of all interfaces/sub types of the class.
For concrete implementations, there is a "workaround"
services.AddScoped<IMyService,MyService>();
services.AddScoped<MyService>(provider => provider.GetService<IMyService>());
Then both MyService and IMyService inside a constructor would receive the same instance of the class.
But this doesn't work with open generics and is a manuell process. For automatic discovery or registration you need a 3rd party IoC container, such as Autofac, StructureMap etc.
But in your concrete sample it should be enough to register IItemRepository<Currency> inside your ConfigureServices method
services.AddScoped<IItemRepository<Currency>,CurrencyRepository>();
But actually the open generic should cover this case already, if you inject IItemRepository<Currency> into your services.

Resolve different decorated instances depending on consumer with structuremap

The application is an ASP.NET MVC webapp built up by repositories fronted by a concrete service layer for backend. I use structure map 3 as IoC to inject the repositories for each concrete service. For logging/caching etc. I use decorated repositories which also is setup with structure map.
The application has a public and non-public part. The non-public part is where some super users log in and create and update content. The public part consists of http handlers and is exposed on the web and handles 99.99% of all requests to the application.
I would like to configure structure map to use cache decorated repositories when instances are resolved in the http handlers but not in the rest of the application. I would also like to inject a different logger to the service when resolved in http handlers.
Is this possible to get different setups of the same interface implementation depending on the consumer?
public interface IEntityRepository<IEntity>
{
}
public class ContentService : IEntityService
{
public ContentService(IEntityRepository<Content> repoistory, ILogger logger)
{
}
}
NOTE that this solution doesn't provide the feature you are looking for - the delegate that is passed into the DecorateAllWith is only called once for each type that is resolved.
The DecorateAllWith method has an overload that can be used to analyse the type being created and filter accordingly
[Fact]
public void DecorateAllWith_Filtered_IsNotReturned()
{
var container = new StructureMap.Container(registry =>
{
registry.Scan(x =>
{
x.TheCallingAssembly();
x.ConnectImplementationsToTypesClosing(typeof(IEntityRepository<>));
});
registry.For(typeof(IEntityRepository<>))
.DecorateAllWith(typeof(CachingDecorator<>), instance => false);
});
var result = container.GetInstance<IEntityRepository<Entity1>>();
Assert.IsNotType<CachingDecorator<Entity1>>(result);
}

Categories