IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Architecture en couches, découplage et injection de dépendances avec Unity


précédentsommairesuivant

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 :

 
Sélectionnez
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 :

 
Sélectionnez
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é

 
Sélectionnez
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 :

 
Sélectionnez
[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 :

 
Sélectionnez
<%@ 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 :

 
Sélectionnez
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).

 
Sélectionnez
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) :

 
Sélectionnez
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


précédentsommairesuivant

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2011 Nathanael Marchand. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Droits de diffusion permanents accordés à Developpez LLC.