2016-04-14 5 views
2

Ich erlebe einen Stich bei der korrekten Implementierung von DI in meiner Xamarin Android-Anwendung mit Autofac, aber ich habe Probleme zu verstehen, wie ich Objekte instanziieren sollte, für die Daten benötigt werden Konstrukteur. Zum Beispiel benötigt eines unserer Ansichtsmodelle eine Zeichenkette und eine Guid, die an seinen Konstruktor übergeben wird. Etwas, das vielversprechend aussieht, ist Delegate Functions, das von Autofac angeboten wird. Hier scheint die Grenze zwischen Service Locator und DI zu verschwimmen, zumindest in meinen Gedanken. Um die Delegate-Funktionen zu verwenden, müssen Sie container.Resolve aufrufen, oder besser, es wird empfohlen, IComponentContext.Resolve zu verwenden. Viele Blogs empfehlen, Resolve nicht außerhalb des Bootstappers/Haupteinstiegs zu verwenden. Gibt es etwas, das mir hier fehlt? Gibt es eine bessere Möglichkeit, Objekte mit DI zu erstellen? Ich bin vertraut mit dem Factory-Muster, um Objekte zu erstellen, aber ich habe das Gefühl, dass ich die Vorteile von DI verliere, da ich zurück bin, Dienste/Objekte manuell an das neu erstellte Objekt weiterzuleiten. Danke für jede Rückmeldung!Verwenden von Abhängigkeitsinjektion (Autofac) und Vermeiden des Service Locator-Musters

+0

Ist das Viewmodel via Injektion instanziiert werden oder versuchen, yo, um herauszufinden, wie eine Instanz der Viewmodel instanziiert, während die Ansichtsmodell zu haben? –

+0

Letzteres ist, was ich versuche zu tun. Konstruktorparameterwerte für bestimmte Ansichtsmodelle sind bis zur Laufzeit nicht bekannt. – NastyNate

+0

"Konstruktor Parameter Werte für bestimmte Viewmodels sind nicht bekannt bis zur Laufzeit". Dies bedeutet, dass Sie Laufzeitwerte in Ihre Komponenten injizieren, was [ein Anti-Pattern] ist (https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=99). – Steven

Antwort

3

Es ist nicht empfohlen, container.Resolve() anrufen, um eine Delegate Factory zu verwenden. Der richtige Weg ist auf the delegate factories page that you already linked to gezeigt:

public class Portfolio 
{ 
    Shareholding.Factory ShareholdingFactory { get; set; } 
    IList<Shareholding> _holdings = new List<Shareholding>(); 

    public Portfolio(Shareholding.Factory shareholdingFactory) 
    { 
    ShareholdingFactory = shareholdingFactory; 
    } 

    public void Add(string symbol, uint holding) 
    { 
    _holdings.Add(ShareholdingFactory(symbol, holding)); 
    } 
} 

Wenn die docs einen expliziten Aufruf an container.Resolve() zeigen sollten Sie erkennen, dass sie nicht am besten Praxis zeigt, sie erweisen sich einfach, dass es ohne Codierung auf eine ganz neue aufgelöst werden kann Klasse (wie Portfolio), um es zu konsumieren.

+0

Vielen Dank für Ihre Antwort, jetzt fühle ich mich ein bisschen dumm, dass ich das verpasst habe. – NastyNate

0

Um die Delegierten Funktionen zu verwenden, müssen Sie container.Resolve rufen

Nein, zumindest nicht in diesem Fall.

Angenommen, Sie haben sich registriert Shareholding. Jetzt können Sie eine Abhängigkeit von Func<Shareholding>, dh fragen. etwas Hut bringt einen Shareholding zurück, wenn Sie es nennen.

Aber da der Shareholding Konstruktor zwei Parameter hat, kann er nicht aufgelöst werden, ohne diese Parameter zu liefern. Fügen Sie sie einfach der Deklaration hinzu: Func<string, uint, Shareholding>. Jetzt können Sie die Abhängigkeit lösen, wenn Sie diese Parameter angeben.

Hier ist ein better example.

+1

Vielen Dank für Ihre Antwort. Während es relevant und hilfreich ist, wies default.kramer auf etwas hin, das ich in der Dokumentation, die meine Frage beantwortet hatte, verpasst hatte, also wählte ich seine Antwort. – NastyNate

0

Ich hatte vor kurzem (gestern) das gleiche Problem, das ich mit dem ServiceClient-Objekt, das Sie im folgenden Code sehen, beendete. Dieses Objekt behandelt Ihre Frage zur Verwendung des Containers außerhalb des Bootstrappers. Ich habe Argumente gelesen, die sagen, dass ich den Container nicht weitergeben muss, und ich denke, dass sie meistens gültig sind. In meinem Fall jedoch stellt die ServiceClient-Klasse einen einzigen Einstiegspunkt in meine Service-Schicht dar, so dass ich es für angemessen hielt, den Container zu übergeben.

So wie ich das im Moment verwenden ist eine Instanz von ServiceClient in meine Baseweitergeben müssen:

// In Global.asax.cs 
builder.RegisterControllers(typeof(MvcApplication).Assembly); 
builder.RegisterType<ServiceClient>().As<IServiceClient>(); 

Base:

public abstract class BaseController<T> : Controller where T :class 
{ 

    public IServiceClient ServiceClient { get; set; } 

    public BaseController(IServiceClient serviceClient) 
    { 
     ServiceClient = serviceClient; 
    } 
} 

In meinem Controller kann ich lösen, instanziiert und Anruf Ein Dienst, der nicht verwaltete Ressourcen mit nur einer Zeile wie dieser verwendet:

ServiceClient: accept injiziert Parameter

public class ServiceClient : IServiceClient 
{ 
    private IComponentContext _container; 

    public ServiceClient(IComponentContext container) 
    { 
     _container = container; 
    } 

    public ServiceCallWrapper<T> OfType<T>() where T : class, IDisposable 
    { 
     return new ServiceCallWrapper<T>(_container); 
    } 
} 

public class ServiceCallWrapper<T> : IServiceCallWrapper<T> where T : class, IDisposable 
{ 
    private IComponentContext _container; 

    internal ServiceCallWrapper(IComponentContext container) 
    { 
     _container = container; 
    } 

    public void Try(Action<T> method) 
    { 
     // consider try/catch/log/throw here 
     using (T client = _container.Resolve<T>()) 
     { 
      method(client); 
     } 
    } 

    public TResult Try<TResult>(Func<T, TResult> method) 
    { 
     using (T client = _container.Resolve<T>()) 
     { 
      return method(client); 
     } 
    } 

    public async Task TryAsync(Func<T, Task> method) 
    { 
     using (T client = _container.Resolve<T>()) 
     { 
      await method(client); 
     } 
    } 

    public async Task<TResult> TryAsync<TResult>(Func<T, Task<TResult>> method) 
    { 
     using (T client = _container.Resolve<T>()) 
     { 
      return await method(client); 
     } 
    } 
} 
Verwandte Themen