Leveraging Dependency Injection(DI) in Universal Applications - Tamir Dresher

34
Tamir Dresher Senior Software Architect March 2015 Leveraging Dependency Injection(DI) in Universal Application

Transcript of Leveraging Dependency Injection(DI) in Universal Applications - Tamir Dresher

Tamir Dresher

Senior Software ArchitectMarch 2015

Leveraging Dependency Injection(DI) in Universal Application

About Me

• Software architect, consultant and instructor

• Software Engineering Lecturer @ Ruppin Academic Center

• Technology addict

• .NET and Native Windows Programming

@[email protected]://www.TamirDresher.com.

Where? Everywhere!

3

Class Dependencies

• Class can depend on other classes

– Services

– Entities

– Components

4

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

Demo

Universal Application

6

Example

7

public MainPageViewModel(){

_lectureManager = new LectureManager();var rootFrame = Window.Current.Content as Frame;_navigationService = new NavigationService(rootFrame);Lectures = new ObservableCollection<LectureDay>();

}

Example – Deeper in the chain

8

public LectureManager(){

_mdevconService = new MdevconService.MdevconService();}

Universal Applications

• Sharing Code is fun

• Sharing Code saves time

• Sharing Code eliminate bugs – NOT!

9

Universal Applications – Sharing is Fun

10

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}

}

Universal Applications - Issues

• Sharing code between projects

• Using non portable libraries

12

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

We Broke DiP

14

MainPageViewModel

LectureManager

MdevconService

High

Low

Redesigning with DiP

15

MainPageViewModel

LectureManager

MdevconService

High

Low

IMdevconService

ILectureManager

Dependency Injection and Inversion Of Control

• Instead of Creating or Resolving my dependencies myselfInject me those dependencies from outside.

• Three primary techniques

– Construction injection

– Property injection

– Parameter injection

16

Redesigning to DI

• Before:

17

public LectureManager(IMdevconService mdevconService){

_mdevconService = mdevconService;}

public LectureManager(){

_mdevconService = new MdevconService.MdevconService();}

• After:

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

Known Containers\Kernels

• Autofac

• Ninject

• Unity

• MEF

• StractureMap

• Windsor

19

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

Demo

Configuring the IoC Container

21

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

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

Platform Specific Classes

24

builder.RegisterType<WinFilePickerService>().As<IFilePickerService>().SingleInstance();

builder.RegisterType<WPFilePickerService>().As<IFilePickerService>().SingleInstance();

Lifetime Scope

• Per Dependency\Transient

• Single instance

25

builder.RegisterType<ConcreteClass>();

// ...is the same as this:builder.RegisterType<ConcreteClass>().InstancePerDependency();

builder.RegisterType<ConcreteClass>().SingleInstance();

Lifetime Scope – Per LifetimeScope

26

builder.RegisterType<ConcreteClass>().InstancePerLifetimeScope();

using(var scope1 = container.BeginLifetimeScope()){ var first = scope1.Resolve<ConcreteClass>();

// same instance as firstvar second = scope2.Resolve<ConcreteClass>();//Same as first

}

Registering Types

27

builder.RegisterType<DebugLogger>.As<ILogger>();

builder.RegisterInstance(new NavigationService(rootFrame)).As<INavigationService>();

builder.RegisterType<FirstViewModel>().AsSelf();

builder.RegisterType<FirstViewModel>().AsSelf();builder.RegisterType<SecondViewModel>().AsSelf();builder.RegisterType<ThirdViewModel>().AsSelf();::builder.RegisterType<LastViewModel>().AsSelf();

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

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

DI Traps – Cyclic Dependencies

30

class ErrorReporter:IErrorReporter{

public ErrorReporter(IMailClient mailer){

}}

class MailClient : IMailClient{

public MailClient(IErrorReporter reporter){

}}

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

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

Summary

• Follow the OODP

• There is no replacement for good Architecture

• DI is not a Silver Bullet

• Maintainability

• Speed

• Thank You!

33

Presenter contact details

t: @tamir_dresher

e: [email protected]

b: TamirDresher.com

w: www.codevalue.net