42
Dependency Injection 2015 The functional way without IoC container @tjaskula

Functional Dependency Injection in C#

Embed Size (px)

Citation preview

Dependency Injection 2015 The functional way without IoC container

@tjaskula

Why to talk about ?

IoC container granted from the start.Developers argue about which framework to chose and not the problem to solve.

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

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);

try{

student.TryEnrollIn(@class);@class.TryEnroll(student);

student.Enroll(@class);@class.Enroll(student);

}catch (Exception e){

// log}

}

New requirements

Log, Security, Audit, Cache…Cross Cutting Concerns

public class EnrollmentCommandHandler{

private readonly StudentRepository _studentRepository;private readonly ClassRepository _classRepository;private readonly StudentArchiveRepository _studentArchiveRepository;private readonly UnitOfWork _unitOfWork;private readonly EnrollementNotificationService _notificationService;private readonly ILogger _logger;private readonly AuthorizationService _authorizationService;private readonly CalendarService _calendarService;private readonly ServiceFoo _serviceFoo;private readonly ServiceBlah _serviceBlah;private readonly FactoryFoo _facoFactoryFoo;private readonly FactoryBlah _factoryBlah;

public EnrollmentCommandHandler(StudentRepository studentRepository,ClassRepository classRepository,StudentArchiveRepository studentArchiveRepository,UnitOfWork unitOfWork,EnrollementNotificationService notificationService,ILogger logger,AuthorizationService authorizationService,CalendarService calendarService,ServiceFoo serviceFoo,ServiceBlah serviceBlah,FactoryFoo facoFactoryFoo,FactoryBlah factoryBlah

){

_studentRepository = studentRepository;_classRepository = classRepository;_studentArchiveRepository = studentArchiveRepository;_unitOfWork = unitOfWork;_notificationService = notificationService;_logger = logger;_authorizationService = authorizationService;_calendarService = calendarService;_serviceFoo = serviceFoo;_serviceBlah = serviceBlah;_facoFactoryFoo = facoFactoryFoo;_factoryBlah = factoryBlah;

} }

public void Handles(StudentEnrollCommand command){

var student = _studentRepository.GetById(command.StudentId);var @class = _classRepository.GetById(command.ClassId);

try{

_unitOfWork.BeginTransaction();student.TryEnrollIn(@class);@class.TryEnroll(student);

…or better… AOP to the rescue

[Logable][Authorizable][Cachable][ExceptionPolicy][Blablable]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 Handles(StudentEnrollCommand command){

var student = _studentRepository.GetById(command.StudentId);var @class = _classRepository.GetById(command.ClassId);

try{

student.TryEnrollIn(@class);@class.TryEnroll(student);

student.Enroll(@class);@class.Enroll(student);

}catch (Exception e){

// log}

}

Yes, but container can do more !

AOP with interceptionvar calculator = new Calculator();

var calculatorProxy = Intercept.ThroughProxy<ICalculator>(calculator,

new InterfaceInterceptor(), new[] { new LogBehavior() });

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

Really ?!!! Is this…

SIMPLE ?

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();

x.With<DomainEntityAliaser>();

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();

x.With<DomainEntityAliaser>();

DI flavors

IoC with manual configuration...

var container = new UnityContainer();

container.RegisterType<IService, Service>(“Service”);

container.RegisterType<IService,ServiceDecorator>(

new InjectionConstructor(new ResolvedParameter(typeof(IService

Func<IUnityContainer, object> factoryFunc = c => new ServiceDecorator(new Service(

c.Resolve<ISubServiceProvider();

));

container.AddNewExtension<DecoratorContainerExtension>();

container.RegisterType<IService, ServiceDecorator>();

container.RegisterType<IService, Service>();

DI flavors

IoC with manual configuration...

var container = new UnityContainer();

container.RegisterType<IService, Service>(“Service”);

container.RegisterType<IService,ServiceDecorator>(

new InjectionConstructor(new ResolvedParameter(typeof(IService

Func<IUnityContainer, object> factoryFunc = c => new ServiceDecorator(new Service(

c.Resolve<ISubServiceProvider();

));

container.AddNewExtension<DecoratorContainerExtension>();

container.RegisterType<IService, ServiceDecorator>();

container.RegisterType<IService, Service>();

DI flavors

IoC with XML configuration...

<unity>

<typeAliases>

<typeAlias alias="string" type="System.String, mscorlib" />

<typeAlias alias="ILogger" type="UnitySamples.ILogger, UnitySamples" />

<typeAlias alias="ConsoleLogger" type="UnitySamples.ConsoleLogger, UnitySamples" />

<typeAlias alias="DebugLogger" type="UnitySamples.DebugLogger, UnitySamples" />

<typeAlias alias="IContext" type="UnitySamples.IContext, UnitySamples" />

<typeAlias alias="UnityContext" type="UnitySamples.UnityContext, UnitySamples" />

<typeAlias alias="CustomerTasks" type="UnitySamples.CustomerTasks, UnitySamples" />

</typeAliases>

<containers>

<container>

<types>

<type type="ILogger" mapTo="ConsoleLogger" name="defaultLogger"/>

<type type="ILogger" mapTo="DebugLogger" name="debugLogger"/>

<type type="IContext" mapTo="UnityContext">

<typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement, Microsoft.Practices

<constructor>

<param name="logger" parameterType="ILogger">

<dependency name="debugLogger"/>

DI flavors

IoC with XML configuration...

<unity>

<typeAliases>

<typeAlias alias="string" type="System.String, mscorlib" />

<typeAlias alias="ILogger" type="UnitySamples.ILogger, UnitySamples" />

<typeAlias alias="ConsoleLogger" type="UnitySamples.ConsoleLogger, UnitySamples" />

<typeAlias alias="DebugLogger" type="UnitySamples.DebugLogger, UnitySamples" />

<typeAlias alias="IContext" type="UnitySamples.IContext, UnitySamples" />

<typeAlias alias="UnityContext" type="UnitySamples.UnityContext, UnitySamples" />

<typeAlias alias="CustomerTasks" type="UnitySamples.CustomerTasks, UnitySamples" />

</typeAliases>

<containers>

<container>

<types>

<type type="ILogger" mapTo="ConsoleLogger" name="defaultLogger"/>

<type type="ILogger" mapTo="DebugLogger" name="debugLogger"/>

<type type="IContext" mapTo="UnityContext">

<typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement, Microsoft.Practices

<constructor>

<param name="logger" parameterType="ILogger">

<dependency name="debugLogger"/>

DI flavors

IoC with XML configuration...

<unity>

<typeAliases>

<typeAlias alias="string" type="System.String, mscorlib" />

<typeAlias alias="ILogger" type="UnitySamples.ILogger, UnitySamples" />

<typeAlias alias="ConsoleLogger" type="UnitySamples.ConsoleLogger, UnitySamples" />

<typeAlias alias="DebugLogger" type="UnitySamples.DebugLogger, UnitySamples" />

<typeAlias alias="IContext" type="UnitySamples.IContext, UnitySamples" />

<typeAlias alias="UnityContext" type="UnitySamples.UnityContext, UnitySamples" />

<typeAlias alias="CustomerTasks" type="UnitySamples.CustomerTasks, UnitySamples" />

</typeAliases>

<containers>

<container>

<types>

<type type="ILogger" mapTo="ConsoleLogger" name="defaultLogger"/>

<type type="ILogger" mapTo="DebugLogger" name="debugLogger"/>

<type type="IContext" mapTo="UnityContext">

<typeConfig extensionType="Microsoft.Practices.Unity.Configuration.TypeInjectionElement, Microsoft.Practices

<constructor>

<param name="logger" parameterType="ILogger">

<dependency name="debugLogger"/>

What problem do we try to solve ?

• Decoupling ?

• Testing ?

• Modular Design ?

• Dependencies management ?

Dependencies are bad....

public class EnrollmentCommandHandler

{

public EnrollmentCommandHandler(StudentRepository studentRepository

ClassRepository classRepository

StudentArchiveRepository studentArchiveRepository

UnitOfWork unitOfWork,

EnrollementNotificationService

ILogger logger,

AuthorizationService authorizationService

CalendarService calendarService

ServiceFoo serviceFoo,

ServiceBlah serviceBlah,

FactoryFoo facoFactoryFoo,

FactoryBlah factoryBlah

)

Cyclic dependencies are evil....

public class EnrollmentCommandHandler

{

public EnrollmentCommandHandler(StudentRepository studentRepository

ClassRepository classRepository

StudentArchiveRepository studentArchiveRepository

UnitOfWork unitOfWork,

EnrollementNotificationService

ILogger logger,

AuthorizationService authorizationService

CalendarService calendarService

ServiceFoo serviceFoo,

ServiceBlah serviceBlah,

FactoryFoo facoFactoryFoo,

FactoryBlah factoryBlah

)

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);}

}

Code we have....

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);

}}

Code that matters…

The rest is Plumbing code

Functional way

int Multiply(int a, int b)

{

return a * b;

}

Partial application

Func<int, int> multiplyBy10 = b => Multiply(10, b);

Functional wayint 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

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…

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…

var studentRepository = new StudentRepository();var classRepository = new ClassRepository();

Action<StudentEnrollCommand> studentEnrollPipeline = c =>handlers.Enroll(studentRepository, classRepository, c);

Bootstrap in one place…

[HttpPost]public void Enroll(EnrollmentRendering r){

_handlers.Dispatch(r.ToCommand());}

Handle in another…

var studentRepository = new StudentRepository();var classRepository = new ClassRepository();

Action<StudentEnrollCommand> studentEnrollPipeline= c =>handlers.Log(c, c1 =>

handlers.Enroll(studentRepository, classRepository, c1));

Want to log ?

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 ?

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 ?

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 !

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

To many dependencies ?

• You have another serious problem

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

Understand your problem before using a tool

References

• Greg Young (8 lines of code InfoQ

http://www.infoq.com/presentations/8-lines-code-refactoring)