简体   繁体   中英

With LightInject, how can I pass arguments to child dependencies without registering a bunch of factories?

In the code below, I am trying to inject a ViewModel into a View, while the ViewModel requires a Model to wrap and another service that is in the container. The Model is not registered as it is not really a "service".

How do I:

a) not have to provide the IService instance as an argument (let the container resolve it),

b) not have to register a factory for my ViewModels (there will be many )

So what I'm really asking the container to do is treat my Model (that I pass as an argument) as if it were a registered "service" for the duration of this call to GetInstance.

If this is not possible with LightInject, are there any containers out there that have something like this?

public static class Program
{
    public static void Main()
    {
        var container = new LightInject.ServiceContainer();
        var service = new Service1();
        container.RegisterInstance<IService>(service);

        // Have to register the factory
        container.Register<IService, PersonModel, PersonViewModel>(
                (f, s, p) => new PersonViewModel(s, p));

        container.Register<View>();

        var person = new PersonModel(); // this is contextual -- not a service.
        object view = CreateView(container, typeof(View), service, person);

        // ultimate desired code:
        //var view = container.GetInstance(typeof(View), new object[] { person });

    }

    private static object CreateView(ServiceContainer container, Type viewType, IService service, object model)
    {
        var ctor = viewType.GetConstructors()[0];
        var parameters = new List<object>();
        foreach (var param in ctor.GetParameters())
        {
            var attr = param.GetCustomAttributes(typeof(ModelAttribute), false).FirstOrDefault();
            if (model != null && attr != null)
            {
                parameters.Add(model);
            }
            else
            {
                parameters.Add(container.GetInstance(param.ParameterType, new object[] { service, model }));
            }
        }
        return Activator.CreateInstance(viewType, parameters.ToArray());
    }
}

public interface IService
{
}

public class Service1 : IService
{
}

public class PersonModel
{
}

public class PersonViewModel
{
    public PersonModel PersonModel { get; set; }
    public PersonViewModel(IService service, [Model] PersonModel person)
    {
        PersonModel = person;
    }
}

public class View
{
    public PersonViewModel PersonViewModel { get; set; }
    public View(PersonViewModel vm)
    {
        PersonViewModel = vm;
    }
}

[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
public class ModelAttribute : Attribute
{

}

I have solved the issues with a combination of techniques...

a) use a Scope and register the ViewModel and View with PerScopeLifetime.

b) use a "ModelTracker" registered with a factory to allow an instance not created by the container to be injected (since models will be created by client code or a DbContext).

This combination also allows me to not register a factory for every ViewModel type -- but instead use the built-in mass registration functions (like RegisterAssembly).

public static class Program
{
    public static void Main()
    {
        var container = new LightInject.ServiceContainer();

        container.RegisterInstance<IService>(new Service1());

        container.Register<View>(new PerScopeLifetime());
        container.Register<PersonViewModel>(new PerScopeLifetime());
        container.Register<ModelTracker>(new PerScopeLifetime());
        container.Register<PersonModel>((f) => (PersonModel)f.GetInstance<ModelTracker>().Instance);

        using (var scope = container.BeginScope())
        {
            var tracker = scope.GetInstance<ModelTracker>();
            tracker.Instance = new PersonModel() { Name = "person1" };

            var view = scope.GetInstance<View>();
        }

    }

}

public class ModelTracker
{
    public object Instance { get; set; }
}

public class PersonModel
{
    public string Name { get; set; }

}

public class PersonViewModel
{
    private readonly IService service;
    private readonly PersonModel person;

    public PersonViewModel(IService service, PersonModel person)
    {
        this.service = service;
        this.person = person;
    }
}

public class View
{
    public PersonViewModel PersonViewModel { get; set; }
    public View(PersonViewModel vm)
    {
        PersonViewModel = vm;
    }
}

public interface IService { }
public class Service1 : IService { }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM