V. Unity en action▲
Afin d'aller un peu plus loin dans les exemples, voici une version plus complète de la solution de démo. Télécharger.
Elle contient l'application Console, une application ASP.Net, des services IIS dans l'application ASP.Net et une application ASP.Net MVC3 Razor.
V-A. Utilisation dans une application ASP.Net▲
L'utilisation de l'injection de dépendances dans une application ASP.Net est assez limitée. Elle est pratiquée uniquement pour les appels vers les couches inférieures de l'application. Ainsi, même si à chaque requête une instance de la page est créée, c'est toujours le même objet Application qui est utilisé par IIS. Il suffit donc de créer une classe d'application globale (global.asax) et de se brancher sur le Application_Start comme ceci :
public
class
Global :
HttpApplication
{
protected
void
Application_Start
(
object
sender,
EventArgs e)
{
var
container =
new
UnityContainer
(
).
LoadConfiguration
(
);
ServiceLocator.
SetLocatorProvider
((
) =>
new
UnityServiceLocator
(
container));
}
}
Le tour est joué ! On peut désormais utiliser le ServiceLocator pour résoudre la couche business et obtenir la liste de ses entités :
public
partial
class
Default :
Page
{
protected
override
void
OnLoad
(
EventArgs e)
{
if
(!
Page.
IsPostBack)
{
var
businessLayer =
ServiceLocator.
Current.
GetInstance<
IBusinessLogic>(
);
var
movies =
businessLayer.
GetMovies
(
);
grdMovies.
DataSource =
movies;
grdMovies.
DataBind
(
);
}
base
.
OnLoad
(
e);
}
}
On notera cependant que l'instanciation de la page n'est pas faite par le container d'injection de dépendances. On ne peut donc pas injecter des dépendances sur le constructeur par exemple.
V-B. Utilisation dans une application WCF hébergée par IIS▲
Lorsque les services WCF sont hébergés dans une application ASP.Net, le principe est identique! L'amélioration que l'on peut apporter, c'est de résoudre dynamiquement le type du service à l'aide de l'interface. Pour ceci, à l'application de démo ASP.Net, il faut ajouter les points d'entrée .svc, et une factory. Cette factory permet de résoudre l'implémentation du service suivant l'interface passée. Lorsque le point d'entrée .svc est sollicité
public
class
UnityServiceFactory :
ServiceHostFactory
{
protected
override
ServiceHost CreateServiceHost
(
Type serviceType,
Uri[]
baseAddresses)
{
var
singleton =
ServiceLocator.
Current.
GetInstance
(
serviceType);
return
new
ServiceHost
(
singleton,
baseAddresses);
}
}
Cependant, cette factory exige quelques prérequis sur l'implémentation du service. D'une part, pour se servir du constructeur ServiceHost(object, Uri[]), il est nécessaire de préciser à WCF que ce service est un singleton. Ceci se fait en décorant la classe avec un attribut ServiceBehavior comme ceci :
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public
class
MovieBaseService :
IMovieBaseService
{
private
readonly
IBusinessLogic _businessLogicLayer;
public
MovieBaseService
(
IBusinessLogic businessLogicLayer)
{
_businessLogicLayer =
businessLogicLayer;
}
public
List<
Movie>
GetMovies
(
)
{
return
_businessLogicLayer.
GetMovies
(
);
}
/* ... Suite de l'implémentation ... */
}
D'une autre part, il faut préciser dans les points d'entrée .svc l'utilisation de la factory et mettre l'interface comme type du service :
<%
@ ServiceHost Language=
"C#"
Service=
"MovieBase.Services.Interfaces.IMovieBaseService"
Factory=
"MovieBase.Presentation.AspNet.Services.UnityServiceFactory"
%>
Le reste de la configuration WCF est identique (déclaration des services dans le Web.Config, etc.).
V-C. Utilisation dans une application ASP.Net MVC3▲
ASP.Net MVC3 ayant été notamment conçu pour les tests unitaires, il permet une plus grande flexibilité. Contrairement à ASP.Net classique, l'instanciation d'un Controller ou d'une ViewPage peut se faire à l'aide d'un conteneur d'injection de dépendances. On peut donc avoir une injection de dépendances sur le constructeur du Controller. La configuration est analogue à ASP.Net, la majeure partie se fait dans le Global.asax :
public
class
MvcApplication :
System.
Web.
HttpApplication
{
/* ... diverses fonctions générées automatiquement ... */
protected
void
Application_Start
(
)
{
AreaRegistration.
RegisterAllAreas
(
);
RegisterGlobalFilters
(
GlobalFilters.
Filters);
RegisterRoutes
(
RouteTable.
Routes);
var
container =
new
UnityContainer
(
).
LoadConfiguration
(
);
ServiceLocator.
SetLocatorProvider
((
) =>
new
UnityServiceLocator
(
container));
container.
RegisterType<
IControllerActivator,
UnityControllerActivator>(
);
DependencyResolver.
SetResolver
(
type =>
{
if
(
container.
IsRegistered
(
type))
return
container.
Resolve
(
type);
else
return
null
;
},
type =>
{
if
(
container.
IsRegistered
(
type))
return
container.
ResolveAll
(
type);
else
return
new
HashSet<
object
>(
);
}
);
}
}
On peut noter plusieurs choses sur cet exemple. D'abord, on enregistre dans le container une instance implémentant IControllerActivor. C'est notre factory de Controller : en fonction du contexte, on décide de l'implémentation d'un Controller à instancier. Enfin, il y a la configuration du DependencyResolver : c'est une façade utilisée en interne par ASP.Net MVC3 pour la résolution de types. Identiquement au ServiceLocator, on utilise notre Container Unity pour résoudre les types. On va se servir de ce DependencyResolver dans notre UnityControllerActivator (notre implémentation de IControllerActivator).
public
class
UnityControllerActivator :
IControllerActivator
{
public
IController Create
(
RequestContext requestContext,
Type controllerType)
{
return
DependencyResolver.
Current.
GetService
(
controllerType) as
IController;
}
}
On peut également créer une implémentation de IViewPageActivator : ce serait lui qui déciderait du fichier .cshtml à instancier. Cependant c'est beaucoup plus délicat de créer une classe à partir d'un fichier .cshtml.
Désormais, on peut procéder de manière classique (il ne faut pas oublier de déclarer notre Controller dans les enregistrements Unity) :
public
class
MoviesController :
Controller
{
private
readonly
IBusinessLogic _businessLogicLayer;
public
MoviesController
(
IBusinessLogic businessLogicLayer)
{
_businessLogicLayer =
businessLogicLayer;
}
//
// GET: /Movies/
public
ActionResult Index
(
)
{
return
View
(
_businessLogicLayer.
GetMovies
(
));
}
/* ... Suite ... */
}
V-D. Utilisation dans une application Silverlight/WPF▲
Entre Silverlight et WPF, la différence est assez fine. Dans une application WPF, toutes les couches peuvent faire partie du même tiers. Dans une application Silverlight, c'est un peu plus complexe : au moins deux tiers doivent être mis en jeu avec une communication par webservice, Silverlight ne pouvant pas accéder à une base de données directement. Par conséquent, l'injection de dépendance sur une application Silverlight ne concerne que les couches de présentation. Cet exemple mérite qu'on s'y penche d'un peu plus près : il sera donc traité dans un article indépendant.
V-E. Une couche d'accès aux données Entity Framework avec Unity▲
Bigre! C'est bien trop complexe pour tenir dans cette section ! Cette partie fera l'objet d'un article complet ultérieurement !
De la littérature sur la toile peut néanmoins être consultée en utilisant le mot clef : UnitOfWork