DryIoc: injecting a proxy of a request-scoped service into singleton - c#

I have a number of services that ultimately depend on the request context but, as they are expensive to instantiate, I'd like to register them with Reuse.Singleton. That means I need to inject these singletons with some form of proxies of my scoped services.
I found that I can register a scoped service A normally and then inject it as Func<A>, allowing me to manually resolve A by invoking the injected delegate, i.e.
// DryIoC config
private static ConfigureDI(...)
{
...
c.Register<A>(Reuse.InWebRequest);
c.Register<B>(Reuse.Singleton);
...
}
// Scoped service
public class A
{
public object Foo() { ... }
}
// Singleton service
public class B
{
private readonly Func<A> _a;
public object Bar()
{
return _a().Foo();
}
}
This approach seems to work, but I wonder: is there a way to do this without having to modify the original classes?

Related

How does the ViewModel constructor get the required interfaces?

My question based on InventorySampleApp by Microsoft.
The ServiceLocator contains method Configure() that register Services and ViewModels. With method GetService<T>() we can get it. For example, ProductView.cs:
ViewModel = ServiceLocator.Current.GetService<ProductDetailsViewModel>();
Each *ViewModel contains constructor with interface, for example:
public ProductDetailsViewModel(IProductService productService, IFilePickerService filePickerService, ICommonServices commonServices)
I can't understand the magiс that ViewModel uses to get such interfaces into its constructor. So there are no lines like this:
... = new ProductDetailsViewModel(productService, filePickerService, commonServices)
How does the ViewModel constructor get the required interfaces?
ServiceLocator
public class ServiceLocator : IDisposable
{
static private readonly ConcurrentDictionary<int, ServiceLocator> _serviceLocators = new ConcurrentDictionary<int, ServiceLocator>();
static private ServiceProvider _rootServiceProvider = null;
static public void Configure(IServiceCollection serviceCollection)
{
serviceCollection.AddSingleton<ISettingsService, SettingsService>();
serviceCollection.AddSingleton<IDataServiceFactory, DataServiceFactory>();
serviceCollection.AddSingleton<ILookupTables, LookupTables>();
serviceCollection.AddSingleton<ICustomerService, CustomerService>();
serviceCollection.AddSingleton<IOrderService, OrderService>();
serviceCollection.AddSingleton<IOrderItemService, OrderItemService>();
serviceCollection.AddSingleton<IProductService, ProductService>();
serviceCollection.AddSingleton<IMessageService, MessageService>();
serviceCollection.AddSingleton<ILogService, LogService>();
serviceCollection.AddSingleton<IDialogService, DialogService>();
serviceCollection.AddSingleton<IFilePickerService, FilePickerService>();
serviceCollection.AddSingleton<ILoginService, LoginService>();
serviceCollection.AddScoped<IContextService, ContextService>();
serviceCollection.AddScoped<INavigationService, NavigationService>();
serviceCollection.AddScoped<ICommonServices, CommonServices>();
serviceCollection.AddTransient<LoginViewModel>();
serviceCollection.AddTransient<ShellViewModel>();
serviceCollection.AddTransient<MainShellViewModel>();
serviceCollection.AddTransient<DashboardViewModel>();
serviceCollection.AddTransient<CustomersViewModel>();
serviceCollection.AddTransient<CustomerDetailsViewModel>();
serviceCollection.AddTransient<OrdersViewModel>();
serviceCollection.AddTransient<OrderDetailsViewModel>();
serviceCollection.AddTransient<OrderDetailsWithItemsViewModel>();
serviceCollection.AddTransient<OrderItemsViewModel>();
serviceCollection.AddTransient<OrderItemDetailsViewModel>();
serviceCollection.AddTransient<ProductsViewModel>();
serviceCollection.AddTransient<ProductDetailsViewModel>();
serviceCollection.AddTransient<AppLogsViewModel>();
serviceCollection.AddTransient<SettingsViewModel>();
serviceCollection.AddTransient<ValidateConnectionViewModel>();
serviceCollection.AddTransient<CreateDatabaseViewModel>();
_rootServiceProvider = serviceCollection.BuildServiceProvider();
}
static public ServiceLocator Current
{
get
{
int currentViewId = ApplicationView.GetForCurrentView().Id;
return _serviceLocators.GetOrAdd(currentViewId, key => new ServiceLocator());
}
}
static public void DisposeCurrent()
{
int currentViewId = ApplicationView.GetForCurrentView().Id;
if (_serviceLocators.TryRemove(currentViewId, out ServiceLocator current))
{
current.Dispose();
}
}
private IServiceScope _serviceScope = null;
private ServiceLocator()
{
_serviceScope = _rootServiceProvider.CreateScope();
}
public T GetService<T>()
{
return GetService<T>(true);
}
public T GetService<T>(bool isRequired)
{
if (isRequired)
{
return _serviceScope.ServiceProvider.GetRequiredService<T>();
}
return _serviceScope.ServiceProvider.GetService<T>();
}
#region Dispose
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (_serviceScope != null)
{
_serviceScope.Dispose();
}
}
}
#endregion
When using dependency injection, the instantiation of objects is moved to a component called Dependency Injection (DI) Container or Inverse of Control (IoC) Container. This component has some kind of registry that contains all known services that can be instantiated. In your example, the serviceCollection is that registry.
Now, whenever a component A needs an instance from the registry, there are two different options:
Directly ask the container for an instance, e. g. ServiceLocator.Current.GetService<ProductDetailsViewModel>(). This is known as the Service Locator Pattern (I'd recommend to forget this immediately).
Rather than asking the container directly, request the dependency via constructor of A (e. g. public A(ProductDetailsViewModel viewModel)).
The second approach can be pushed more and more upwards until the top of the application hierarchy is reached - the so called composition root.
Anyways, in both ways, the container uses the mechanism of Reflection. It is a way of retrieving metadata of classes, methods, properties, constructors, etc. Whenever the container is asked for a certain type (e. g. ProductDetailsViewModel), he uses reflection to get information about its constructor.
Once the constructor is resolved, its dependencies are known as well (IProductService, IFilePickerService, ICommonServices). Since these dependencies are registered within the container (remember the serviceCollection), instances can be created.
This goes on and on until there are no more dependencies and the container can start instantiating and composing all the objects. Finally, there is an instance of ProductDetailsViewModel.
If there is one dependency within the construction chain that is unknown to the container, the instantiation fails.
So basically, the process of instantiation is moved away from your code into the DI container.
Notice that the GetService method calls into ServiceProvider.GetService. This is a library method that takes care of things for you. Underneath the covers, it uses reflection to examine the constructor of the type you request.
For example, when you request a ProductDetailsViewModel, the ServiceLocator can see that it needs object of the types IProductService, IFilePickerService, ICommonServices.
It then looks into its registry of services. For example, the line
serviceCollection.AddSingleton<IProductService, ProductService>();
registers the concrete type ProductService against the interface IProductService, so whenever the ServiceLocator needs to create an IProductService object, it'll use a ProductService object.
This process is called auto-wiring and is described in more details in chapter 12 of Steven van Deursen's and my book about Dependency Injection.
I can't understand the magiс that ViewModel uses to get such interfaces into its constructor.
Indeed, do yourself the favour and learn Pure DI instead of relying on opaque libraries that you don't feel comfortable with.
I've never seen that example code base before, but from the examples posted here, it looks like it's filled with code smells and anti-patterns.

Dynamically instantiate objects when they're first invoked in autofac c#

Im trying to figure out how to dynamically instantiate class when it's first used. Something like autofac's Lazy does but without refactoring all my classes.
Is there any possiblity to do something like this:
public class MyService : IMyService {
public MyService() {
// I want it to be invoked only if SomeMethod was invoked before.
// 1. Attempt to invoke SomeMethod
// 2. call MyService.constructor
// 3. invoke MyService.SomeMethod()
}
public void SomeMethod() {
///literally any code.
}
}
It has to be done without changing existing codebase (except services registration/ autofac setup or other areas that could be changed without much effort), and all services look's like that:
public class Service : IService {
public Service(AnotherService service){
///...
}
}
My initial idea was to create Proxy class and then while registering services wrap it with that proxy.
It could look something like this:
public class Proxy<T>
{
private T _target;
private bool instantiated = false;
private void Instantiate()
{
Console.WriteLine("Creating instance");
_target = Activator.CreateInstance<T>();
}
public void xxx() - this method should be called every time any wrapped type method get's called.
{
if (instantiated == false)
{
Instantiate();
instantiated = true;
}
/// proceed with invocation. (im not sure how to do this via reflection).
}
}
The main issue with this idea is that above proxy class should be created at runtime via reflection and it has to mimic wrapping class behaviour.
I'd appreciate any advice on how to approach this problem.
All i want to lazy create dependencies in autofac container (currently if dependency A is requiring dependency B then B is instantiated, i want change this to instantiate B only if any method from A calls B.method).
Thanks!
What you're looking for is the Proxy pattern. You can create a lazy proxy as follows:
public class LazyMyServiceProxy : IMyService
{
private readonly Lazy<MyService> lazy;
public LazyMyServiceProxy(Lazy<MyService> lazy) => this.lazy = lazy;
public void SomeMethod() => this.lazy.SomeMethod();
}
You can use this proxy using the following Autofac registrations.
builder.RegisterType<MyService>();
builder.RegisterType<LazyMyServiceProxy>().As<IMyService>();

Simple Injector - Transient object implementing IDisposable, depending on other transient objects

I am writing a service that tells multiple classes implementing the interface IDeviceFinder to go look for connected devices, which the service will put in a cache for other objects to use.
The controller script looks as follows:
private Container _container;
public bool Start()
{
_container.Collection.Register<IDeviceFinder>(
new Assembly[] { Assembly.GetExecutingAssembly() });
_container.Register<IDeviceCache, DeviceCache>(Lifestyle.Singleton);
_container.Register<IDeviceService, DeviceService>();
_container.Verify();
}
Using Simple Injector, the device finders are passed to the DeviceService in the constructor. When the finders find a device they report it back to the service via a delegate. The service then proceeds to put it into the device cache (a singleton).
The device service itself implements the interface IDisposable. Using Dispose the service unsubscribes from the delegates of the device finders.
public class DeviceService : IDeviceService //IDeviceService inherits from IDisposable
{
private IDeviceCache _cache;
private List<IDeviceFinder> _finders = new List<IDeviceFinder>();
public DeviceService(IDeviceCache cache, IDeviceFinder[] finders)
{
this._cache = cache;
this._finders = finders.ToList();
foreach (var finder in this._finders)
{
finder.DeviceFound += AddDeviceToCache;
}
}
public void Dispose()
{
foreach (var finder in this._finders)
{
finder.DeviceFound -= AddDeviceToCache;
}
}
private void AddDeviceToCache(Device device)
{
//...
}
}
However, Simple Injector gives me a warning that the transient DeviceService cannot implement IDisposable.
When I change the lifestyle to be scoped, I get a warning that there is a lifestyle mismatch, because DeviceService (async scoped) depends on IDeviceFinder[] (transient).
How would I fix this error? I don't really want to get rid of the IDisposable interface.

Dependency Injection best practice when new object graph is needed

I'm new to dependency injection and still trying to wrap my head around it. As I understand, best practice according to the book Dependency Injection in .NET states that the object graph is created once at app startup in the Composition Root and the container is not accessed again in the application.
What happens when the user hits the new/open button?
Normally I would create new instances of the object graph portion needed (Forest) but if I'm not supposed to access the container, do I call a clear method that propagates the object graph? Do I somehow use lifetime management and make sure there are no current references to the portion of the object graph needed to be new?
Edit:
Example using MVVM/WPF
public class Bootstrapper {
public void Initialize() {
Container.Register<IMainViewModel, MainViewModel>();
Container.Register<IForest, Forest>();
Container.Register<ITrees, Trees>();
Container.Register<ITree, Tree>();
}
}
public class MainViewModel : IMainViewModel {
private IForest _forest;
public MainViewModel(IForest forest) { _forest = forest; }
public void New() { Forest.Clear(); }
public void AddTrees(){ _trees.Add(new Tree()); }
}
public class ViewModel : IViewModel {
private ITrees _trees;
private ITree _selectedTree;
public ViewModel(ITrees trees){ _trees = trees; }
public void AddTrees() { _trees.Add(new Tree()); }
}
public class Forest : IForest {
private ITrees _trees;
public Forest(ITrees trees){ _trees = trees; }
public void AddTree(ITree tree){ _trees.Add(tree); }
public void Clear(){ _trees.Clear(); }
}
public class Trees : ITrees {
public List<ITree> trees = new List<ITree>();
public void Add(ITree tree){ trees.Add(tree); }
public void Clear(){ trees.Clear(); }
}
according to the book Dependency Injection in .NET states that the object graph is created once at app startup in the Composition Root and the container is accessed again in the application.
Nowhere does the book state that an application's object graph should be created just once, and it certainly does not state that the container should be accessed from the application.
The 1st edition of the book however states the following (not exact quotes):
Objects graphs should be composed as close as possible to the application's entry point (section 3.3.1, page 75)
Object graphs are configured once (at application startup), but created (resolved) many time throughout the application's lifetime (section 3.3.2, page 84)
Asking a DI Container for granular services from anywhere else but the Composition Root implies the Service Locator anti-pattern. (section 5.4, page 155)
To summarize:
Object graphs are always composed inside the Composition Root
They are configured/registered just once, during application startup
Any time a new object graph is required, it can be composed. This means that object graphs can be composed many times during the application's lifetime
The code that requests a new object graph should always be part of the Composition Root to prevent the Service Locator anti-pattern.
but if I'm not supposed to access the container, do I call a clear method that propagates the object graph?
The answer to this seeming paradox is: Abstraction!
To be able to construct an object graph lazily from within an already-constructed component, you can define a specific Abstraction (an Abstract Factory to be more precise) that allows the creation of that specific service. This Factory abstraction can be defined on application level, and implemented inside the Composition Root.
For instance:
// Defined in application
public interface IForestFactory
{
IForest Create();
}
// Defined inside the Composition Root
public class ForestFactory : IForestFactory
{
private readonly Container container;
public ForestFactory(Container container) {
this.container = container;
}
public IForest Create() {
return this.container.GetInstance<IForest>();
}
}
// Inside the Bootstrapper
var factory = new ForestFactory(Container);
Container.Register<IForestFactory>(() => factory);
The second edition of the book however describes that these kinds of factories are often a code smell. Instead of using a factory, it is typically better to use a Virtual Proxy.
A Virtual Proxy is a stand-in for an object that should be created lazily. A Virtual Proxy however implements the same Abstraction as the lazily created object. This allows the Virtual Proxy to be injected into a consumer, without the consumer having to know of the existence of the Virtual Proxy.
In your case that means that you create a Virtual Proxy for IForest, for instance:
// Defined inside the Composition Root
public class LazyForestProxy : IForest
{
private readonly Container container;
public LazyForestProxy(Container container){
this.container = container;
}
// Implement IForest members
void IForest.Run() {
var forest = GetInstance();
// Forward the call to the 'real' forest
forest.Run();
}
private IForest GetInstance() {
return this.container.GetInstance<IForest>();
}
}

IoC, Dependency injection and constructor arguments

I have a service that I want to be able to create according to the Inversion of Control principle so I have created an interface and a service class.
public interface IMyService
{
void DoSomeThing1();
void DoSomeThing2();
void DoSomeThing3();
string GetSomething();
}
public class MyService : IMyService
{
int _initialValue;
//...
public MyService(int initialValue)
{
_initialValue = initialValue;
}
public void DoSomeThing1()
{
//Do something with _initialValue
//...
}
public void DoSomeThing2()
{
//Do something with _initialValue
//...
}
public void DoSomeThing3()
{
//Do something with _initialValue
//...
}
public string GetSomething()
{
//Get something with _initialValue
//...
}
}
With for example Unity I can set up my IoC.
public static class MyServiceIoc
{
public static readonly IUnityContainer Container;
static ServiceIoc()
{
IUnityContainer container = new UnityContainer();
container.RegisterType<IMyService, MyService>();
Container = container;
}
}
The problem is the constructor parameter. I could use a ParameterOverride like
var service = MyServiceIoc.Container.Resolve<IMyService>(new ParameterOverrides
{
{"initialValue", 42}
});
But I don't want to use losely typed parameters. What if someone changes the constructor parameter name or adds one parameter? He won't be warned at comple-time and maybe no one will detect it but the end user. Maybe the programmer changes he IoC setup for the tests, but forgets it for the "release" usage, then not even a codebase with 100% code coverage will detect the run-time error.
One could add an Init-function to the interface and service, but then the user of the service have to understand that and remember to call the init function every time he gets an instance of the service. The service becomes less self explanetory and open for incorrect usage. I'ts best if methods are not dependent on which order they are called.
One way to make it a little safer would be to have a Create-function on the Ioc.
public static class MyServiceIoc
{
//...
public IMyService CreateService(int initialValue)
{
var service = Container.Resolve<IMyService>();
service.Init(initialValue);
}
}
But the concerns mentioned above still applies if you only look at the service and its interface.
Does anyone have an robust solution to this problem? How can I pass an initial value to my service in a safe way still using IoC?
A DI Container is reflection-based, and fundamentally weakly typed. The problem is much broader than with Primitive Dependencies - it's present everywhere.
As soon as you do something like the following, you've already lost compile-time safety:
IUnityContainer container = new UnityContainer();
container.RegisterType<IMyService, MyService>();
var service = container.Resolve<IMyService>(new ParameterOverrides
{
{"initialValue", 42}
});
The problem is that you can remove the second statement, and the code still compiles, but now it'll no longer work:
IUnityContainer container = new UnityContainer();
var service = container.Resolve<IMyService>(new ParameterOverrides
{
{"initialValue", 42}
});
Notice that the lack of compile-time safety has nothing to do with the Concrete Dependency, but with the fact that a DI Container is involved.
This isn't a Unity problem either; it applies to all DI Containers.
There are cases where a DI Container may make sense, but in most cases, Pure DI is a simpler and safer alternative:
IMyService service = new MyService(42);
Here, you'll get a compiler error if someone else changes the API while you're looking away. That's good: compiler errors give you more immediate feedback than run-time errors.
As an aside, when you pass in a Primitive Dependency and invisibly turn it into a Concrete Dependency, you make it more difficult for the client to understand what's going on.
I'd recommend designing it like this instead:
public class MyService : IMyService
{
AnotherClass _anotherObject;
// ...
public MyService(AnotherClass anotherObject)
{
_anotherObject = anotherObject;
}
// ...
}
This is still easy and type-safe to compose with Pure DI:
IMyService service = new MyService(new AnotherClass(42));
How can I pass an initial value to my service in a safe way still using IoC?
You can explicitly call a type's constructor while registering it in Unity using the IUnityContainer.RegisterInstance method:
container.RegisterInstance<IMyService>(new MyService(42));
This would give you the compile-time safety that you mention, but the cost is that it would be instantiated only once, and would be created immediately (as opposed to when it is first requested).
You could perhaps deal with this drawback by using one of the method overloads, which accepts a LifetimeManager class.
It depends on your use case, but in IoC container world it could look something like this:
public class MyService : IMyService
{
int _initialValue;
// ...
public MyService(IConfigurationService configurationService)
{
_initialValue = configurationService.GetInitialValueForMyService();
}
// ...
}
If your class with constructor parameters is outside your code (e.g. in 3rd party library), you can use an adapter.
public class AdaptedMyService : MyService
{
public AdaptedMyService(IConfigurationService configurationService)
: base(configurationService.GetInitialValueForMyService())
{
}
}
And then register adapted class in IoC container like this:
container.Register<IMyService, AdaptedMyService>();

Categories