2

我有一个 Presenter,它在其构造函数中将 Service 和 View Contract 作为参数:

public FooPresenter : IFooPresenter {
    private IFooView view;
    private readonly IFooService service;

    public FooPresenter(IFooView view, IFooService service) {
        this.view = view;
        this.service = service;
    }
}

我用 Autofac 解决了我的服务:

private ContainerProvider BuildDependencies() {
    var builder = new ContainerBuilder();
    builder.Register<FooService>().As<IFooService>().FactoryScoped();  

    return new ContainerProvider(builder.Build());  
}

在我的 ASPX 页面(查看实现)中:

public partial class Foo : Page, IFooView {
    private FooPresenter presenter;

    public Foo() {
        // this is straightforward but not really ideal
        // (IoCResolve is a holder for how I hit the container in global.asax)
        this.presenter = new FooPresenter(this, IoCResolve<IFooService>());

        // I would rather have an interface IFooPresenter so I can do
        this.presenter = IoCResolve<IFooPresenter>();
        // this allows me to add more services as needed without having to 
        // come back and manually update this constructor call here
    }
}

问题是 FooPresenter 的构造函数需要特定的页面,而不是容器创建新页面。

我可以为容器提供视图的特定实例(当前页面)以实现此分辨率吗?这样做有意义吗,还是我应该以另一种方式这样做?

4

2 回答 2

2

在 Autofac 中解决依赖关系时,解决传递我喜欢调用的数据参数的方法是使用生成的工厂

(更新:这个问题讨论了同样的问题,我的文章展示了如何避免大量的工厂代表)。

您的问题的解决方案将如下所示:

首先,声明一个只接受数据参数的工厂委托:

public delegate IFooPresenter FooPresenterFactory(IFooView view);

您的演示者保持不变:

public FooPresenter : IFooPresenter {
    private IFooView view;
    private readonly IFooService service;

    public FooPresenter(IFooView view, IFooService service) {
        this.view = view;
        this.service = service;
    }
}

接下来是 Autofac 容器设置:

var builder = new ContainerBuilder();
builder.Register<FooService>().As<IFooService>().FactoryScoped();  
builder.Register<FooPresenter>().As<IFooPresenter>().FactoryScoped();  
builder.RegisterGeneratedFactory<FooPresenterFactory>();

现在在您的页面中,您可以在两行代码中通过首先获取工厂然后调用工厂为您执行解析来解析演示者:

public partial class Foo : Page, IFooView {
    private FooPresenter presenter;

    public Foo() {
        var factory = IoCResolve<FooPresenterFactory>();
        this.presenter = factory(this);
    }
}
于 2009-12-18T07:25:28.190 回答
0

我实际上解决了这个确切的问题并围绕它构建了一个框架。我使用Autofac 参数将现有视图传递给演示者解析调用。

首先,我定义了一个源自 Autofac 的自定义解析接口:

public interface IMvpContext : IContext
{
    T View<T>();
}

这允许我注册一个解决视图的演示者:

builder.RegisterPresenter(c => new FooPresenter(
    c.View<IFooView>(),
    c.Resolve<IFooService>()));

使用将 Autofac 包装IContext在以下实现中的扩展方法IMvpContext

public static IConcreteRegistrar RegisterPresenter<T>(
    this ContainerBuilder builder,
    Func<IMvpContext, T> creator)
{
    return builder
        .Register((context, parameters) => creator(new MvpContext(context, parameters)))
        .FactoryScoped();
}

我定义了一个代表视图参数的参数类型:

public class MvpViewParameter : NamedParameter
{
    public static readonly string ParameterName = typeof(MvpViewParameter).AssemblyQualifiedName;

    public MvpViewParameter(object view) : base(ParameterName, view)
    {}
}

它使用自己的程序集限定类型名称作为参数名称。这与合法参数发生冲突的可能性非常低。

MvpContext将所有标准分辨率调用传递给基本上下文。对于视图,它使用众所周知的名称解析参数:

public sealed class MvpContext : IMvpContext
{
    private IContext _context;
    private IEnumerable<Parameter> _resolutionParameters;

    public MvpContext(IContext context, IEnumerable<Parameter> resolutionParameters)
    {
        _context = context;
        _resolutionParameters = resolutionParameters;
    }

    #region IContext

    // Pass through all calls to _context

    #endregion

    #region IMvpContext

    public T View<T>()
    {
        return _resolutionParameters.Named<T>(MvpViewParameter.ParameterName);
    }
    #endregion
}

解析演示者的调用提供了视图参数:

public partial class Foo : Page, IFooView
{
    private readonly FooPresenter presenter;

    public Foo()
    {
        this.presenter = IoCResolve<IFooPresenter>(new MvpViewParameter(this));
    }
}
于 2009-12-18T06:20:20.153 回答