Upload
thomas-jaskula
View
820
Download
1
Tags:
Embed Size (px)
Citation preview
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}
}
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);
[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}
}
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
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…
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 take away
• Be stupid! Don’t waste your brain on complicated code
• Don’t waste your time to understand Rube Goldberg machines
• Simplicity
• Readability