使用依赖注入 (Autofac) 并避免服务定位器模式

Using Dependency Injection (Autofac) and avoiding Service Locator pattern

我正在尝试使用 Autofac 在我的 Xamarin Android 应用程序中正确实现 DI,但我在理解如何处理需要将数据传递到其构造函数的实例化对象时遇到了问题。例如,我们的一个视图模型需要一个字符串和一个 guid 传递给它的构造函数。 Autofac 提供的 Delegate Functions 看起来很有前途。这就是服务定位器和 DI 之间的界限似乎模糊的地方,至少在我看来是这样。为了使用委托函数,您必须调用 container.Resolve,或者更确切地说,建议使用 IComponentContext.Resolve。许多博客建议不要在 bootstapper/main 入口点之外使用 Resolve。我在这里缺少什么吗?有没有更好的方法来使用 DI 创建对象?我熟悉工厂模式来创建对象,但我觉得我正在失去 DI 走这条路的好处,因为我又回到手动将 services/objects 传递给新创建的对象。感谢您的任何反馈!

建议调用container.Resolve()使用委托工厂。正确的方法显示在 the delegate factories page that you already linked to:

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

当文档显示对 container.Resolve() 的显式调用时,您应该意识到它们并没有显示最佳实践,它们只是证明无需编写全新的 class(喜欢 Portfolio) 来消费它。

In order to use the Delegate Functions you must call container.Resolve

不,至少在这种情况下不是。

假设您已经注册 Shareholding。现在你可以询问对 Func<Shareholding> 的依赖,即。当你调用它时,它的帽子 returns 和 Shareholding

但是由于 Shareholding 构造函数有两个参数,如果不提供这些参数就无法解析。只需像这样将它们添加到声明中:Func<string, uint, Shareholding>。现在您可以在提供这些参数时解决依赖关系。

这里是better example.

我最近(昨天)遇到了同样的问题,我在使用您在下面的代码中看到的 ServiceClient 对象时遇到了同样的问题。该对象解决了您关于在引导程序之外使用容器的问题。我读过关于不要传递容器的论点,我认为它们大多是有效的。然而,在我的例子中,ServiceClient class 代表进入我的服务层的单个入口点,所以我认为传递容器是合适的。

我目前使用它的方式是将 ServiceClient 的实例传递到我的 BaseController 中:

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

基地控制器:

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

    public IServiceClient ServiceClient { get; set; }

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

在我的控制器中,我可以解析、实例化和调用使用非托管资源的服务,只需一行代码,如下所示:

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

服务客户端:

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