I have many AOP libraries that use Castle DynamicProxy with Autofac DI container for logging, auditing, transaction control, etc.
I wonder if there is a way to declare interceptors using the default .NET Core DI container. It will be good to have this flexibility since many .NET Core projects don't use Autofac.
Yes, you can use DynamicProxy using Core DI. I've written up a blog post explaining it at http://codethug.com/2021/03/17/Caching-with-Attributes-in-DotNet-Core5/, but here is the code for it:
Create an attribute
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class CacheAttribute : Attribute
{
public int Seconds { get; set; } = 30;
}
Create an interceptor (requires Castle.Core nuget package)
public class CacheInterceptor : IInterceptor
{
private IMemoryCache _memoryCache;
public CacheInterceptor(IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
}
// Create a cache key using the name of the method and the values
// of its arguments so that if the same method is called with the
// same arguments in the future, we can find out if the results
// are cached or not
private static string GenerateCacheKey(string name,
object[] arguments)
{
if (arguments == null || arguments.Length == 0)
return name;
return name + "--" +
string.Join("--", arguments.Select(a =>
a == null ? "**NULL**" : a.ToString()).ToArray());
}
public void Intercept(IInvocation invocation)
{
var cacheAttribute = invocation.MethodInvocationTarget
.GetCustomAttributes(typeof(CacheAttribute), false)
.FirstOrDefault() as CacheAttribute;
// If the cache attribute is added ot this method, we
// need to intercept this call
if (cacheAttribute != null)
{
var cacheKey = GenerateCacheKey(invocation.Method.Name,
invocation.Arguments);
if (_memoryCache.TryGetValue(cacheKey, out object value))
{
// The results were already in the cache so return
// them from the cache instead of calling the
// underlying method
invocation.ReturnValue = value;
}
else
{
// Get the result the hard way by calling
// the underlying method
invocation.Proceed();
// Save the result in the cache
var options = new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow =
new System.TimeSpan(hours: 0, minutes: 0,
seconds: cacheAttribute.Seconds)
};
_memoryCache.Set(cacheKey, invocation.ReturnValue,
options);
}
}
else
{
// We don't need to cache the results,
// nothing to see here
invocation.Proceed();
}
}
}
Add an extension method to help register classes in DI:
public static void AddProxiedScoped<TInterface, TImplementation>
(this IServiceCollection services)
where TInterface : class
where TImplementation : class, TInterface
{
// This registers the underlying class
services.AddScoped<TImplementation>();
services.AddScoped(typeof(TInterface), serviceProvider =>
{
// Get an instance of the Castle Proxy Generator
var proxyGenerator = serviceProvider
.GetRequiredService<ProxyGenerator>();
// Have DI build out an instance of the class that has methods
// you want to cache (this is a normal instance of that class
// without caching added)
var actual = serviceProvider
.GetRequiredService<TImplementation>();
// Find all of the interceptors that have been registered,
// including our caching interceptor. (you might later add a
// logging interceptor, etc.)
var interceptors = serviceProvider
.GetServices<IInterceptor>().ToArray();
// Have Castle Proxy build out a proxy object that implements
// your interface, but adds a caching layer on top of the
// actual implementation of the class. This proxy object is
// what will then get injected into the class that has a
// dependency on TInterface
return proxyGenerator.CreateInterfaceProxyWithTarget(
typeof(TInterface), actual, interceptors);
});
}
Add these lines to ConfigureServices in Startup.cs
// Setup Interception
services.AddSingleton(new ProxyGenerator());
services.AddScoped<IInterceptor, CacheInterceptor>(
After that, if you want to use the cache interceptor, you need to do two things:
First, add the attribute to your method
[Cache(Seconds = 30)]
public async Task<IEnumerable<Person>> GetPeopleByLastName(string lastName)
{
return SomeLongRunningProcess(lastName);
}
Second, register the class in DI using the Proxy/Interception:
services.AddProxiedScoped<IPersonRepository, PersonRepository>();
Instead of the normal way without the Proxy/Interception:
services.AddScoped<IPersonRepository, PersonRepository>();
The base .NET Core container does not have any extra features like interceptors. The whole reason the DI container in .NET Core can be swapped out for something like Autofac is so you can move to a different container once you outgrow the default one.
Related
We are transitioning from Xamarin.Forms to .Net MAUI but our project uses Prism.Unity.Forms. We have a lot of code that basically uses the IContainer.Resolve() passing in a collection of ParameterOverrides with some primitives but some are interfaces/objects. The T we are resolving is usually a registered View which may or may not be the correct way of doing this but it's what I'm working with and we are doing it in backend code (sometimes a service). What is the correct way of doing this Unity thing in DryIoC? Note these parameters are being set at runtime and may only be part of the parameters a constructor takes in (some may be from already registered dependencies).
Example of the scenario:
//Called from service into custom resolver method
var parameterOverrides = new[]
{
new ParameterOverride("productID", 8675309),
new ParameterOverride("objectWithData", IObjectWithData)
};
//Custom resolver method example
var resolverOverrides = new List<ResolverOverride>();
foreach(var parameterOverride in parameterOverrides)
{
resolverOverrides.Add(parameterOverride);
}
return _container.Resolve<T>(resolverOverrides.ToArray());
You've found out why you don't use the container outside of the resolution root. I recommend not trying to replicate this error with another container but rather fixing it - use handcoded factories:
internal class SomeFactory : IProductViewFactory
{
public SomeFactory( IService dependency )
{
_dependency = dependency ?? throw new ArgumentNullException( nameof(dependency) );
}
#region IProductViewFactory
public IProductView Create( int productID, IObjectWithData objectWithData ) => new SomeProduct( productID, objectWithData, _dependency );
#endregion
#region private
private readonly IService _dependency;
#endregion
}
See this, too:
For dependencies that are independent of the instance you're creating, inject them into the factory and store them until needed.
For dependencies that are independent of the context of creation but need to be recreated for each created instance, inject factories into the factory and store them.
For dependencies that are dependent on the context of creation, pass them into the Create method of the factory.
Also, be aware of potential subtle differences in container behaviours: Unity's ResolverOverride works for the whole call to resolve, i.e. they override parameters of dependencies, too, whatever happens to match by name. This could very well be handled very differently by DryIOC.
First, I would agree with the #haukinger answer to rethink how do you pass the runtime information into the services. The most transparent and simple way in my opinion is by passing it via parameters into the consuming methods.
Second, here is a complete example in DryIoc to solve it head-on + the live code to play with.
using System;
using DryIoc;
public class Program
{
record ParameterOverride(string Name, object Value);
record Product(int productID);
public static void Main()
{
// get container somehow,
// if you don't have an access to it directly then you may resolve it from your service provider
IContainer c = new Container();
c.Register<Product>();
var parameterOverrides = new[]
{
new ParameterOverride("productID", 8675309),
new ParameterOverride("objectWithData", "blah"),
};
var parameterRules = Parameters.Of;
foreach (var po in parameterOverrides)
{
parameterRules = parameterRules.Details((_, x) => x.Name.Equals(po.Name) ? ServiceDetails.Of(po.Value) : null);
}
c = c.With(rules => rules.With(parameters: parameterRules));
var s = c.Resolve<Product>();
Console.WriteLine(s.productID);
}
}
UPDATE 2: FIXED THE CODE at the end
I have the abp.io service below with 2 parameters in the constructor instantiated via DI.
One of them, IOutcomeWriter, has 2 implementations.
I'd like to define at runtime which of the implementations of IOutcomeWriter to use.
This is the main service:
public class UCManagerService
: DomainService, IUCManagerService, ITransientDependency {
private readonly IUCInputReader _inputReader;
// This field can have 2 or 3 implementations.
private readonly IOutcomeWriter _outcomeWriter;
public UCManagerService(
IUCInputReader inputReader, IOutcomeWriter outcomeWriter) {
_inputReader = inputReader;
_outcomeWriter = outcomeWriter;
}
public async Task ExecuteAsync() {
// start processing the input and generate the output
var input = _inputReader.GetInput());
// do something
// ...
_outcomeWriter.Write(something);
}
}
The main service is registered in the AbpModule together with with IUCInputReader and the 2 implementations of IOutcomeWriter:
[DependsOn(
typeof(SwiftConverterDomainModule),
typeof(AbpAutofacModule) // <= use Autofac in some way (I don't know how)
)]
public class ProgramAppModule : AbpModule {
public override void ConfigureServices(ServiceConfigurationContext context) {
context.Services.AddTransient<IUCManagerService, UCManagerService>();
context.Services.AddTransient<IUCInputReader, UCInputReader>();
// 2 implementations of IOutcomeWriter
context.Services.AddTransient<IOutcomeWriter, OutcomeWriter1>();
context.Services.AddTransient<IOutcomeWriter, OutcomeWriter2>();
}
}
What I would like is to instantiate UCManagerService sometimes with OutcomeWriter1 and sometimes with OutcomeWriter2, according to some values in appsettings.json:
IList<JobSetting> jobsToSet = _configuration.GetSection("Jobs")
.Get<List<JobSetting>>();
foreach (JobSetting jobToSet in jobsToSet) {
// If jobsToSet.SomeValue == 'MyValue1' following line should have to
// require a IUCManagerService using OutcomeWriter1. If it is
// 'MyValue2' it'd use OutcomeWriter2, and so on:
var service = abpApplication.ServiceProvider.GetRequiredService<IUCManagerService>(); // ???
// do something else with service
// ...
}
Finally, if a tomorrow I add an OutcomeWriter3 I would just like to register it in ProgramAppModule.ConfigureServices(...) and of course use a different key in appsettings.json.
If I understand correctly, you need the IOutcomeWriter to differ based on the currently executed job. In other words, that means that you need to dynamically switch the writer based on its context.
The fact that it you need to change it dynamically, it means that is not a problem that can be solved solely using your DI configuration, because DI configurations are best kept static.
Instead, you need to mix and match a few concepts. First of all, you need a way to set the used job in the context. For instance:
// DI configuration
services.AddScoped<JobContext>();
// Execution of a job
using (var scope = abpApplication.ServiceProvider.CreateScope())
{
var context = scope.GetRequiredService<JobContext>();
context.CurrentJob = typeof(MyFirstJob);
var job = scope.GetRequiredService<MyFirstJob>();
var job.Execute();
}
In this example, JobContext is a class that holds the data that is used during the execution of a certain job. It is registered as Scoped to allow this data to be available for multiple classes within the same scope.
Now using this new JobContext, you can build an adapter for IOutcomeWriter that can forward the incoming call to the right implementation based on its injected JobContext. This might look as follows:
public class JobSpecificOutcomeWriter : IOutcomeWriter
{
private readonly JobContext context;
private readonly IList<JobSetting> settings;
private readonly IEnumerable<IOutcomeWriter> writers;
public JobSpecificOutcomeWriter(
JobContext context,
IList<JobSetting> settings,
IEnumerable<IOutcomeWriter> writers)
{
this.context = context;
this.settings = settings;
this.writers = writers;
}
// Implement all IOutcomeWriter methods by forwarding them to the
// CurrentWriter.
object IOutcomeWriter.SomeMethod(object a) =>
this.CurrentWriter.SomeMethod(a);
private IOutcomeWriter CurrentWriter
{
get
{
// TODO: Based on the current context and the settings,
// select the proper outcome writer from the writers list.
}
}
}
When JobSpecificOutcomeWriter is injected into UCManagerService (or any component for that matter), it transparently allows the proper writer to be used, without the consuming class from knowing about this.
The tricky part, actually, is to now configure your DI container correctly using JobSpecificOutcomeWriter. Depending on which DI Container you use, your mileage might vary and with the MS.DI Container, this is actually quite complicated.
services.AddTransient<IOutcomeWriter>(c =>
new JobSpecificOutcomeWriter(
context: c.GetRequiredService<JobContext>(),
settings: jobsToSet,
writers: new IOutcomeWriter[]
{
c.GetRequiredService<MyFirstJob>(),
c.GetRequiredService<MySecondJob>(),
c.GetRequiredService<MyThirdJob>(),
});
services.AddTransient<MyFirstJob>();
services.AddTransient<MySecondJob>();
services.AddTransient<MyThirdJob>();
I am using the Cookie Middleware to authenticate the user. I have been following this official tutorial.
Inside my Startup class, an excerpt from my Configure method looks like this:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// ...
// Cookie-based Authentication
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationScheme = CookieAuthenticationDefaults.AuthenticationScheme,
AutomaticAuthenticate = true,
AutomaticChallenge = true,
Events = new CustomCookieAuthenticationEvents(app),
});
// ...
}
The CustomCookieAuthenticationEvents class is defined as follows:
public class CustomCookieAuthenticationEvents : CookieAuthenticationEvents
{
private IApplicationBuilder _app;
private IMyService _myService = null;
private IMyService MyService
{
get
{
if(_myService != null)
{
return _myService;
} else
{
return _myService = (IMyService) _app.ApplicationServices.GetService(typeof(IMyService));
}
}
}
public CustomCookieAuthenticationEvents(IApplicationBuilder app)
{
_app = app;
}
public override async Task ValidatePrincipal(CookieValidatePrincipalContext context)
{
string sessionToken = context.Principal.Claims.FirstOrDefault(x => x.Type == ClaimTypes.Sid)?.Value;
LogonSession response = null;
var response = await MyService.CheckSession(sessionToken);
if (response == null)
{
context.RejectPrincipal();
await context.HttpContext.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
}
}
}
Since the dependency injection is not available at Startup.Configure (the services are not even registered at that point), I made a bit of a workaround:
Pass IApplicationBuilder service to the CustomCookieAuthenticationEvents class
Fetch IMyService upon first request inside a read-only property (singleton pattern)
tl;dr
My solution works, but it's ugly. There is no dependency injection involved, as it is not possible at that time.
The essence of the problem is that I must instantiate CustomCookieAuthenticationEvents. As far as I have read the source code, there is no way around this, because the UseCookieAuthentication throws an exception if I omit the options parameter.
Any suggestion how can one make my current solution nicer?
Startup.ConfigureServices() is called before Startup.Configure() (see https://learn.microsoft.com/en-us/aspnet/core/fundamentals/startup for more information). So Dependency Injection is available at that time ;)
As a consequence, you can resolve your dependence in your configure method like this:
app.ApplicationServices.GetRequiredService<CustomCookieAuthenticationEvents>()
You should be really careful when you resolve services inside middleware. Your current approach (and the one suggested by #arnaudauroux) can result in difficulties when you use/need/require scoped services (i.e. usage of DbContext).
Resolving via app.ApplicationServices results in static (singleton) services, when the service is registered as scoped (transient are resolved per call, so they are not affected). It would be better to resolve your service during the request from HttpContext inside ValidatePrincipal method.
public override async Task ValidatePrincipal(CookieValidatePrincipalContext context)
{
string sessionToken = context.Principal.Claims.FirstOrDefault(x => x.Type == ClaimTypes.Sid)?.Value;
LogonSession response = null;
var myService = context.HttpContext.RequestServices.GetService<IMyService >();
var response = await myService.CheckSession(sessionToken);
if (response == null)
{
context.RejectPrincipal();
await context.HttpContext.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
}
}
With this approach you don't need to pass any dependencies inside your CustomCookieAuthenticationEvents class at all. HttpContext.RequiredServices is made specifically for such classes (any other can be solved via constructor injection, but not middleware and http context related pipeline, as there is no other otherway to correctly resolve scoped services in middlewares - Middleware instance is static and only instantiated once per request)
This way you won't have lifetime issues with your scoped services.
When you resolve transient services, they will be disposed at the end of request. Whereas transient services resolved via app.ApplicationServices will be resolved at some point in future after the request is finished and when garbage collection triggers (means: your resources will be freed at the earliest possible moment, which is when the request ends).
I am writing an ASP.NET Web API 2 web service using OdataControllers I have found out how to set page size using the PageSize Property of the EnableQueryAttribute. I want to allow the users of my web service to set the page size in the app.config and then have the application read this setting and set the page size. The problem is that using the attribute requires Page Size be set to a compile time constant.
Usage of attribute:
[EnableQuery(PageSize = 10)]
public IHttpActionResult GetProducts()
{
return repo.GetProducts();
}
One proposed solution I have seen is to construct the EnableQueryAttribute and set it on the HTTPConfiguration config object like this
int customSize = ReadPageSizeSettingFromConfigFile();
var attr = new EnableQueryAttribute { PageSize = customSize };
config.AddODataQueryFilter(attr);
but this doesn't actually work. The HttpConfiguration's Filter collection remains empty.
A comment on another post (buried in a list of comments) suggested removing all the EnableQuery attributes on the controllers but that has no effect either. Since the EnableQuery attribute replaced the older Queryable attribute I am wondering if this is a Microsoft problem.
This question has been asked and not answered before: How limit OData results in a WebAPI
All help is greatly appreciated.
You can use $top and $skip to achieve your goal, if client want pagesize is 10, and want the second page:
localhost/odata/Customers?$top=10&$skip=10
About dynamically set the pagesize:
public class MyEnableQueryAttribute : EnableQueryAttribute
{
public override IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions)
{
int pagesize = xxx;
var result = queryOptions.ApplyTo(queryable, new ODataQuerySettings { PageSize = pagesize });
return result;
}
}
and put this attribute in your controller method.
You can set config for MaxTop in webApiConfig resgister method
public static class WebApiConfig{
public static void Register(HttpConfiguration config){
config.Select().Expand().Filter().OrderBy().MaxTop(100).count() // you can change max page size here
}
}
I was able to accomplish this by creating a new attribute (I called it ConfigurableEnableQueryAttribute) that extends the enable query attribute. In the constructor for that attribute, load your config file and set any settings you are interested in in the base. Personally I loop through all settings provided in an "OData" section of my appsettings, and if there are matching settings in the EnableQuery attribute, I cast them to the specified type and supply them, but you can only look for specific settings if you want.
My attribute:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class ConfigurableEnableQueryAttribute : EnableQueryAttribute
{
public ConfigurableEnableQueryAttribute()
{
var builder = new ConfigurationBuilder().AddJsonFile("appsettings.json");
var configuration = builder.Build();
var configProps = configuration.GetSection("OData").GetChildren();
var baseProps = typeof(EnableQueryAttribute).GetProperties();
foreach (var configProp in configProps)
{
var baseProp = baseProps.FirstOrDefault(x => x.Name.Equals(configProp.Key));
if (baseProp != null)
{
baseProp.SetValue(this, Convert.ChangeType(configProp.Value, baseProp.PropertyType));
}
}
}
}
and then in the controller
[HttpGet]
[ConfigurableEnableQuery]
public IQueryable<T> Get()
{
return _context.Set<T>().AsQueryable();
}
I have several Windsor Installers at my ASP.NET MVC application. They register controllers and services. Most of them depend on ICurrentService.
Controller example:
public DataStructureController(
IMapper mapper,
DataEntityService dataEntityService,
FieldDefinitionService fieldDefinitionService,
CompanyService companyService,
ICurrentService currentService,
SelectListService selectListService,
EnumResourceService enumResourceService,
WebPreprocessService preprocessService)
: base(preprocessService)
{
// Initialise variables code here
}
Service example:
public DataEntityService(DataEntitySpec specification, ICurrentService currentService)
: base(specification)
{
// Initialise variables code here
}
In my test classes I have a method, that I call once in [TestInitialize] marked method, or on/many times in [TestMethod] marked methods:
private static ICurrentService MockCurrentUser(User user)
{
var currentUserSerivceMock = new Mock<ICurrentService>(MockBehavior.Strict);
currentUserSerivceMock.Setup(x => x.UserId).Returns(user.Id);
currentUserSerivceMock.Setup(x => x.CompanyId).Returns(user.CompanyProfile.Id);
return currentUserSerivceMock.Object;
}
I want to replace ICurrentService implementation when calling the container.Resolve<> method, because it depends on HttpContext, that isn't available when unit tests run. Is it possible and how do I do it with minimum code?
You can simply create a fake HttpContext:
HttpContext.Current = new HttpContext(
new HttpRequest(null, "http://tempuri.org", null),
new HttpResponse(null));
And then in your tests:
[SetUp]
public void SetUp()
{
HttpContext.Current = new HttpContext(
new HttpRequest(null, "http://tempuri.org", null),
new HttpResponse(null));
}
[TearDown]
public void TearDown()
{
HttpContext.Current = null;
}
*Reference: http://caioproiete.net/en/fake-mock-httpcontext-without-any-special-mocking-framework/
Register your implementation as a Fallback with Windsor. Then in your test register your mock instance. That or just build up a dedicated instance of the container for your test and register what you like.