Leveraging Dependency Injection(DI) in Universal Applications - Tamir Dresher
- 2. About Me
• Software architect, consultant and instructor
• Software Engineering Lecturer @ Ruppin Academic Center
• Technology addict
• .NET and Native Windows Programming
@tamir_dresher
tamirdr@codevalue.net
http://www.TamirDresher.com.
- 5. How do we get the dependencies
• Instantiating
– I must know the concrete class
– What if that class has dependencies of its own?
• Using a Factory Class
– Now I have a dependency on the Factory
• Using a Singleton
– Now I have a Dependency on the singleton
– Now I must know that it is a singleton
5
- 8. Example – Deeper in the chain
8
public LectureManager()
{
_mdevconService = new MdevconService.MdevconService();
}
- 11. Universal Applications – Sharing is Fun (Sometimes)
11
public void OpenLecture(Lecture lecture)
{
#if WINDOWS_APP
_navigationService.Navigate<LectureInfoPage>(lecture);
#else
_navigationService.Navigate<LecturePage>(lecture);
#endif
}
}
- 13. Dependency Inversion Principle
1. “High level modules should not depend on low level modules.
Both should depend on abstractions”
2. “Abstractions should not depend on details. Details should
depend on abstractions”
( The Dependency Inversion Principle, Robert C. Martin, C++ Report, May 1996 )
13
All the dependency resolving methods we discussed breaks DiP
- 16. Dependency Injection and Inversion Of Control
• Instead of Creating or Resolving my dependencies myself
Inject me those dependencies from outside.
• Three primary techniques
– Construction injection
– Property injection
– Parameter injection
16
- 17. Redesigning to DI
• Before:
17
public LectureManager(IMdevconService mdevconService)
{
_mdevconService = mdevconService;
}
public LectureManager()
{
_mdevconService = new MdevconService.MdevconService();
}
• After:
- 18. Dependency injection/IoC Container
• A class that is responsible to instantiate other classes
• Provide dependencies to created instances
• Manage Instances Lifetime
• Think of it as a big Dictionary from type to instance(s)
18
- 20. Configuring the Container
• Nuget: Install-Package Autofac
• Register Types
20
// Create the builder with which components/services are registered.
var builder = new ContainerBuilder();
// Register types that expose interfaces...
builder.RegisterType<ConsoleLogger>.As<ILogger>();
// Build the container to finalize registrations
// and prepare for object resolution.
var container = builder.Build();
- 22. Platform Specific Code
• Open a File
22
FileOpenPicker openPicker = new FileOpenPicker();
openPicker.ViewMode = PickerViewMode.Thumbnail;
openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
openPicker.FileTypeFilter.Add(".jpg");
await openPicker.PickSingleFileAsync();
- 23. Platform Specific Code – WP8.1
• Open a File
23
FileOpenPicker openPicker = new FileOpenPicker();
openPicker.ViewMode = PickerViewMode.Thumbnail;
openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
openPicker.FileTypeFilter.Add(".jpg");
openPicker.PickSingleFileAndContinue();
- 25. Lifetime Scope
• Per DependencyTransient
• Single instance
25
builder.RegisterType<ConcreteClass>();
// ...is the same as this:
builder.RegisterType<ConcreteClass>().InstancePerDependency();
builder.RegisterType<ConcreteClass>().SingleInstance();
- 26. Lifetime Scope – Per LifetimeScope
26
builder.RegisterType<ConcreteClass>().InstancePerLifetimeScope();
using(var scope1 = container.BeginLifetimeScope())
{
var first = scope1.Resolve<ConcreteClass>();
// same instance as first
var second = scope2.Resolve<ConcreteClass>();//Same as first
}
- 28. Registering Types – Assembly Scanning
28
builder.RegisterAssemblyTypes(typeof(MainPageViewModel).GetTypeInfo().Assembly)
.Where(t => t.Name.EndsWith("ViewModel"))
.AsSelf();
builder.RegisterAssemblyTypes(typeof(ClassInLibrary).GetTypeInfo().Assembly)
.Except<MyUnwantedType>()
.AsImplementedInterfaces();
- 29. Modules
• Separation on Concerns
29
class CommonClassesModule:Module
{
protected override void Load(ContainerBuilder builder)
{
base.Load(builder);
builder.RegisterType<LectureManager>().As<ILectureManager>().SingleInstance();
builder.RegisterType<DummyMdevconService>().As<IMdevconService>().SingleInstance();
}
}
builder.RegisterModule<CommonClassesModule>();
builder.RegisterAssemblyModules(assembly);
- 30. DI Traps – Cyclic Dependencies
30
class ErrorReporter:IErrorReporter
{
public ErrorReporter(IMailClient mailer)
{
}
}
class MailClient : IMailClient
{
public MailClient(IErrorReporter reporter)
{
}
}
- 31. Cyclic Dependencies – Using Lazy
31
class ErrorReporter:IErrorReporter
{
public ErrorReporter(
Lazy<IMailClient> lazyMailer)
{
_lazyMailer=lazyMailer
}
}
class MailClient : IMailClient
{
public MailClient(
Lazy<IErrorReporter> lazyReporter)
{
_lazyReporter=lazyReporter;
}
}
_lazyMailer.Value.Send(…); _lazyReporter.Value.Report(…);
- 32. Cyclic Dependencies – Property Injection
32
class MailClient : IMailClient
{
public MailClient()
{}
public IErrorReporter ErrorReporter
{
get; set;
}
}
class ErrorReporter:IErrorReporter
{
public ErrorReporter()
{}
public IMailClient MailClient
{
get; set;
}
}
builder.RegisterType<MailClient>()
.As<IMailClient>()
.SingleInstance ()
.PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies);
- 33. Summary
• Follow the OODP
• There is no replacement for good Architecture
• DI is not a Silver Bullet
• Maintainability
• Speed
• Thank You!
33