Injecting eventloggers with different configurations in ASP.NET app - c#

In a ASP.NET Core5 app:
I'd like to inject a logger like this:
public class X {
X( ILogger<X> loggerX )...
}
public class Y {
X( ILogger<Y> loggerY )...
}
When I inject these loggers I'd like both loggers to log to the Windows Eventlog, but I'd like loggerX to set the Source field to "X" and loggerY to set the Source field to "Y".
This way, I'd be able to differentiate easily between logs generated by either class. (The classes in my case would typically be controllers).
My question is:
How do I configure the loggers in the DI container to have this behaviour? (customized logger configuration based on the type of logger extracted).
And also, is there a way to configure the DI Container so that the above scenario would create loggerX to write to a new Log called "X" and logger "Y" to a log called "Y" ? (So, instead of setting the Source, write to different logs).....
Finally, and I realize that this might be asking for a lot, but I may as well ask and learn... Is there a way to achieve the above through "mostly" configuration(appsettings.json) instead of writing loads of code? I don't mind writing the code, but I am trying to make as much as possible configurable where I can and where it makes sense.

I know 2 way for this issue:
First way :
1.Regsiter services in Startup class
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IMessageService, EmailService>();
services.AddTransient<IMessageService, SmsService>();
}
2.Resolve Services
public MessagesController(IEnumerable<IMessageService> messageServices)
{
_messageServices = messageServices;
}
var emailService = _messageServices.OfType<EmailService>().First();
var messageServices = serviceProvider.GetServices<IMessageService>();
Second way
1.Regsiter services in Startup class
services.AddTransient<EmailService>();
services.AddTransient<SmsService>();
services.AddTransient<Func<MessageServiceType, IMessageService>>(serviceProvider => key =>
{
switch (key)
{
case MessageServiceType.EmailService:
return serviceProvider.GetRequiredService<EmailService>();
case MessageServiceType.SmsService:
return serviceProvider.GetRequiredService<SmsService>();
default:
throw new NotImplementedException($"Service of type {key} is not implemented.");
}
});
2.Resolve services
private readonly Func<MessageServiceType, IMessageService> _messageServiceResolver;
public MessagesController(Func<MessageServiceType, IMessageService> messageServiceResolver)
{
_messageServiceResolver = messageServiceResolver;
}
public IActionResult Index()
{
var emailService = _messageServiceResolver(MessageServiceType.EmailService);
return View();
}

Related

NET Core dependency injection - resolve service or configuration based on dependent class

Assuming this use case:
You've got two classes X and Y that depends on a configuration of type Config
public class X
{
public X(IOptions<Config> config)
{
}
}
public class Y
{
public Y(IOptions<Config> config)
{
}
}
Now, you want to create each an instance of X and Y, but with different configurations. What would be the right way to register this?
From everything I read, the only way to solve this would be by adding some sort of "naming" for the different configuration instances and resolve them via a custom resolver:
public delegate Config ServiceResolver(string key);
services.AddTransient<ServiceResolver>(serviceProvider => key =>
{
switch (key)
{
case "A":
return ... (whatever to get the first config instance);
case "B":
return ... (whatever to get the second config instance);
default:
throw new KeyNotFoundException();
}
});
However, this means that the implementation of each X and Y must know about details about how to get the configurations:
They must know the correct name (A or B) and
they must know the ConfigResolver type, which is only an implementation detail/helper class for the sake of dependency injection.
This problem hits even harder if you need to go through several stages of dependencies, like
Config (A) Config (B)
| |
v v
Service Service
| |
v v
X Y
My feeling is, there should be a better way to solve this.
Like some form of receipent dependent service factory:
Host.CreateDefaultBuilder(args).ConfigureServices((context, services) => {
services.Configure<Config>(context.Configuration.GetSection("ConfigA")).For<X>();
services.Configure<Config>(context.Configuration.GetSection("ConfigB")).For<Y>();
});
and maybe
Host.CreateDefaultBuilder(args).ConfigureServices((context, services) => {
services.AddTransient<Service>((services, receiverType) => {
if(receiverType == typeof(X)) {
... resolve a service instance;
}
else {
... resolve some other service instance;
}
});
});
So, is there just some feature I missed until now? Is my understanding of the situation totaly misguided? Or is this really a feature that should be, but has not been added until now?
EDIT:
To make my point clearer: Just assume that X and Y are classes of a third-party library. Their constructors signature cannot be changed by you, as you don't have access to the source code.
So, how would you set this up in a way that you can get each an instance of X with ConfigA and an instance of Y with ConfigB?
Another EDIT 2023-01-02:
Happy new year everyone :)
Seems I have to describe a bit better what's my problem. This is not constrained to IOptions/configurations, but more a general question about where to decide about which service to inject and how it is configured.
Assume I have two a congress location with 2 stages. I call them "bigStage" and "smallStage", but in the end they've got the same implementation. I also got two speakers invited, called "loadSpeaker" and "quietSpeaker", but at this moment in time I don't know which one will speak on which of the two stages.
So I decide I've got this setup:
class Stage {
public Stage(string name, ISpeaker speaker) {
...
}
}
class Speaker: ISpeaker {
public Speaker(string name) {
...
}
}
Now, at the latest time possible, I want to compose my final setup so that I've got 2 Stages (called bigStage and smallStage) and their assigned Speakers (loudSpeaker on bigStage and quietSpeaker on smallStage). This composition/assignment should completely happen in my composition root, so that no code changes have to happen in the rest of my code. How can I do that?
I suggest to use a factory for your Service:
class X {
private readonly Service _service;
public X(ServiceFactory serviceFactory) {
_service = serviceFactory.Create<X>();
}
}
class Service {
private readonly Config _config;
public Service(Config config) { _config = config; }
}
class ServiceFactory {
private readonly IConfiguration _configuration;
/* other Service dependencies would also be injected here */
public ServiceFactory(IConfiguration configuration, /* Service dependencies */) {
_configuration = configuration;
...
}
public Service Create<T>() {
return Create(typeof(T));
}
public Service Create(Type type) {
var configName = switch typeof(T) {
X => "ConfigX",
Y => "ConfigY",
default => throw new Exception()
};
var config = _configuration.GetSection(configName).Get<Config>();
return new Service(config, /* other dependencies */);
}
}
The switch statement can be replaced with a Dictionary<Type, string> or Dictionary<string, string> if you would want to export this dictionary to IConfiguration.
Getting the Config can be also cached for performance (don't forget the thread safety)
So the "trick" to all of this is... you have to piggy back onto ~something to make a decision on which one IMySomething . when you register multiple IMySomething(s).
The factory above where you switch/case on the object.TYPE....is one way.
But it is "fragile", IMHO. Or at the very last, violates the Open/Closed principle of SOLID, as you have to keep editing the code to add a new case-statement.
So I also think you want a Factory.......BUT I do not like "hard coding" the values of the switch/case statements.
So if you follow my IShipper example:
Using a Strategy and Factory Pattern with Dependency Injection
I think you want to create a
IShipperFactory
and inject the IEnumerable of "IShipper"(s).
..
Then you will use your IShipperFactory... when registering your things that need an IShipper.
This does cause a small "ripple" because you need access to the IShipperFactory....to do (later) IoC registrations.
But it would be "clean" and have good separations of concerns.
Let me pseudo code it.
public interface IShipper (from other article)
3 concretes (Usps, FedEx, Ups)
public interface IShipperFactory()
public IShipper GetAnIShipper(string key)
..
public class ShipperFactoryConcrete
(from other article, inject multiple IShippers here)
public IShipper GetAnIShipper(string key)
// look through the injected IShippers to find a match, or else throw exception.
.....
public interface IOrderProcessor
..
public class WestCoastOrderProcessor : IOrderProcessor
/* c-stor */
public WestCoastOrderProcessor(IShipper aSingleShipper)
public class EastCoastOrderProcessor : IOrderProcessor
/* c-stor */
public WestCoastOrderProcessor(IShipper aSingleShipper)
........
Ok, so we decide at compile-time, we want to define the "best" IShipper for the EastCoastOrderProcessor and WestCoastOrderProcessor. (making up some kind of example here)
So need need to IoC register.
from the other article:
cont.RegisterType<IShipper, FedExShipper>(FedExShipper.FriendlyName);
cont.RegisterType<IShipper, UspsShipper>(UspsShipper.FriendlyName);
cont.RegisterType<IShipper, UpsShipper>(UpsShipper.FriendlyName);
now it gets a little "off beaten path".
See:
https://stackoverflow.com/a/53885374/214977
and
// so this is a cart-horse situation, where we need something from the IoC container.... to complete the IoC registrations.
IShipperFactory sf = services.GetRequiredService<IShipperFactory>(); // see https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-7.0#resolve-a-service-at-app-start-up
.. and now we IoC register...but we specify specific values for the constructor. please see the SOF (214977), for syntax-sugar hints. the below is definately pseduo code.....
_serviceCollection.AddSingleton<IOrderProcesor>(x =>
ActivatorUtilities.CreateInstance<EastCoastOrderProcessor>(x, sf.GetAnIShipper(FedExShipper.ShipperName));
);
_serviceCollection.AddSingleton<IOrderProcesor>(x =>
ActivatorUtilities.CreateInstance<WestCoastOrderProcessor>(x, sf.GetAnIShipper(UspsShipper.ShipperName));
);
APPEND:ONE:
Another "trick" .. if you have a code base that you cannot change is.
The "proxy design pattern":
The Proxy design pattern provides a surrogate or placeholder for
another object to control access to it.
https://www.dofactory.com/net/proxy-design-pattern
public EastCoastOrderProcessorProxy
private readonly ThirdPartyOrderProcessor innerThirdPartyOrderProcessor;
public EastCoastOrderProcessor(ThirdPartyOrderProcessor innerThirdPartyOrderProcessor)
{
this.innerThirdPartyOrderProcessor = innerThirdPartyOrderProcessor;
}
..
public WestCoastOrderProcessorProxy
private readonly ThirdPartyOrderProcessor innerThirdPartyOrderProcessor;
public EastCoastOrderProcessor(ThirdPartyOrderProcessor innerThirdPartyOrderProcessor)
{
this.innerThirdPartyOrderProcessor = innerThirdPartyOrderProcessor;
}
So while you cannot change the ThirdPartyOrderProcessor, you can write 1:N wrapper-proxies around it.
The simplest solution I can think of, without using named options inside of your service classes, is moving the selection of the configuration object from the class constructor to the composition root of the application.
This way, your service class simply receives a configuration object as a constructor parameter and it is not aware of the underlying configuration infrastructure.
The composition root, which is in charge of composing the objects which make your application, do know about the configuration infrastructure and picks the right configuration object for your services.
In order to implement this pattern, you need to define an option class as the first step. This option class is needed in order to leverage the options pattern support offered by ASP.NET core. You will only use this class at the composition root level.
public sealed class LayoutOptions
{
public const string Layout = "Layout";
public const string Basic = "Basic";
public const string Complex = "Complex";
public string Name { get; set; } = default!;
public string Color { get; set; } = default!;
public int NumberOfColumns { get; set; }
}
Then you need to define a class which represents the configuration object for your services. This is basically a strongly typed configuration object used to configure your services. This object is built strating from the options class, notice that you don't need to make it identical to the options class itself.
public sealed class LayoutConfiguration
{
public string Name { get; }
public string Color { get; }
public LayoutConfiguration(string name, string color)
{
Name = name;
Color = color;
}
}
Now you need to define your service classes. These types are configured by using the LayoutConfiguration configuration class. Each service class will be properly configured by the composition root of the application, by using the proper named options.
public interface ILayoutService
{
string GetLayoutDescription();
}
public sealed class BasicLayoutService : ILayoutService
{
private readonly LayoutConfiguration _config;
public BasicLayoutService(LayoutConfiguration config)
{
_config = config ?? throw new ArgumentNullException(nameof(config));
}
public string GetLayoutDescription() =>
$"Basic layout description. Name: '{_config.Name}' Color: '{_config.Color}'";
}
public sealed class ComplexLayoutService : ILayoutService
{
private readonly LayoutConfiguration _config;
public ComplexLayoutService(LayoutConfiguration config)
{
_config = config ?? throw new ArgumentNullException(nameof(config));
}
public string GetLayoutDescription() =>
$"Complex layout description. Name: '{_config.Name}' Color: '{_config.Color}'";
}
You can also defined a couple of controllers, that you can use to test this implementation and be user that your services are wired-up correctly by the composition root of the application:
[ApiController]
[Route("[controller]")]
public sealed class BasicLayoutController : ControllerBase
{
private readonly BasicLayoutService _basicLayoutService;
public BasicLayoutController(BasicLayoutService basicLayoutService)
{
_basicLayoutService = basicLayoutService ?? throw new ArgumentNullException(nameof(basicLayoutService));
}
[HttpGet("description")]
public string GetDescription() => _basicLayoutService.GetLayoutDescription();
}
[ApiController]
[Route("[controller]")]
public sealed class ComplexLayoutController : ControllerBase
{
private readonly ComplexLayoutService _complexLayoutService;
public ComplexLayoutController(ComplexLayoutService complexLayoutService)
{
_complexLayoutService = complexLayoutService ?? throw new ArgumentNullException(nameof(complexLayoutService));
}
[HttpGet("description")]
public string GetDescription() => _complexLayoutService.GetLayoutDescription();
}
This is the most important part. Put this registration code inside the Program.cs class (which is the composition root for an ASP.NET core 6 application):
// Configure named options
builder.Services.Configure<LayoutOptions>(
LayoutOptions.Basic,
builder.Configuration.GetSection($"{LayoutOptions.Layout}:{LayoutOptions.Basic}")
);
builder.Services.Configure<LayoutOptions>(
LayoutOptions.Complex,
builder.Configuration.GetSection($"{LayoutOptions.Layout}:{LayoutOptions.Complex}")
);
// Register the BasicLayoutService by picking the right configuration
builder
.Services
.AddScoped(serviceProvider =>
{
// Get named options
var layoutOptions = serviceProvider.GetRequiredService<IOptionsSnapshot<LayoutOptions>>();
var basicLayoutOptions = layoutOptions.Get(LayoutOptions.Basic);
// Create strongly typed configuration object from named options
var configuration = new LayoutConfiguration(
basicLayoutOptions.Name,
basicLayoutOptions.Color);
// Creates new instance of BasicLayoutService using the service provider and the configuration object
return ActivatorUtilities.CreateInstance<BasicLayoutService>(
serviceProvider,
configuration);
});
// Register the ComplexLayoutService by picking the right configuration
builder
.Services
.AddScoped(serviceProvider =>
{
// Get named options
var layoutOptions = serviceProvider.GetRequiredService<IOptionsSnapshot<LayoutOptions>>();
var complexLayoutOptions = layoutOptions.Get(LayoutOptions.Complex);
// Create strongly typed configuration object from named options
var configuration = new LayoutConfiguration(
complexLayoutOptions.Name,
complexLayoutOptions.Color);
// Creates new instance of ComplexLayoutService using the service provider and the configuration object
return ActivatorUtilities.CreateInstance<ComplexLayoutService>(
serviceProvider,
configuration);
});
You can now test this implementation. As an example, you can set the following configuration in appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Layout": {
"Basic": {
"Name": "Basic Layout",
"Color": "red",
"NumberOfColumns": 2
},
"Complex": {
"Name": "Complex Layout",
"Color": "blue",
"NumberOfColumns": 3
}
}
}
If you run this application and you issue a GET request to /BasicLayout/description, you ge the following response:
Basic layout description. Name: 'Basic Layout' Color: 'red'
If you issue a GET request to /ComplexLayout/description the response you get is:
Complex layout description. Name: 'Complex Layout' Color: 'blue'
A final note on the service lifetime for BasicLayoutService and ComplexLayoutService. In my example I decided to register them as scoped services, because you may want to recompute the configuration object for them (LayoutConfiguration) for each incoming request. This is useful if your configuration may change over time. If this is not the case, you can safely register them as singleton services. That's up to you and depends on your requirements.

unit test for a method that takes an instance through Resolve<T> of Autofac

I'm facing a problem trying to implement a unit test for a method on a service.
The architecture of the project is a little bit cumbersome, to say the less...
The problem is that within the method to test it calls another method to take an instance of another service, here is the little monster:
public void SendOrderEmail(string orderCode)
{
Order order= GetOrderService().SerachByCode(orderCode);
.... Send email with the order ....
}
private IOrderService GetOrderService()
{
return OrderService = AutofacDependencyResolver.Current.ApplicationContainer.Resolve<IOrderService>();
}
Please, don't ask why a service calls another service or why is that service not injected at the constructor, as i said the architecture of this project is weird in some points.
I just need to know what is the way to implement a unit test for a method like that.
Thank you!
I would refactor a little the code, let the class that implement this method have IOrderService injected through the constructor, save the instance and then use it,
this way you can inject your fake IOrderService during the test (or use Automock) :)
If you really really can't change the constructor, you can use a property to set IOrderService
---------------- edit
Since i got some downvote on this answer I've tried to get to understand better what is going on.
I'm not sure about this, but seems like you can't edit this class you wrote about, you just want to test it.
Well if that is the case i think i can still give you some advices.
Advice number one: make a test project, link the class file, make a new file with a class like the following one.
class AutofacDependencyResolver {
public static Current { get; private set; }
public ILifetimeScope ApplicationContainer { get; private set; }
public AutofacDependencyResolver(ILifetimeScope scope) {
Current = this;
ApplicationContainer = scope;
}
}
Since the class you need to test is linked it's gonne to compile it and you just can now achieve what you need.
The other (and i think better) advice is do not test stuff you did not wrote / can't modify. What i'm suggesting is writing an adapter, so a class that use the one you can't modify as a black box.
In this case i think you need to test the email, so just check the email output the address stuff like that and ignore the rest.
the people who wrote those classes should have followed solid principles...
As others have said, and you're probably aware yourself anyway, you really want to refactor classes like this and use constructor injection if at all possible. Service location is generally considered an anti-pattern (https://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/) and it specifically makes unit testing like this harder and less transparent.
However, if you absolutely can't refactor, you can still make methods like this somewhat testable by just providing different registrations for the services you're accessing via service location.
In your case, if you have:
public class EmailSender
{
public void SendOrderEmail(string orderCode)
{
Order order = GetOrderService().SearchByCode(orderCode);
//....Send email with the order ....
}
private IOrderService GetOrderService()
{
return AutofacDependencyResolver.Current.ApplicationContainer.Resolve<IOrderService>();
}
}
...and you're looking to specifically run unit tests over SendOrderEmail to validate the logic surrounding your IOrderService implementation (which could be easily covered by a separate test), the other classes implied there might look like:
public class AutofacDependencyResolver // this is problematic but we can't change it
{
public AutofacDependencyResolver(IContainer applicationContainer)
{
ApplicationContainer = applicationContainer;
}
public IContainer ApplicationContainer { get; }
public static AutofacDependencyResolver Current { get; private set; }
public static void SetContainer(IContainer container)
{
Current = new AutofacDependencyResolver(container);
}
}
public static class ContainerProvider // this sets up production config across your app
{
public static IContainer GetProductionContainer()
{
var builder = new ContainerBuilder();
builder.RegisterType<RealOrderService>()
.As<IOrderService>();
// register all other real dependencies here
return builder.Build();
}
}
With that setup, you only need to provide mocks which are required for the specific method you're testing, assuming you can set your container within AutofacDependencyResolver easily in order to have production and test configuration running in parallel. That might look like the following, using xUnit, Moq and Autofac in a test project:
public class EmailSenderTests
{
private readonly Mock<IOrderService> _orderService;
public EmailSenderTests()
{
// to set up the test fixture we'll create a mock OrderService and store a reference to the mock itself for validation later on
_orderService = new Mock<IOrderService>();
var mockOrder = new Order();
_orderService.Setup(os => os.SearchByCode(It.IsAny<string>()))
.Returns(mockOrder);
}
private IContainer GetTestContainer()
{
// here we're adding just one registration we need, setting the mocked OrderService instance to be used for IOrderService
var builder = new ContainerBuilder();
builder.Register(c => _orderService.Object)
.As<IOrderService>();
return builder.Build();
}
[Fact]
public void SendEmail()
{
AutofacDependencyResolver.SetContainer(GetTestContainer()); // set the test container on the global singleton
var sender = new EmailSender();
sender.SendOrderEmail("abc"); // internally the email sender will retrieve the mock IOrderService via service location
// make any assertions here, e.g.
_orderService.Verify(os=>os.SearchByCode("abc"), Times.Exactly(1));
}
}

Configuration scopes for services with Asp.Net Core DI

Is it possible to set up injection scopes for the default DI in Asp.Net Core? I mean For example:
services.AddSingleton<IUser, UserService>
services.AddSingleton<IUser, UserService>
And for the second configuration somehow specify that it should be injected into only HomeController. Unlike the first one should be injected to all others. Is it possible with default DI?
I answered a similar question here but using scoped instead of singleton:
How to register multiple implementations of the same interface in Asp.Net Core?
My gut feeling is that this might be what you're trying to achieve, or might be a better approach, and you might be mixing up the User with the UserService. When you have multiple implementations of the same interface DI will add these to a collection, so it's then possible to retrieve the version you want from the collection using typeof.
// In Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped(IUserService, UserServiceA);
services.AddScoped(IUserService, UserServiceB);
services.AddScoped(IUserService, UserServiceC);
}
// Any class that uses the service(s)
public class Consumer
{
private readonly IEnumerable<IUserService> _myServices;
public Consumer(IEnumerable<IUserService> myServices)
{
_myServices = myServices;
}
public UserServiceA()
{
var userServiceA = _myServices.FirstOrDefault(t => t.GetType() == typeof(UserServiceA));
userServiceA.DoTheThing();
}
public UserServiceB()
{
var userServiceB = _myServices.FirstOrDefault(t => t.GetType() == typeof(UserServiceB));
userServiceB.DoTheThing();
}
public UseServiceC()
{
var userServiceC = _myServices.FirstOrDefault(t => t.GetType() == typeof(UserServiceC));
userServiceC.DoTheThing();
}
}
Assuming this registration, how should the dependency injection container possibly know which “singleton” (it’s not really a singleton when there are two of them) it should inject into the HomeController, or a different service, when they are all just depend on IUser?
The type the dependency gets registered as, in your case IUser, is the “key” which DI containers use to resolve the dependency. So two services that both depend on IUser will get their dependency resolved in the same way. With a singleton lifetime, this means that both services get the same instance.
Service registrations are also usually replacing. So if you have one registration AddSingleton<X, Y>() and then have another one AddSingleton<X, Z>(), then the latter will replace the former. So all services dependending on X will receive Z.
DI containers, including the default container that ships with ASP.NET Core, do usually support resolving all registrations by depending on IEnumerable<X> instead. But for this example this just means that a services would get both Y and Z.
The closest thing you are looking for are keyed or named dependencies. While these are supported in some DI containers, they are technically not part of dependency injection and as such often deliberately absent from many containers, including the ASP.NET Core one. See this answer for more details on that and for some idea to get around that.
To get back to your use case, you should really think about what you are actually doing there. If you have two “singleton” instances of UserService, you should really think about why that is the case: Why isn’t there just one? And if there is support for multiple, why not register it as transient?
More importantly, what would possibly differ between those two instances? After all, they are both instances of the same implementation, so there isn’t much that they can do differently.
If you can identify that, and also confirm that this is something that actually makes the instances different, then consider splitting this up in the type hierarchy as well. It’s difficult to explain this without having a use case here, but what you should try is to end up with two different interfaces that each do exactly what each dependent service type needs. So HomeController can depend on IUserA, and others can depend on IUserB (please choose better names than this).
I have the similar issue. There is my solution.
On the top level in controller I use custom attribute for the action, where I need specific service implementation (for reports for example):
public class HomeController : ControllerBase
{
private readonly IService _service;
public HomeController(IService service)
{
_service = service;
}
[HttpGet]
[ReportScope]
public IEnumerable<WeatherForecast> Get()
{
_service.DoSomeThing();
}
This attribute is processed by custom middleware:
public class ReportScopeLoggingMiddleware
{
private readonly RequestDelegate _next;
public ReportScopeLoggingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context, ReportScopeContext scopeContext)
{
var controllerActionDescriptor = context
.GetEndpoint()
.Metadata
.GetMetadata<ControllerActionDescriptor>();
bool analytical = controllerActionDescriptor.EndpointMetadata.Any(m => m is ReportScopeAttribute);
if (analytical) scopeContext.SetActive();
await _next(context);
}
}
In this middleware I use ReportScopeContext.
public class ReportScopeContext
{
public bool Active { get; private set; } = false;
public void SetActive()
{
Active = true;
}
}
This ReportScopeContext has scoped lifetime in DI and I use it to select an implementation of IService:
services.AddScoped<ReportScopeContext>();
services.AddTransient<Service2>();
services.AddTransient<Service1>();
services.AddTransient<IService>(sp =>
sp.GetRequiredService<ReportScopeContext>().Active
? sp.GetRequiredService<Service1>()
: sp.GetRequiredService<Service2>());

Can not resolve a generic service using manual interception with ASP.NET Core 2 and Castle.Core

I know there are a lot of question similar to this one but actually none of them solved my issue.
I created a new Asp.Net Core 2 application.
Now I am trying to use an intercepter for a specific service to fetch some data into this service(I am using Castle.Core nuget package).
I have a generic IConfigurationInterceptor<> and a real implementation ConfigurationInterceptor<>
Here is the interface:
public interface IConfigurationInterceptor<T> : IInterceptor where T : class { }
public class ConfigurationInterceptor<T> : IConfigurationInterceptor<T> where T : class
{
public ConfigurationInterceptor(ConfigurationInfo<T> configurationInfo,
some other services)
{
_configurationInfo = configurationInfo;
//.....
}
public void Intercept(IInvocation invocation)
{
invocation.ReturnValue = somefunc(someconfig, invocation.Arguments);
}
}
Then I have an extension method like below:
public static void AddSingletonConfiguration<TInterface, TImplementation>(
this IServiceCollection services, string featureName)
where TImplementation : class, TInterface where TInterface : class
{
var info = new ConfigurationInfo<TImplementation>(featureName, typeof(TInterface));
var generator = new ProxyGenerator();
services.AddSingleton(x =>
{
var ic = x.GetService<Func<ConfigurationInfo<TImplementation>,
IConfigurationInterceptor<TImplementation>>>();
var icTemp = ic.Invoke(info);
return (TInterface) generator.CreateInterfaceProxyWithoutTarget(
info.ServiceType, icTemp);
});
}
But when I get to this line of code:
var ic = x.GetService<Func<ConfigurationInfo<TImplementation>,
IConfigurationInterceptor<TImplementation>>>();
it returns me a null value for ic:
ConfigurationInfo class is just a simple class I create for storing some extra data.
public sealed class ConfigurationInfo<TImpl>
{
public Type ServiceType { get; }
public string FeatureName { get; }
public ConfigurationInfo(string featureName, Type serviceType)
{
FeatureName = featureName;
ServiceType = serviceType;
}
public override string ToString()
=> $"{FeatureName} ({ServiceType} -> {typeof(TImpl)})";
}
In my ConfigureServices I have these both lines:
services.AddSingleton(typeof(IConfigurationInterceptor<>),
typeof(ConfigurationInterceptor<>));
services.AddSingletonConfiguration<IStaticDataConfiguration, StaticDataConfiguration>(
"SomeFeatureKey");
I am not sure why ic variable is null because previously another project was using Autofac and was working perfectly but in the startup you would find something like this:
builder.RegisterGeneric(typeof(ConfigurationInterceptor<>))
.As(typeof(IConfigurationInterceptor<>)).SingleInstance();
builder.RegisterConfiguration<IStaticDataConfiguration, StaticDataConfiguration>(
"SomeFeatureKey");
and the extension method was like this one:
public static void RegisterConfiguration<TInterface, TImplementation>(
this ContainerBuilder builder, string featureName)
where TImplementation : class, TInterface
{
var info = new ConfigurationInfo<TImplementation>(featureName, typeof(TInterface));
var generator = new ProxyGenerator();
builder
.Register(c =>
{
var ic = c.Resolve<Func<ConfigurationInfo<TImplementation>,
IConfigurationInterceptor<TImplementation>>>()(info);
return generator.CreateInterfaceProxyWithoutTarget(info.ServiceType, ic);
})
.As<TInterface>()
.SingleInstance();
}
Any help would be appreaciated.
EDIT 1:
Now I changed from method GetService<> to method GetRequiredService<> and throws an exception like below:
No service for type 'System.Func'2[StaticDataProvider.DomainModel.ConfigurationInfo'1[StaticDataProvider.Services.StaticDataConfiguration],StaticDataProvider.Services.Interfaces.IConfigurationInterceptor'1[StaticDataProvider.Services.StaticDataConfiguration]]' has been registered.
EDIT 2:
To wrap it up here is the issue: In my current project in Asp.Net core I can not get a Func<X, B> while in the Asp.Net MVC 5 project(It is a whole different project) I can get a Func<X, B> using Autofac. I think this has to do with parametrized instantiation feature in Autofac provided by default: here
Now, I dont know if in Asp.Net Core default DI container has something like this 'parametrized instantiation' feature where it allows me resolving Func<X, B> instead of B.
I'm guessing the root of the problem is in the fairly complex manual wiring up of the interceptors.
If you're using interceptors with Autofac, it'd be better to use the Autofac.Extras.DynamicProxy2 package and wire up interceptors using the built-in Autofac functionality instead of trying to chain a bunch of resolutions together with functions and parameters. I see a lot of little gotchas in here like how you're setting up a singleton interface proxy without a target but I'm not entirely clear how the target gets added post-facto. There's a lot of complexity you can avoid by using the tools provided.
That said, I'm also looking at the exception message. Without a stack trace I can't 100% guarantee it, but a search on the Autofac source indicates that's not a message that came from Autofac - it's likely, then, a message from the default Microsoft.Extensions.DependencyInjection container. That indicates you may not actually have everything wired up the way you think you do.
I'd back up a bit and just get simple things working and ensure they're coming from Autofac. If you decide you don't want Autofac in play, make sure you've removed it entirely from the equation. Basically, just make sure it's clean and working in the general sense.
After that, add things back slowly, one at a time. I might recommend putting a reproduction in a unit test where you use these registration mechanisms and get things working without the complexity of the entire app weighing down. Unwind it from there. If it's too complex to unit test... maybe that's an indicator you should simplify it and refactor. Make it testable.
I'll leave my previous answer for posterity, but... The default Microsoft IoC provider is very simple and does not support all the features of Autofac. You won't get parameterized resolution or auto-generated factories from it.
Here is what I had to do:
Modified ConfigureService method like below:
public void ConfigureServices(IServiceCollection services)
{
IConfigurationInterceptor<T> GetConfigurationInterceptor<T>(ConfigurationInfo<T> info) where T : class
{
return new ConfigurationInterceptor<T>(info, services.GetService<IConfigurationProvider>(), Configuration);
}
services.AddSingletonConfiguration<IStaticDataConfiguration, StaticDataConfiguration>("someFeatureKey", GetConfigurationInterceptor);
}
Then modified extension methods like below:
public static void AddSingletonConfiguration<TInterface, TImplementation>(this IServiceCollection services,
string featureName, Func<ConfigurationInfo<TImplementation>, IConfigurationInterceptor<TImplementation>> ic) where TImplementation : class, TInterface where TInterface : class
{
var info = new ConfigurationInfo<TImplementation>(featureName, typeof(TInterface));
var generator = new ProxyGenerator();
services.AddSingleton(x =>
{
var icTemp = ic.Invoke(info);
return (TInterface) generator.CreateInterfaceProxyWithoutTarget(info.ServiceType, icTemp);
});
}
public static TInterface GetService<TInterface>(this IServiceCollection services) where TInterface : class
{
var serviceProvider = services.BuildServiceProvider();
return serviceProvider.GetRequiredService<TInterface>();
}
Now its working fine but the idea is that I had to create Func<X, B> myself and pass as a parameter to extension method.

Injecting dependencies into AuthorizeAttribute in a class library

I've run into an interesting design issue with a class library I am writing. I have a custom implementation of the AuthorizeAttribute that I want clients to be able to use like this:
[Protected("permission_name")]
In the above code, PermissionAttribute inherits from AuthorizeAttribute and uses a local default (DefaultContext created using HttpContext).
Behind the scenes, the attribute uses a SecurityService to check users, roles and permissions against (the SecurityService itself uses a client-provided persistence service that they can wire up in the composition root of their app).
So my attributes need a reference to the SecurityService to function. Since Attribute constructors can only have compile-time constants, I cannot use constructor injection.
I don't want to force my clients to use a DI framework - they should be able to discover and wire up the necessary dependencies in their composition root without using an IoC library, if they so choose.
Here are my options:
Have the library use a singleton SecurityService.
Use property injection, which would work but
it would make the dependency seem optional, which it is not and
I don't know where I can do property injection in an MVC app on an authorize attribute.
A possible solution to 2. above is to do set an instance of SecurityService as a static property on the attribute at application startup and use a guard clause to prevent it from being set more than once, like this:
class ProtectedAttribute : ...
{
private static ISecurityService _SecurityService ;
public static ISecurityService SecurityService
{
get
{
return _SecurityService ;
}
set
{
if (_SecurityService != null)
throw new InvalidOperationException("You can only set the SecurityService once per lifetime of this app.") ;
_SecurityService = value ;
}
}
}
The SecurityService could be an abstract service facade so that it can be extended/replaced by a different implementation.
Is there a better way to solve this problem?
UPDATE: Adding some code to show how I am going to do it:
Add a public property on the attribute that returns the permission name:
public class ProtectedAttribute : ...
{
private string _Permission ;
public string Permission { get { return _Permission ; } /*...*/ }
public ProtectedAttribute(string permission) { /*...*/ }
}
Setup an authorization filter and configure dependency via Ninject (if using Ninject):
using Ninject.Web.Mvc.FilterBindingSyntax;
public class MyModule : Ninject.Modules.NinjectModule
{
public override void Load()
{
// mySecurityService instance below can have a singleton lifetime - perfect!
this.BindFilter<MyAuthorizationFilter>(FilterScope.Action, 0)
.WhenActionMethodHas<ProtectedAttribute>()
.WithConstructorArgument("securityService", mySecurityService)
.WithConstructorArgumentFromActionAttribute<ProtectedAttribute>("permission", p => p.PermissionName) ;
}
}
Ohhh it's...beautiful sniffle
With ASP.NET MVC 3 you could use constructor injection with action filters thanks to the new IFilterProvider. This way you no longer need to decorate your controller actions with action filters. You could apply them thanks to this interface and using a marker attribute.
And if you don't wanna bother implementing it manually you could always use an existing DI framework such as Ninject which provides a fluent way to define action filter dependencies.
My applications inherit from a base Application class that exposes the IOC container.
public interface IInjectableApplication
{
IUnityContainer Container { get; }
}
Then I have a base attribute class, which is aware of this
public abstract IocAwareActionFilterAttribute : ActionFilterAttribute{
protected T ResolveItem<T>(ResultExecutedContext context)
{
var app = context.HttpContext.ApplicationInstance as IInjectableApplication;
if (app == null) { throw new NullReferenceException("Application is not IInjectable."); }
T c = (T)app.Container.Resolve(typeof(T));
if (c == null) { throw new NullReferenceException(string.Format("Could not find injected {0}.", typeof(T).FullName)); }
return c;
}
}
While this is not true Injection, since Attributes aren't constructed 'normally', this provides a similar behavior. No reason it should not be adaptable to other IOCs

Categories

Resources