Learn how to leverage functional concepts of Partial Application and Function Composition for Dependency Injection in C#. This is to achieve Simplicity.
2. Why to talk about ?
IoC container granted from the start.
Developers argue about which framework to chose and not the
problem to solve.
3. DI vs. IoC
Inversion of Control (IoC) :
Objects don’t create other objects. They get them from outside
Dependency Injection (DI) :
Subset of IoC that means that object creation is done without the object
intervention, usually by a framework component
10. AOP with interception
var calculator = new Calculator();
var calculatorProxy = Intercept.ThroughProxy<ICalculator>(calculator,
new InterfaceInterceptor(), new[] { new LogBehavior() });
11. but one must :
• know Dynamic Proxy pattern
• know the difference of Instance and Type Interceptors
• know Interception behaviors
• not forget VIRTUAL keyword on methods
• wire up IoC container correctly
14. DI flavors
IoC with conventions...
Scan(x =>
{
x.TheCallingAssembly();
x.ExcludeNamespaceContainingType<IEvent>();
x.ExcludeNamespaceContainingType<SearchModel>();
x.ExcludeNamespaceContainingType<AuthenticationService>()
x.ExcludeNamespaceContainingType<DovetailController>();
x.AddAllTypesOf<IDomainMap>();
x.WithDefaultConventions();
15. DI flavors
IoC with conventions...
Scan(x =>
{
x.TheCallingAssembly();
x.ExcludeNamespaceContainingType<IEvent>();
x.ExcludeNamespaceContainingType<SearchModel>();
x.ExcludeNamespaceContainingType<AuthenticationService>()
x.ExcludeNamespaceContainingType<DovetailController>();
x.AddAllTypesOf<IDomainMap>();
x.WithDefaultConventions();
16. DI flavors
IoC with manual configuration...
var container = new UnityContainer();
container.RegisterType<IService, Service>(“Service”);
container.RegisterType<IService,ServiceDecorator>(
new InjectionConstructor(new ResolvedParameter(typeof(IS
Func<IUnityContainer, object> factoryFunc = c => new ServiceDecorator(ne
c.Resolve<ISubServiceProvider();
));
container.AddNewExtension<DecoratorContainerExtension>();
container.RegisterType<IService, ServiceDecorator>();
17. DI flavors
IoC with manual configuration...
var container = new UnityContainer();
container.RegisterType<IService, Service>(“Service”);
container.RegisterType<IService,ServiceDecorator>(
new InjectionConstructor(new ResolvedParameter(typeof(IS
Func<IUnityContainer, object> factoryFunc = c => new ServiceDecorator(ne
c.Resolve<ISubServiceProvider();
));
container.AddNewExtension<DecoratorContainerExtension>();
container.RegisterType<IService, ServiceDecorator>();
27. Functional way
int Multiply(int a, int b)
{
return a * b;
}
Partial application
Func<int, int> multiplyBy10 = b => Multiply(10, b);
28. Functional way int a = 3;
int b = a + 7;
int c = b * 10;
Composition
Func<int, int> calcCFromA = a => CalcC(CalcB(a));
int CalcCFromA(int a, int b, int c)
{
return (a + 7) * 10;
}
int CalcCFromA(int a, int b, int c)
{
return CalcC(CalcB(a));
}
let calcCFromA = calcB >> calcC
29. public class EnrollmentCommandHandler
{
private readonly StudentRepository _studentRepository;
private readonly ClassRepository _classRepository;
private readonly StudentArchiveRepository _studentArchiveRepository;
public EnrollmentCommandHandler(StudentRepository studentRepository,
ClassRepository classRepository,
StudentArchiveRepository studentArchiveRepository)
{
_studentRepository = studentRepository;
_classRepository = classRepository;
_studentArchiveRepository = studentArchiveRepository;
}
public void Enroll(StudentEnrollCommand command)
{
var student = _studentRepository.GetById(command.StudentId);
var @class = _classRepository.GetById(command.ClassId);
student.TryEnrollIn(@class);
@class.TryEnroll(student);
student.Enroll(@class);
@class.Enroll(student);
}
}
Refactor this…
30. public void Enroll(StudentRepository studentRepository, ClassRepository classRepository,
StudentEnrollCommand command)
{
var student = studentRepository.GetById(command.StudentId);
var @class = classRepository.GetById(command.ClassId);
student.TryEnrollIn(@class);
@class.TryEnroll(student);
student.Enroll(@class);
@class.Enroll(student);
}
to this…
31. var studentRepository = new StudentRepository();
var classRepository = new ClassRepository();
Action<StudentEnrollCommand> studentEnrollPipeline = c =>
handlers.Enroll(studentRepository, classRepository, c);
Bootstrap in one place…
33. var studentRepository = new StudentRepository();
var classRepository = new ClassRepository();
Action<StudentEnrollCommand> studentEnrollPipeline
= c =>
handlers.Log(c, c1 =>
handlers.Enroll(studentRepository, classRepository, c1));
Want to log ?
34. var studentRepository = new StudentRepository();
var classRepository = new ClassRepository();
Action<StudentEnrollCommand> studentEnrollPipeline
= c =>
handlers.Audit(c, c1 =>
handlers.Log(c1, c2 =>
handlers.Enroll(studentRepository, classRepository, c2));
Audit ?
35. var lifeTime = new LifeTime();
Func<StudentRepository> studentRepositoryFactory = () => new StudentRepository();
Func<ClassRepository> classRepositoryFactory = () => new ClassRepository();
Action<StudentEnrollCommand> studentEnrollPipeline
= c =>
handlers.Audit(c, c1 =>
handlers.Log(c1, c2 =>
handlers.Enroll(lifeTime.PerThread(studentRepositoryFactory),
lifeTime.PerThread(classRepositoryFactory), c2));
Lifetime management ?
36. public TResult PerThread<TResult>(Func<TResult> dependencyFactory) where TResult : class
{
ThreadLocal<object> threadLocal = new ThreadLocal<object>(dependencyFactory);
threadLocal = _dependencies.GetOrAdd(typeof(TResult), threadLocal);
return (TResult)threadLocal.Value;
}
Easy !
37. Why to do it ?
• 99% of time you don’t have a problem for IoC container
• IoC makes easier things you shouldn’t be doing anyway
• Feel the pain and think
39. To take away
• Be stupid! Don’t waste your brain on complicated code
• Don’t waste your time to understand Rube Goldberg machines
• Simplicity
• Readability
42. References
• Greg Young (8 lines of code InfoQ
http://www.infoq.com/presentations/8-lines-code-refactoring)
Notes de l'éditeur
My talk about DI containers with Olivier Spinelli several years ago at ALT.NET meetup. Now, this is where I am.
But before looking why, let's see a context.
We don't even question if IoC container is needed because nowadays this is considered granted. We argue between containers and theirs features rather than a correct application design.
Can I explain it to junior developer ?
Do you want to not hire developers because we don't have 2 years of teaching a framework ?
How many developpers do you know that knows Rube Goldberg machine ?
What to do if there is a bug in a framework ? You send an email to the group and pray
What kills simplicity is that sometimes Rube Goldberg machine stops working and by definition you don't understand Rube Goldberg machine