现在我的ViewModel看起来是这样的:

public class MyViewModel
{
    private readonly IMyService myService;

    public ClaimantSearchViewModel(IMyService myService)
    {
        this.myService = myService;
    }
}

消耗这个的myController看起来是这样的:
public class MyController : Controller
{
    private readonly IMyService myService;
    public HomeController(IMyService myService)
    {
        this.myService = myService;
    }

    public IActionResult Index()
    {
        var model = new MyViewModel(myService);

        return View(model);
    }

    [HttpPost]
    public async Task<IActionResult> Find()
    {
        var model = new MyViewModel(myService);
        await TryUpdateModelAsync(model);

        return View("Index", model);
    }
}

我需要我的ViewModel看起来是这样的:
public class MyController : Controller
{
    private readonly IServiceProvider servicePovider;
    public MyController(IServiceProvider servicePovider)
    {
        this.servicePovider = servicePovider;
    }

    public IActionResult Index()
    {
        var model = servicePovider.GetService(typeof(MyViewModel));

        return View(model);
    }

    [HttpPost]
    public IActionResult Index(MyViewModel model)
    {
        return View(model);
    }
}

现在,调用第一个Controller方法很好(使用
builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource(x => x.Name.Contains("ViewModel")));

在myIndex)中,但是执行Startup classPOST会给您一个Index(MyViewModel model)异常。我意识到使用我的No parameterless constructor defined for this objectcustom model binder将是最有可能的解决方案…但我找不到任何帮助,甚至无法开始这里。请帮助我,特别是在DI中的Autofac

最佳答案

我们得到了答案:https://github.com/aspnet/Mvc/issues/4167
答案是使用:[fromservices]
我的模特最后看起来是这样的:

public class MyViewModel
{
    [FromServices]
    public IMyService myService { get; set; }

    public ClaimantSearchViewModel(IMyService myService)
    {
        this.myService = myService;
    }
}

虽然让这个属性public很难过,但它比使用custom model binder要少得多。
而且,假设您应该能够将[FromServices]作为action方法中参数的一部分传递,它确实解析了类,但这会破坏模型绑定…我的财产都没有测绘出来。看起来是这样的:(但同样,这不起作用,所以使用上面的例子)
public class MyController : Controller
{
    ... same as in OP

    [HttpPost]
    public IActionResult Index([FromServices]MyViewModel model)
    {
        return View(model);
    }
}

更新1
在使用[FromServices]属性之后,我们决定所有ViewModels中的属性注入不是我们想要的方式,特别是在考虑使用测试进行长期维护时。因此,我们决定删除[FromServices]属性并使我们的自定义模型绑定器工作:
public class IoCModelBinder : IModelBinder
{
    public Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext)
    {
        var serviceProvider = bindingContext.OperationBindingContext.HttpContext.RequestServices;

        var model = serviceProvider.GetService(bindingContext.ModelType);
        bindingContext.Model = model;

        var binder = new GenericModelBinder();
        return binder.BindModelAsync(bindingContext);
    }
}

它在StartupConfigureServices方法中注册如下:
        services.AddMvc().AddMvcOptions(options =>
        {
            options.ModelBinders.Clear();
            options.ModelBinders.Add(new IoCModelBinder());

        });

就这样。(甚至不确定是否需要options.ModelBinders.Clear();。)
更新2
在经历了各种使其工作的迭代(在帮助下https://github.com/aspnet/Mvc/issues/4196)之后,下面是最终结果:
public class IoCModelBinder : IModelBinder
{
    public async Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext)
    {   // For reference: https://github.com/aspnet/Mvc/issues/4196
        if (bindingContext == null)
            throw new ArgumentNullException(nameof(bindingContext));

        if (bindingContext.Model == null && // This binder only constructs viewmodels, avoid infinite recursion.
                (
                    (bindingContext.ModelType.Namespace.StartsWith("OUR.SOLUTION.Web.ViewModels") && bindingContext.ModelType.IsClass)
                        ||
                    (bindingContext.ModelType.IsInterface)
                )
            )
        {
            var serviceProvider = bindingContext.OperationBindingContext.HttpContext.RequestServices;
            var model = serviceProvider.GetRequiredService(bindingContext.ModelType);

            // Call model binding recursively to set properties
            bindingContext.Model = model;
            var result = await bindingContext.OperationBindingContext.ModelBinder.BindModelAsync(bindingContext);

            bindingContext.ValidationState[model] = new ValidationStateEntry() { SuppressValidation = true };

            return result;
        }

        return await ModelBindingResult.NoResultAsync;
    }
}

很明显,您想用OUR.SOLUTION...代替namespace来注册我们的ViewModels
        services.AddMvc().AddMvcOptions(options =>
        {
            options.ModelBinders.Insert(0, new IoCModelBinder());
        });

更新3:
这是Model Binder及其与Provider一起工作的ASP.NET Core 2.X的最新迭代:
public class IocModelBinder : ComplexTypeModelBinder
{
    public IocModelBinder(IDictionary<ModelMetadata, IModelBinder> propertyBinders, ILoggerFactory loggerFactory) : base(propertyBinders, loggerFactory)
    {
    }

    protected override object CreateModel(ModelBindingContext bindingContext)
    {
        object model = bindingContext.HttpContext.RequestServices.GetService(bindingContext.ModelType) ?? base.CreateModel(bindingContext);

        if (bindingContext.HttpContext.Request.Method == "GET")
            bindingContext.ValidationState[model] = new ValidationStateEntry { SuppressValidation = true };
        return model;
    }
}

public class IocModelBinderProvider : IModelBinderProvider
{
    private readonly ILoggerFactory loggerFactory;

    public IocModelBinderProvider(ILoggerFactory loggerFactory)
    {
        this.loggerFactory = loggerFactory;
    }

    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (!context.Metadata.IsComplexType || context.Metadata.IsCollectionType) return null;

        var propertyBinders = new Dictionary<ModelMetadata, IModelBinder>();
        foreach (ModelMetadata property in context.Metadata.Properties)
        {
            propertyBinders.Add(property, context.CreateBinder(property));
        }
        return new IocModelBinder(propertyBinders, loggerFactory);
    }
}

然后在Startup中:
services.AddMvc(options =>
{
    // add IoC model binder.
    IModelBinderProvider complexBinder = options.ModelBinderProviders.FirstOrDefault(x => x.GetType() == typeof(ComplexTypeModelBinderProvider));
    int complexBinderIndex = options.ModelBinderProviders.IndexOf(complexBinder);
    options.ModelBinderProviders.RemoveAt(complexBinderIndex);
    options.ModelBinderProviders.Insert(complexBinderIndex, new IocModelBinderProvider(loggerFactory));

08-24 23:33