Asp.net MVC4 Web Api et Unity

 

Je suis en train de migrer un certain nombre de service pour utiliser Web Api, j’utilise au quotidien Unity comme container pour tous mes services. Voici comment utiliser un Inverseur de controle comme Unity avec Asp.Net MVC4 :

image

Dans on premier temps il faut sélectionner un projet de type Web Api

image

Puis ajouter Unity en utilisant NuGet, la dernière version est la 2.1

l’ApiControlleur  fourni par défaut avec le nouveau projet est le suivant :

   1:      public class ValuesController : ApiController
   2:      {
   3:          // GET /api/values
   4:          public IEnumerable<string> Get()
   5:          {
   6:              return new string[] { "value1", "value2" };
   7:          }
   8:   
   9:          // GET /api/values/5
  10:          public string Get(int id)
  11:          {
  12:              return "value";
  13:          }
  14:   
  15:          // POST /api/values
  16:          public void Post(string value)
  17:          {
  18:          }
  19:   
  20:          // PUT /api/values/5
  21:          public void Put(int id, string value)
  22:          {
  23:          }
  24:   
  25:          // DELETE /api/values/5
  26:          public void Delete(int id)
  27:          {
  28:          }
  29:      }

Nous ajoutons un constructeur dans lequel nous allons passer le service comme ceci :


   1:      public class ValuesController : ApiController
   2:      {
   3:          public ValuesController(Services.IValueService valueService)
   4:          {
   5:              ValueService = valueService;
   6:          }
   7:   
   8:          protected Services.IValueService ValueService { get; private set; }
   9:   
  10:          // GET /api/values
  11:          public IEnumerable<string> Get()
  12:          {
  13:              return ValueService.GetValues();
  14:          }
  15:   
  16:          // GET /api/values/5
  17:          public string Get(int id)
  18:          {
  19:              return ValueService.GetValueById(id);
  20:          }
  21:   
  22:          // POST /api/values
  23:          public void Post(string value)
  24:          {
  25:              ValueService.Save(value);
  26:          }
  27:   
  28:          // PUT /api/values/5
  29:          public void Put(int id, string value)
  30:          {
  31:              var v = new KeyValuePair<int, string>(id, value);
  32:              ValueService.Save(v);
  33:          }
  34:   
  35:          // DELETE /api/values/5
  36:          public void Delete(int id)
  37:          {
  38:              ValueService.Delete(id);
  39:          }
  40:      }

Voici le détail du service (simpliste) :


   1:      public class ValueService : IValueService
   2:      {
   3:          private Dictionary<int, string> m_Values;
   4:   
   5:          public ValueService()
   6:          {
   7:              m_Values = new Dictionary<int, string>()
   8:              {
   9:                  { 1 , "value1" }, 
  10:                  { 2 , "value2" },
  11:              };
  12:          }
  13:   
  14:          public IEnumerable<string> GetValues()
  15:          {
  16:              return m_Values.Select(i => i.Value);
  17:          }
  18:   
  19:          public string GetValueById(int Id)
  20:          {
  21:              var value = m_Values.SingleOrDefault(i => i.Key == Id);
  22:               if (value.Key != null)
  23:              {
  24:                  return value.Value;
  25:              }
  26:              return null;
  27:          }
  28:   
  29:          public void Save(string value)
  30:          {
  31:              m_Values.Add(m_Values.Count + 1, value);
  32:          }
  33:   
  34:          public void Save(KeyValuePair<int, string> v)
  35:          {
  36:              m_Values.Add(v.Key, v.Value);
  37:          }
  38:   
  39:          public void Delete(int id)
  40:          {
  41:              m_Values.Remove(id);
  42:          }
  43:      }

Maintenant si nous appelons le service directement voici le résultat :


image


<ExceptionType>System.InvalidOperationException</ExceptionType>

<Message>

An error occurred when trying to create a controller of type 'UnityMvcApplication.Controllers.ValuesController'. Make sure that the controller has a parameterless public constructor.

</Message>

<StackTrace>

Nous allons donc faire de telle sorte que Unity vienne injecter le service attendu dans le constructeur, pour cela il faut configurer unity dans la methode app_start de l’application comme ceci :

   1:          protected void Application_Start()
   2:          {
   3:              AreaRegistration.RegisterAllAreas();
   4:   
   5:              RegisterGlobalFilters(GlobalFilters.Filters);
   6:              RegisterRoutes(RouteTable.Routes);
   7:   
   8:              var container = new Microsoft.Practices.Unity.UnityContainer();
   9:   
  10:              // Mapping des services pour Web Api
  11:              container.RegisterInstance<System.Web.Http.HttpConfiguration>(System.Web.Http.GlobalConfiguration.Configuration);
  12:              container.RegisterType<System.Web.Http.Dispatcher.IHttpControllerFactory, System.Web.Http.Dispatcher.DefaultHttpControllerFactory>();
  13:              container.RegisterType<System.Web.Http.Dispatcher.IHttpControllerActivator, System.Web.Http.Dispatcher.DefaultHttpControllerActivator>();
  14:              container.RegisterType<System.Web.Http.Common.ILogger, Services.ServiceLogger>();
  15:              container.RegisterType<System.Web.Http.Controllers.IHttpActionSelector, System.Web.Http.Controllers.ApiControllerActionSelector>();
  16:              container.RegisterType<System.Web.Http.Controllers.IHttpActionInvoker, System.Web.Http.Controllers.ApiControllerActionInvoker>();
  17:              container.RegisterType<System.Web.Http.Controllers.IActionValueBinder, System.Web.Http.ModelBinding.DefaultActionValueBinder>();
  18:              container.RegisterType<System.Web.Http.Metadata.ModelMetadataProvider, System.Web.Http.Metadata.Providers.CachedDataAnnotationsModelMetadataProvider>();
  19:              container.RegisterType<System.Net.Http.Formatting.IFormatterSelector, System.Net.Http.Formatting.FormatterSelector>();
  20:              System.Web.Http.GlobalConfiguration.Configuration.ServiceResolver.SetResolver(
  21:                  (Type serviceType) =>
  22:                  {
  23:                      return container.Resolve(serviceType);
  24:                  },
  25:                  (Type serviceType) =>
  26:                  {
  27:                      return container.ResolveAll(serviceType);
  28:                  });
  29:   
  30:              // Mapping du service des valeurs pour la demo
  31:              container.RegisterType<Services.IValueService, Services.ValueService>();
  32:   
  33:              BundleTable.Bundles.RegisterTemplateBundles();
  34:          }

lors du même appel Web Api le résultat est le suivant, le service à bien été injecté dans le constructeur  :


image


Explications, le service Web Api pour fonctionner avec Unity à besoin qu’un certain nombre de services du framework soient déclarés :

			container.RegisterType<System.Web.Http.Dispatcher.IHttpControllerFactory, System.Web.Http.Dispatcher.DefaultHttpControllerFactory>();
container.RegisterType<System.Web.Http.Dispatcher.IHttpControllerActivator, System.Web.Http.Dispatcher.DefaultHttpControllerActivator>();
container.RegisterType<System.Web.Http.Common.ILogger, Services.ServiceLogger>();
container.RegisterType<System.Web.Http.Controllers.IHttpActionSelector, System.Web.Http.Controllers.ApiControllerActionSelector>();
container.RegisterType<System.Web.Http.Controllers.IHttpActionInvoker, System.Web.Http.Controllers.ApiControllerActionInvoker>();
container.RegisterType<System.Web.Http.Controllers.IActionValueBinder, System.Web.Http.ModelBinding.DefaultActionValueBinder>();
container.RegisterType<System.Web.Http.Metadata.ModelMetadataProvider, System.Web.Http.Metadata.Providers.CachedDataAnnotationsModelMetadataProvider>();
container.RegisterType<System.Net.Http.Formatting.IFormatterSelector, System.Net.Http.Formatting.FormatterSelector>();

 

J’ai trouvé pour chacun une implémentation concrète sauf pour ILogger, si quelqu’un sait ou se trouve une implémentation dans le framework, pour l’instant j’ai du créer une classe qui implémente l’interface.

une fois tous les service déclarés “mappé” avec Unity il suffit d’indiquer un resolver pour Web Api, ce mecanisme avait déjà été initié avec MVC3 et son DependencyResolver.

			System.Web.Http.GlobalConfiguration.Configuration.ServiceResolver.SetResolver(
(Type serviceType) =>
{
return container.Resolve(serviceType);
},
(Type serviceType) =>
{
return container.ResolveAll(serviceType);
});

la c’est très bien fait, il n’y a que 2 methodes à declarer comme pour le DependencyResolver , Resolve et ResolveAll, a la fin on oublie pas de déclarer également le service des valeurs :

			container.RegisterType<Services.IValueService, Services.ValueService>();
Ci dessous le code source de cet exemple :
 

Aucun commentaire: