Pregunta

Tengo un Presentador que tiene un Servicio y una Vista Contrato como parámetro en su constructor:

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

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

Puedo resolver mi servicio con Autofac:

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

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

En mi página ASPX (la implementación de la Vista):

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

El problema es FooPresenter del constructor espera que la Página específica, no para el contenedor para crear una nueva.

Puedo proporcionar una instancia específica de la vista, la página actual, el contenedor solo para esta resolución?¿Que sentido hacer, o que debo hacer esto de otra manera?

¿Fue útil?

Solución

La forma de resolver pasando lo que me gusta llamar a datos parámetros hora de resolver las dependencias en Autofac es mediante el uso de fábricas generados .

(Actualización: esta pregunta discute el mismo problema y mi artículo muestra cómo se pueden evitar grandes cantidades de delegados de fábrica).

La solución a su problema va a ser algo como esto:

En primer lugar, declarar un delegado de la fábrica Thath solamente acepta los parámetros de datos:

public delegate IFooPresenter FooPresenterFactory(IFooView view);

Su presentador va sin cambios:

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

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

A continuación, la instalación de contenedores Autofac:

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

En su página puede en dos líneas de código resolver el presentador al obtener por primera vez la fábrica y luego llamar a la fábrica para hacer la solución para usted:

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

    public Foo() {
        var factory = IoCResolve<FooPresenterFactory>();
        this.presenter = factory(this);
    }
}

Otros consejos

He resuelto este problema exacto y la construcción de un marco a su alrededor.He utilizado Autofac parámetros para pasar los puntos de vista existentes, el presentador de la resolución de convocatoria.

En primer lugar, he definido una resolución personalizada de la interfaz de derivados de la Autofac:

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

lo que me permitió registrar un presentador que se resuelve la vista:

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

el uso de un método de extensión que se ajusta a la Autofac IContext en una implementación de IMvpContext:

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

He definido un tipo de parámetro que representa el parámetro de vista:

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

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

Utiliza su propia asamblea-nombre de tipo calificado como el nombre del parámetro.Esto tiene una muy baja probabilidad de entrar en conflicto con legítimo de los parámetros.

MvpContext pasa con todos los estándares de resolución de llamadas a la base de contexto.Para la vista, se resuelve el parámetro con el nombre conocido:

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
}

La llamada a resolver el presentador proporciona el parámetro de vista:

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

    public Foo()
    {
        this.presenter = IoCResolve<IFooPresenter>(new MvpViewParameter(this));
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top