Мы получили ответ здесь: 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);
}
}
В методе Startup
ConfigureServices
это прописано так:
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));
person
Serj Sagan
schedule
26.02.2016