2 votes

Utiliser l'injection de dépendances (Autofac) et éviter le modèle Service Locator.

J'essaie d'implémenter correctement l'intégration des données dans mon application Xamarin Android à l'aide d'Autofac, mais j'ai du mal à comprendre comment gérer l'instanciation des objets qui nécessitent des données passées dans leur constructeur. Par exemple, l'un de nos modèles de vue a besoin d'une chaîne et d'un guide passés dans son constructeur. Une solution qui semble prometteuse est la suivante Fonctions déléguées offert par Autofac. C'est là que la frontière entre Service Locator et DI semble s'estomper, du moins dans mon esprit. Afin d'utiliser les fonctions déléguées, vous devez appeler container.Resolve, ou plutôt il est recommandé d'utiliser IComponentContext.Resolve. De nombreux blogs recommandent de ne pas utiliser Resolve en dehors du point d'entrée bootstapper/main. Y a-t-il quelque chose qui m'échappe ici ? Existe-t-il un meilleur moyen de créer des objets à l'aide de DI ? Je connais bien le modèle Factory pour créer des objets, mais j'ai l'impression de perdre les avantages de DI en suivant cette voie, puisque je dois à nouveau passer manuellement des services/objets à l'objet nouvellement créé. Merci pour tout commentaire !

3voto

default.kramer Points 3119

C'est pas recommandé d'appeler container.Resolve() pour utiliser une usine de délégués. La méthode correcte est indiquée sur la page sur les usines de délégués dont vous avez déjà fait le lien :

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));
  }
}

Lorsque la documentation montre un appel explicite à container.Resolve() vous devez comprendre qu'ils ne montrent pas les meilleures pratiques, ils prouvent simplement que le problème peut être résolu sans coder une toute nouvelle classe (comme la classe Portfolio ) pour le consommer.

0voto

Wimmel Points 7641

Afin d'utiliser les fonctions déléguées, vous devez appeler container.Resolve.

Non, du moins pas dans ce cas.

En supposant que vous vous soyez inscrit Shareholding . Maintenant vous pouvez demander une dépendance sur Func<Shareholding> c'est-à-dire quelque chose qui renvoie un Shareholding quand vous l'appelez.

Mais comme le Shareholding a deux paramètres, il ne peut pas être résolu sans fournir ces paramètres. Il suffit de les ajouter à la déclaration comme ceci : Func<string, uint, Shareholding> . Vous pouvez maintenant résoudre le problème de dépendance lorsque vous fournissez ces paramètres.

Voici un meilleur exemple .

0voto

Sam Points 2521

J'ai récemment (hier) rencontré le même problème. J'ai fini par utiliser l'objet ServiceClient que vous voyez dans le code ci-dessous. Cet objet répond à votre question sur l'utilisation du conteneur en dehors du bootstrapper. J'ai lu des arguments qui disent de ne pas faire circuler le conteneur et je pense qu'ils sont pour la plupart valables. Dans mon cas, cependant, la classe ServiceClient représente un point d'entrée unique dans ma couche de services et j'ai donc pensé qu'il était approprié de passer le conteneur.

La façon dont je l'utilise actuellement est de passer une instance de ServiceClient dans mon BaseController :

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

Contrôleur de base :

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

    public IServiceClient ServiceClient { get; set; }

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

Dans mon contrôleur, je peux résoudre, instancier et appeler un service qui utilise des ressources non gérées avec une seule ligne comme celle-ci :

myViewModel = await ServiceClient.OfType<ICustomerService>().TryAsync(x => x.GetCustomerByID(id));

ServiceClient :

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);
        }
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X