We're using ASP.NET core and are running into the problem, that some registered services (from third-party libraries) request a specific 'service' (based on an interface) that has been deprecated.
The problem is that we don't know which libraries are using this deprecated service interface.
What we've done so far is:
create a custom implementation for the service interface
and registered this custom implementation (class) with DI
// Registration
services.AddTransient<IServiceInterface>((services) => new CustomCustomService(Log.Logger));
// Custom implementation
internal class CustomService : IServiceInterface
{
public CustomService (ILogger logger)
{
logger.Warning("!!! CustomService is still being used !!!");
}
}
So now we can see that the unwanted service is being used 'somewhere'.
But is it in some way possible to detect for which service the deprecated service has been created?
I've tried listing the stack trace using
var st = new System.Diagnostics.StackTrace();
logger.Warning("!!! CustomService is still being used !!!" + Environment.NewLine + "{stacktrace}", st.ToString());
But that doesn't seem to give information about the service using the deprecated service...
You can try the following:
var registrationsDependingOnMyService =
from descriptor in services
where descriptor.ImplementationType != null
let dependencies =
from ctor in descriptor.ImplementationType!.GetConstructors()
from param in ctor.GetParameters()
select param.ParameterType
where dependencies.Contains(typeof(IServiceInterface))
select descriptor;
This will query the IServiceCollection for registrations whose implementation type have a constructor argument of type IServiceInterface.
This might not be a bulletproof solution, as types or registrations can more sneakily depend on the service collection (e.g. by calling back into the IServiceProvider from within a registration delegate), but this is likely the best you can do with MS.DI.
So basically - as expected - it's not possible to exactly know which libraries (for which you don't have the code) use a certain dependency.
It's just trial and error ;)
Thanks for the ideas everyone.
Related
Problem Statement: I have a custom IConfigurationProvider that requires a complex service to function properly. This complex service would, naturally be registered to the applications IServiceProvider. I want to use IServiceCollection/IServiceProvider facilities in conjunction with IConfigurationProvider to avoid manual new of this complex service, and to re-use registration code that would otherwise be written in the normal part of the DI container building portion of the app.
I've found plenty of documentation describing the troubles of needing an IServiceProvider in an IConfigurationProvider. This is the closest thing that felt ok to me, and is the inspiration for this post.
Here's my approach at a high level
Build the configuration up enough to construct the intermediate IServiceProvider
Build the intermediate IServiceProvider
Build the rest of the configuration via custom IConfigurationProvider's that require special services, retrieved via intermediateServiceProvider.GetRequiredService<T>();
Transfer the registrations and, specifically, singleton objects, from the intermediate IServiceCollection/IServiceProvider to the final IServiceCollection/IServiceProvider. This will help avoid re-registering things in step #5 and will help avoid second instances of singletons in the final IServiceProvider.
Register the final set of services to complete the final IServiceProvider, using configuration that was injected in step #4.
#1,#2,#3,#5 are simple enough. #4 is where I'm hitting roadblocks. My first attempt at #4 was the following
foreach (var sd in intermediateServiceCollection)
{
if (sd.Lifetime == ServiceLifetime.Singleton)
{
// Externally owned
if (sd.ImplementationInstance != null)
{
finalServiceCollection.AddSingleton(sd.ServiceType, sd.ImplementationInstance);
}
// Provide a factory function to delegate to intermediate service provider
else
{
finalServiceCollection.AddSingleton(sd.ServiceType,
s => intermediateServiceProvider.GetRequiredService(sd.ServiceType));
}
}
// Transient/scoped service descriptors can be forwarded along without issue
else
{
finalServiceCollection.Add(sd);
}
}
As documented here, registering open-generic types with a factory function is not supported.
After stumbling upon this limitation, my latest approach looks like:
foreach (var sd in intermediateServiceCollection)
{
if (sd.Lifetime == ServiceLifetime.Singleton)
{
// Externally owned
if (sd.ImplementationInstance != null)
{
finalServiceCollection.AddSingleton(sd.ServiceType, sd.ImplementationInstance);
}
// Provide a factory function to delegate to intermediate service provider
else if (!sd.ServiceType.IsGenericType)
{
finalServiceCollection.AddSingleton(sd.ServiceType,
s => intermediateServiceProvider.GetRequiredService(sd.ServiceType));
}
else
{
// Simply adding the service descriptor to the final service collection
// opens the door for singleton instances to be created again
//
// In reality, this may be configurable to raise an exception to signal
// to our developers they need to avoid registering open-generics in the
// bootstrapping portion of the app. But, this may serve it's purpose
// if you can live with multiple instances of a singleton.
finalServiceCollection.Add(sd);
}
}
// Transient/scoped service descriptors can be forwarded along without issue
else
{
finalServiceCollection.Add(sd);
}
}
Obviously, my current implementation is not perfect as it allows for multiple singleton instances if that singleton is registered as an open-generic. But, with an understood limitation of bootstrap registration being non open-generic types, I can "successfully" create an intermediate IServiceProvider for use within IConfigurationProvider's and transfer it to the final IServiceProvider.
Can anyone provide inspiration that can lead to a complete implementation for #4, specifically around transferring open-generic registrations?
Is the assumption that this approach is reasonable total nonsense and I should opt for a different pattern to configure my application?
If you use the same configuration provider for intermediate and final service provider and you need the same services in the final service provider as within the intermediate one, why don't you put your whole setup logic of the intermediate provider into a method that gets the target builder as parameter? Then you can first call it to setup your intermediate one and later another time to setup your final provider.
In that case you don't need any kind of reflection and you can use all available extension helper class or own logic to setup the configuration provider.
I use AutoFac. I have to resolve a type with an explicit instance which I get from another service.
For example: I have an instance of type Client which I get from somewhere (not from the container).
I want to configure the Autofac container so that always when an object of type Client should be resolved, it should return my instance.
Problem is that I don't have this instance at the time, when I configure the container with the Containerbuilder - so I cannot use for example LambdaRegistration.
Is there another solution for solving my problem?
You can do the following:
MyService service = null;
builder.Register(c => service).As<IMyService>();
// Later on
service = new MyService();
Depending on your needs there are quite some variations of this approach possible, such as:
Send a 'setter' delegate to some initialization code that will call the delegate after the service gets created, e.g. MyServiceInitializer.AfterInitialization(s => service = s);
Promote the service variable to a class property and provide that new wrapper to the initialization
Hide access to the service behind specific read and write abstractions, e.g. interface IMyServiceContext { IMyService Current { get; } } and interface IMyServiceSetter { void SetCurrent(IMyService service); }.
Prevent Autofac from accidentally resolving the service before it is initialized by throwing an exception instead of throwing null, e.g. Register(c => service ?? throw new InvalidOperationException("..."))
It's important to note, however, that in general, the creation of components should be fast and reliable. The fact that your component isn't available at startup is likely because it requires I/O to setup. This is a situation should should try to prevent, for instance by hiding it behind an abstraction completely. This allows you to implement a Proxy that allows the real service to be lazy loaded.
Hopefully this gives you some clues on how to solve this.
Details
I have attempted to create a background processing structure using the recommended IHostedService interface in ASP.NET 2.1. I register the services as follows:
services.AddSingleton<AbstractProcessQueue<AbstractImportProcess>>();
services.AddHostedService<AbstractBackgroundProcessService<AbstractImportProcess>>();
services.AddSignalR();
The AbstractProcessQueue is just a wrapper around a BlockingCollection of processes that can be enqueued and dequeued. The AbstractBackgroundProcessService implements the IHostedService interface and looks at the queue for new processes it can start.
Now, the trouble starts when, inside a SignalR hub, I attempt to get a reference to the background processing service via the Dependency Injection mechanisms. I have tried the following solutions, but none seem to be working as intended:
Option 1:
public HubImportClient(IServiceProvider provider)
{
//This returns null.
var service = provider.GetService<AbstractBackgroundProcessService<AbstractImportProcess>>();
}
Option 2:
public HubImportClient(IServiceProvider provider)
{
//This returns null.
var service = (AbstractBackgroundProcessService<AbstractImportProcess>) provider.GetService(typeof(AbstractBackgroundProcessService<AbstractImportProcess>>));
}
Option 3:
public HubImportClient(IServiceProvider provider)
{
//This throws an exception, because the service is missing.
var service = provider.GetRequiredService<AbstractBackgroundProcessService<AbstractImportProcess>>();
}
Option 4:
public HubImportClient(IServiceProvider provider)
{
//This throws an exception, because the service is missing.
var service = (AbstractBackgroundProcessService<AbstractImportProcess>) provider.GetRequiredService(typeof(AbstractBackgroundProcessService<AbstractImportProcess>);
}
Option 5:
public HubImportClient(IServiceProvider provider)
{
//This returns a correct service, but prevents me from adding additional AbstractBackgroundProcessService implementations with different type parameters.
//Additionally, it seems like this reference was newly created, and not the instance that was created on application startup (i.e. the hash codes are different, and the constructor is called an additional time).
var service = provider.GetService<IHostedService>();
if(service is AbstractBackgroundProcessService<AbstractProcessService>)
{ this.Service = (AbstractBackgroundProcessService<AbstractProcessService>) service;}
}
Option 6:
public HubImportClient(IServiceProvider provider)
{
//This works similarly to the previous option, and allows multiple implementations, but the constructor is still called twice and the instances thus differ.
AbstractBackgroundProcessService<AbstractImportProcess> service = null;
foreach(IHostedService service in provider.GetServices<IHostedService>())
{
if(service is AbstractBackgroundProcessService<AbstractImportProcess>)
{
service = (AbstractBackgroundProcessService<AbstractImportProcess>) service;
break;
}
}
}
Option 7:
public HubImportClient(IServiceProvider provider)
{
//This just skips the for each loop all together, because no such services could be found.
AbstractBackgroundProcessService<AbstractImportProcess> service = null;
foreach(AbstractBackgroundProcessService<AbstractImportProcess> current in provider.GetServices<AbstractBackgroundProcessService<AbstractImportProcess> >())
{
service = current;
break;
}
}
Option 8:
//This works, but prevents multiple implementations again.
public HubImportClient(IHostedService service)
{
this.Service = service;
}
Option 9:
//This does not work again.
public HubImportClient(AbstractBackgroundProcessService<AbstractImportProcess> service)
{
this.Service = service;
}
Question
So then my question remains: how am I supposed to get a reference to an IHostedService implementation so that:
(a): I can inject multiple instances of the service that differ only by their type parameter (e.g. a hosted service for AbstractImportProcesses as well as one for AbstractExportProcesses)
(b): there is only ever one instance of the IHostedService for that specific type parameter.
Thanks in advance for any help!
Current workaround from mentioned git page:
services.AddSingleton<YourServiceType>();
services.AddSingleton<IHostedService>(p => p.GetRequiredService<YourServiceType>());
Or, if your service implements some other interfaces:
services.AddSingleton<YourServiceType>();
services.AddSingleton<IYourServiceType>(p => p.GetRequiredService<YourServiceType>());
services.AddSingleton<IHostedService>(p => p.GetRequiredService<YourServiceType>());
This creates your service as hosted (runs and stops at host's start and shutdown), as well as gets injected as depedency wherever you require it to be.
Update:
I don't see this solution as a "workaround" anymore.
Instead, I would describe it this way: hosted component and a regular service are entities of different types, each one serving its own purpose.
The solution above, however, allows one to combine them, registering a hosted component as a service, which can be used in the dependency resolution chain.
Which is awesome.
This is just a slight modification to the answer by #AgentFire. This method is clearer and allows for several background hosted services in a single Web Service.
services.AddSingleton<YourServiceType>();
services.AddHostedService<YourServiceType>(p => p.GetRequiredService<YourServiceType>());
There has been some discussion around this topic. For example, see: https://github.com/aspnet/Hosting/issues/1489. One of the problems that you'll run into is that hosted services are added as transient services (from ASP.NET Core 2.1+), meaning that resolving an hosted service from the dependency injection container will result in a new instance each time.
The general advice is to encapsulate any business logic that you want to share with or interact from other services into a specific service. Looking at your code I suggest you implement the business logic in the AbstractProcessQueue<AbstractImportProcess> class and make executing the business logic the only concern of AbstractBackgroundProcessService<T>.
In .Net Core 3.1 and .Net 5.0you can get references to the existing instances of the Hosted Services with:
IEnumerable<IHostedService> allHostedServices = this.serviceProvider.GetService<IEnumerable<IHostedService>>();
You get this directly from the IServiceProvider in dotnet core 3.1 and later:
var myHostedService = serviceProvider
.GetServices<IHostedService>()
.OfType<MyHostedService>()
.Single();
How to replicate this code with Autofac syntax?
public static class MenuConfig
{
public static void Initialize()
{
var _menuService = DependecyFactory.GetInstance<IMenuService>();
Parameters.Menu = _menuService.Menu();
}
}
Before calling this a "duplicate question" please note that I'm looking for an Autofac command. I CANNOT inject the interface anywhere and then call "Resolve". What I need to is perform an "InstancePerRequest" inline and uninjected so I don't have to do this:
var _service = new Service(new Dependency(new context()));
LightInject has a method that allows instantiation from an interface OUTSIDE of a constructor like this:
var _service = DependecyFactory.GetInstance<IService>();
What is the equivalent method for Autofac?
When calling containerBuilder.Build() you get back a container which implements IContainer and ILifetimeScope, whenever you get hold of one of these interfaces, you can resolve types from it:
container.Resolve<IService>();
If you want this container to be static, you could add the container as a static property to the Program or Startup class (depending if you're creating a Console or ASP.NET application).
Remember that the root container will be around for the entire duration of your application, so this can result in unwanted memory leaks when used incorrectly. Also see the warning in the documentation.
Still, it's perfectly possible to do the memory management yourself by resolving an Owned<> version from your interface:
using (var service = Program.Container.Resolve<Owned<IService>>())
{
service.Value.UseService();
}
Anyway, since you mention a static class in the comments, the best solution is to change that into a non-static class and register it as a singleton with Autofac. Then you can inject a Func<Owned<IService>> serviceFactory into that singleton and create/dispose an instance of the service wherever you need it.
using (var service = serviceFactory())
{
service.Value.UseService();
}
This is simply not possible with Autofac. All other solutions involving Autofac will require code refactoring which may potentially break software functionality. So unfortunately, the most elegant and least disruptive solution is this:
var _service = new Service(new Dependency(new context()));
Since this is an edge case addressing only one part of the software, this compromise is acceptable. It would be nice, however, if Autofac implemented this functionality in some future release.
I am currently getting started with the extending of Microsoft Dynamics CRM using Plugins.
Is it possible to add Dependency injection to these plugins (for testing, loose coupling, etc. purposes)? Where can I register my IoC-container so that it's used over all the plugins of the same type?
We've been trying to unit test and apply dependency injection on our Dynamics CRM application. Unfortunately, as Microsoft support and consultants are confirmed, there is no supported way to do it. You may either transfer all of your plugin business logic to an another business class and apply dependency injection or stop thinking about it.
If you choose to fight back with Dynamics CRM, you need to define a static field on a plugin super class which will be your DI Container. As follows,
public abstract class SuperPlugin : IPlugin{
public void Execute(IServiceProvider serviceProvider){
// initialize a static container instance if not available
var containerWrapper = new ContainerWrapper{
Container = serviceProvider.GetService(typeof(IPluginExecutionContext)),
Resolver = //static resolver instance of dependency container
};
OnExecution(containerWrapper);
}
public abstract void OnExecution(IDependencyResolver resolver);
}
I really cannot understand why Microsoft doesn't simply let us register some components to the IServiceProvider implementation that they are using internally.
Ps. Since your SuperPlugin class is an IPlugin, you may forget to write the interface implementation on the sub class. But we encountered some bugs on Plugin Registration tool that is shipped with official Dynamics CRM SDK. So in case you may have the same problem you should also implement your plugins as follows,
public class MyPlugin : SuperPlugin, IPlugin{
public abstract void OnExecution(IDependencyResolver resolver){};
}
Edit: See a small example that explains the concept https://github.com/nakahparis/DIForCRM
Plugins in CRM are the Bane of Unit Testing:
Issues with non-plugin test
No way to temporarily disable
Easy to forget it is running
Issues with testing plugins themselves
Unable to unit test and attach to process
A lot to mock out, Pipeline, Service Provider etc
Runs multi-threaded
This has led me to the following solution for testing plugins:
Get rid of the plugin context as quickly as possible, extracting out all objects and service required from it right away.
Create an ExecutePlugin method to hook unit tests into, and immediately call this method after extracting the objects from the plugin context.
Push as much code as possible into the business layer.
This results in plugins that look like this (with a heavy use of extension methods to make this simpler):
public void Execute(IServiceProvider provider)
{
var context = provider.GetContext();
var service = provider.GetService(context);
var target = GetTarget<Contact>(context);
if (target == null || !target.ContainsAllNonNull(c => new
{
c.FirstName,
c.LastName,
}))
{
// Entity is of the wrong type, or doesn't contain all of the required attributes
return;
}
ExecutePlugin(service, target);
}
public void ExecutePlugin(IOrganizationService service, Contact target){
// Logic Goes Here
}
Once this is done, the only thing you need to unit test the ExceutePlugin is your own IOrganizationService that mocks out the required calls and you have your unit testing done. I don't even bother unit testing the Execute method. Either it'll work, or it won't and blow chow on the first use from within CRM.