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);
}
Related
I have a three layered architecture.
I can't use constructor injection and I need to get access to a service in my business code, in which I don't have access to HttpContext.
For example, in action methods, or in filters or middleware I can get a service using:
HttpContext.RequestServices.GetRequiredService<ITranslator>();
But in my business code, I don't have access to HttpContext.
How can I get an instance of my service?
Update:
Here's my business code:
public class InvoiceBusiness
{
// for some reasons, I can't use constructor injection here
public void CalculateTranslationsInvoice(long customerId)
{
// I need to get an instance of ITranslator here, and a couple of other services.
// If this method was in a controller, I could use HttpContext.RequestServices.
// But here what should I do?
}
}
If you're needing to access HTTP concerns in the inner layers, you should abstract it to an interface.
Assume you need to access the current user. Normally, you'd use HttpContext.User. But you can't access it in the domain layer.
The solution is to define an interface in your domain layer that encapsulates what your ITranslator implementation actually needs from the HTTP context.
public interface IUserAccessor {
ClaimsPrincipal CurrentUser { get; }
}
public class Translator: ITranslator {
// inject the interface
private readonly IUserAccessor _userAccessor;
public Translator(IUserAccessor userAccessor) {
_userAccessor = userAccessor;
}
// ...
}
Keep this interface as focused as possible. Here, I'm OK with using ClaimsPrincipal and having a dependency on the standard library, but if you're not, you can just extract the user id claim if that makes sense in your application.
Then implement this interface in the application/HTTP layer.
internal class HttpUserAccessor: IUserAccessor {
IHttpContextAccessor _httpAccessor;
public HttpUserAccessor(IHttpContextAccessor httpAccessor) {
_httpAccessor = httpAccessor;
}
public ClaimsPrincipal CurrentUser => _httpAccessor.HttpContext?.User;
}
Then register this implementation:
services.AddHttpContextAccessor();
services.AddScoped<IUserAccessor, HttpUserAccessor>();
Now you can access HTTP concerns in any layer without that layer knowing where the data actually comes from.
The bottom line is: you don't have to forego dependency injection. You can define & implement interfaces in different layers.
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
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));
I've taken this approach to injecting a custom resource provider in my ASP.NET MVC application, but I'm having some problems with object lifetime management.
I'm using Castle Windsor, so I have the following implementation of the factory:
public class DefaultResourceProviderFactory : ResourceProviderFactory
{
public override IResourceProvider CreateGlobalResourceProvider(string classKey)
{
// IoC is a static helper class that gives me static access to the
// container. IoC.Resolve<T>(args...) simply calls container.Resolve<T>(args...).
return IoC.Resolve<IResourceProvider>(new { resourceType = "Global" });
}
public override IResourceProvider CreateLocalResourceProvider(string virtualPath)
{
// resourceType
return IoC.Resolve<IResourceProvider>(new { ResourceType = virtualPath });
}
}
However, the IResourceProvider I have registered in the container doesn't seem to have its lifetime managed correctly. It has some other dependencies of its own, some of which have somewhat complicated lifestyles (per web request or per transaction), so I've registered the IResourceProvider as transient to ensure that its dependencies are always valid. But the MVC framework is stepping on my toes, keeping a reference to the IResourceProvider across web requests, which causes ObjectDisposedExceptions when its dependencies have been invalidated on the next request.
What I'd like to do, is to make the MVC framework use the factory every time it needs an instance of my IResourceProvider, and - if possible - also to invoke IoC.Release(provider) or something similar when it's done with it.
How do I micro-manage the lifestyle of the custom IResourceProvider in a way that the MVC framework will respect?
After searching around for various ways to control the lifetime of the IResourceProvider itself, I decided that it was better to refactor my implementation to utilize the Typed Factory Facility.
My IResourceProvider implementation formerly looked something like this:
public class CachedResourceProvider : IResourceProvider {
CachedResourceProvider(IResourceRecordRepository repo) { /* ... */ }
// other members...
}
Now, I changed it to this instead:
public class CachedResourceProvider : IResourceProvider {
CachedResourceProvider(IResourceRecordRepositoryFactory repo) { /* ... */ }
// other members...
}
The factory interface is a new one, defined as
public interface IResourceRecordRepositoryFactory {
IResourceRecord NewInstance();
void Release(IResourceRecord instance);
}
and every usage of the private _repo instance in the CachedResourceProvider was refactored to three statements: get a repo instance from the factory, use the repo instance to fetch/save something, release the instance through the factory.
I registered them like this:
container.AddFacility<TypedFactoryFacility>();
container.Register(Component.For<IResourceRecordRepositoryFactory>().AsFactory());
Now, even though MVC is keeping a reference to my resource provider across web requests, the services it uses are re-fetched from the Windsor container each time they're used, so the container is in full control of their lifetime.
I am making a rest service using ServiceStack (http://www.servicestack.net). I'm using the unit of work pattern for my data access layer. I am using StructureMap to connect all my services and the unit of work together.
What I need to do is to create a single unit of work for each individual request that I receive and then dispose of it after.
I have a WCF Service which is using the mechanism here, http://andreasohlund.net/2009/04/27/unitofwork-in-wcf-using-structuremap.
Essentially resulting in something like this
ObjectFactory.Initialize(x =>
{
x.Scan(a =>
{
a.AssemblyContainingType<IUnitOfWork>();
a.WithDefaultConventions();
});
x.For<IUnitOfWork>().LifeCycleIs(new WcfOperationLifecycle());
}
I am looking for a similar 'Lifecycle' for ServiceStack.
[Solution]
The solution is in the comments of the accepted answer.
a) Set the StructureMap lifecycle to HttpContext
x.For<IUnitOfWork>().LifecycleIs(Lifecycles.GetLifecycle(InstanceScope.HttpContext));
b) Updated the structure map IOC adapter to extend the IRelease interface
class StructureMapContainerAdapter : IContainerAdapter, IRelease
{
public T Resolve<T>()
{
return ObjectFactory.GetInstance<T>();
}
public T TryResolve<T>()
{
return ObjectFactory.TryGetInstance<T>();
}
public void Release(object instance)
{
ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
}
}
Sounds like you just want Request Scope?
Also check out the concrete Service base class on how you can use Lazy loading + Dispose() to get this behaviour.
As well as in ServiceStack's new API you can override your services OnBeforeExecute() OnAfterExecute() event hooks by using your own ServiceRunner (in the older API you would need to provide a custom service base class).