Comment ajouter une injection de dépendances (DI) à l'aide de Unity MVC 4 dans une classe sans contrôleur
J'utilise donc Unity MVC-4 pour réaliser l'injection de dépendances et cela fonctionne très bien avec mes Controller
classes, mais dès que j'essaye d'injecter dans ma classe non contrôleur, j'obtiens le NullReferenceException
et je peux voir que mes objets injectés ne sont pas initialisés par le framework . Je vais vous donner les classes correspondantes que j'utilise:
Controller
classe (DI fonctionne):
public class HomeController : Controller
{
IMyService _myService;
#region CTOR
public HomeController(IMyService myService)
{
_myService = myService;
}
#endregion
public string GetMyString()
{
string mystring=string.Empty;
try
{
mystring = _myService.GetMyStringFromDLL();
}
catch (Exception ex)
{
StringBuilder str = new StringBuilder();
str.AppendLine("Exception in method GetMyString, Error msg: " + ex.Message);
WriteLog(sb);
}
return mystring;
}
}
Et si je fais la même chose dans une méthode non contrôleur (DI ne fonctionne pas ici), j'obtiens un NullReferenceException
:
public inteface IMyLogic
{
string GetMyString();
}
public class MyLogic: IMyLogic
{
IMyService _myService;
#region CTOR
public MyLogic(IMyService myService)
{
_myService = myService;
}
#endregion
public string GetMyString()
{
string mystring=string.Empty;
try
{
mystring = _myService.GetMyStringFromDLL(); //Getting NullReferenceException here
}
catch (Exception ex)
{
StringBuilder str = new StringBuilder();
str.AppendLine("Exception in method GetMyString, Error msg: " + ex.Message);
WriteLog(sb);
}
return mystring;
}
}
Ma BootStrapper.cs
classe ressemble à:
public static class Bootstrapper
{
public static IUnityContainer Initialise()
{
var container = BuildUnityContainer();
container.RegisterType<IMyService , MyService>();
container.RegisterType<IMyLogic, MyLogic>(new HierarchicalLifetimeManager());
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
return container;
}
private static IUnityContainer BuildUnityContainer()
{
var container = new UnityContainer();
RegisterTypes(container);
return container;
}
public static void RegisterTypes(IUnityContainer container)
{
}
}
Si vous voyez ci-dessus dans la ligne container.RegisterType<IMyService , MyService>();
, l'interface et sa mise en œuvre concrète se trouvent dans un module séparé.
Et mon Global.asax.cs
est:
protected void Application_Start()
{
Bootstrapper.Initialise();
AreaRegistration.RegisterAllAreas();
GlobalFilters.Filters.Add(new OfflineActionFilter());
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
Comment puis-je injecter le IMyService
en MyLogic
classe?
Réponses
Utilisez l'attribut [InjectionConstructor] pour indiquer à l'Unity que la MyLogic
classe dépend d'un objet qui doit être injecté dans le constructeur:
[InjectionConstructor]
public MyLogic(IMyService myService)
{
_myService = myService;
}
En fait, il [InjectionConstructor]
est recommandé d'utiliser lorsque la classe injectée contient plusieurs constructeurs. Par conséquent, l'attribut est utilisé pour résoudre la désambiguïsation. C'était juste mon hypothèse pour laquelle le Unity
ne peut pas résoudre le type requis, car le code de la question ne contient pas toutes les parties du code. Mais dans le code de test ci-dessous, l' [InjectionConstructor]
attribut n'est pas nécessaire, car un seul constructeur est déclaré.
Voici le code de test.
La IMyService
définition de l'interface:
public interface IMyService
{
string GetMyStringFromDLL();
}
La ILogic
définition de l'interface:
public interface IMyLogic
{
string GetMyString();
}
La MyLogic
mise en œuvre:
public class MyLogic : IMyLogic
{
IMyService _myService;
public MyLogic(IMyService myService)
{
_myService = myService;
}
public string GetMyString()
{
var mystring = string.Empty;
try
{
mystring = "MyLogic.GetMyString() -> " + _myService.GetMyStringFromDLL();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("Exception in method MyLogic.GetMyString(): " + ex.Message);
}
return mystring;
}
}
La MyService
mise en œuvre:
public class MyService : IMyService
{
public string GetMyStringFromDLL()
{
return "MyService.GetMyStringFromDLL() is called.";
}
}
L' Bootstrapper
initialisation:
public static class Bootstrapper
{
public static IUnityContainer Initialise()
{
var container = new UnityContainer();
container.RegisterType<IMyService, MyService>();
container.RegisterType<IMyLogic, MyLogic>(new HierarchicalLifetimeManager());
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
return container;
}
}
L' Home
implémentation du contrôleur:
public class HomeController : Controller
{
private readonly IMyService _myService;
private readonly IMyLogic _myLogic;
#region CTOR
public HomeController(IMyService myService, IMyLogic myLogic)
{
_myService = myService;
_myLogic = myLogic;
}
#endregion
public ActionResult Index()
{
// Obtaining string directly from the IMyService
var sService = _myService.GetMyStringFromDLL();
// Obtaining string through the IMyLogic
var sLogic = _myLogic.GetMyString();
return View(new List<string>() { sService, sLogic} );
}
}
Et enfin, lorsque la méthode d'action par défaut du Home
contrôleur est exécutée, les deux lignes suivantes s'affichent:
MyService.GetMyStringFromDLL() is called.
MyLogic.GetMyString() -> MyService.GetMyStringFromDLL() is called.
Je n'ai pas travaillé avec Unity depuis la dernière fois que j'ai travaillé sur NopCommerce V1.90, mais je me souviens que tout ce que vous enregistrez dans votre conteneur revient si vous utilisez Resolve sur une instance implémentant IUnityResolver.
Donc, fondamentalement, vous avez enregistré IMyService, mais vous devez également enregistrer IMyLogic - alors au lieu de faire "var logic = new MyLogic ();", vous feriez "var logic = resolver.Resolve (typeof (IMyLogic)); " puis vos paramètres injectés seront résolus en fonction de l'injecteur de dépendances (ou vous obtiendriez les erreurs appropriées si elles étaient manquantes).