Skip to content

Instantly share code, notes, and snippets.

@davidfowl
Last active March 16, 2023 09:26
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save davidfowl/86490126bf4a4f1301aa5bb049d8d244 to your computer and use it in GitHub Desktop.
Save davidfowl/86490126bf4a4f1301aa5bb049d8d244 to your computer and use it in GitHub Desktop.
DI under the hood. This is what DI containers automate for you
using System;
using System.Threading;
namespace PureDI
{
class Program
{
static void Main(string[] args)
{
// Create the singletons once
var singletons = new SingletonDependencies();
// Create a transient object
var myClass = new MyTransientClass1(singletons.Singleton1, singletons.Singleton2, new MyTransientClass2(new MyTransientClass3()));
// Create another transient object
var otherClass = new MyTransientClass2(new MyTransientClass3());
}
static void WebRequest(SingletonDependencies singletons)
{
// Make a per request scope with access to the singletons
var requestScope = new ScopedDependencies(singletons);
// Create a controller passing the request scoped dependency and transient dependency
var controller = new MyController(requestScope.Scoped1);
}
}
public class MyController
{
public IScoped1 Scoped1 { get; }
public MyController(IScoped1 scoped1)
{
Scoped1 = scoped1;
}
}
// Transient dependencies
public interface IMyTransientClass1 { }
public interface IMyTransientClass2 { }
public interface IMyTransientClass3 { }
public class MyTransientClass1 : IMyTransientClass1
{
public ISingleton1 Singleton1 { get; }
public ISingleton2 Singleton2 { get; }
public IMyTransientClass2 MyTransientClass2 { get; }
public MyTransientClass1(ISingleton1 singleton1, ISingleton2 singleton2, IMyTransientClass2 myOtherClass)
{
Singleton1 = singleton1;
Singleton2 = singleton2;
MyTransientClass2 = myOtherClass;
}
}
public class MyTransientClass2 : IMyTransientClass2
{
public IMyTransientClass3 MyTransientClass3 { get; }
public MyTransientClass2(IMyTransientClass3 anotherClass)
{
MyTransientClass3 = anotherClass;
}
}
public class MyTransientClass3 : IMyTransientClass3
{
}
// Scoped dependencies
public interface IScoped1 { }
public interface IScoped2 { }
public interface IScoped3 { }
public class Scoped1 : IScoped1
{
}
public class Scoped2 : IScoped2
{
public IScoped1 Scoped1 { get; }
public Scoped2(IScoped1 scoped1)
{
Scoped1 = scoped1;
}
}
public class Scoped3 : IScoped3
{
public IMyTransientClass1 MyTransientClass1 { get; }
public IScoped2 Scoped2 { get; }
public Scoped3(IMyTransientClass1 myTransientClass1, IScoped2 scoped2)
{
MyTransientClass1 = myTransientClass1;
Scoped2 = scoped2;
}
}
// The class that represents all scoped dependencies
public class ScopedDependencies
{
private IScoped1 _scoped1;
private IScoped2 _scoped2;
private IScoped3 _scoped3;
private object _lockObject = new object();
private readonly SingletonDependencies _singletons;
public ScopedDependencies(SingletonDependencies singletons)
{
_singletons = singletons;
}
public IScoped1 Scoped1 => LazyInitializer.EnsureInitialized(ref _scoped1, ref _lockObject, () => new Scoped1());
public IScoped2 Scoped2 => LazyInitializer.EnsureInitialized(ref _scoped2, ref _lockObject, () => new Scoped2(Scoped1));
public IScoped3 Scoped3 => LazyInitializer.EnsureInitialized(ref _scoped3, ref _lockObject, () => new Scoped3(
new MyTransientClass1(
_singletons.Singleton1,
_singletons.Singleton2,
new MyTransientClass2(new MyTransientClass3())),
Scoped2));
}
// Singleton dependencies
public interface ISingleton1 { }
public interface ISingleton2 { }
public class Singleton1 : ISingleton1
{
public ISingleton2 Singleton2 { get; }
public Singleton1(ISingleton2 singleton2)
{
Singleton2 = singleton2;
}
}
public class Singleton2 : ISingleton2 { }
// The class that represents all singleton dependencies
public class SingletonDependencies
{
private ISingleton1 _singleton1;
private ISingleton2 _singleton2;
private object _lockObject = new object();
public ISingleton1 Singleton1 => LazyInitializer.EnsureInitialized(ref _singleton1, ref _lockObject, () => new Singleton1(Singleton2));
public ISingleton2 Singleton2 => LazyInitializer.EnsureInitialized(ref _singleton2, ref _lockObject, () => new Singleton2());
}
}
@davidfowl
Copy link
Author

Thanks, did not know that, but sounds logical. Wonder if this also happens with singletons, because if they use the resolver to resolve idisposable transient dependencies that could result in memory leaks.

https://github.com/dotnet/extensions/issues/2207

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment