Связывание сложных свойств дочернего класса с привязкой модели polymorphi c в. NET Core 3.0 - PullRequest
0 голосов
/ 15 февраля 2020

Недавно я играл с пользовательским полиморфом c modelbinder , немного большим, чем то, что есть в документах, и я столкнулся со сценарием, в котором я не уверен, как справиться.

Я реализовал связыватель модели так же, как в документации, для иерархии, подобной этой:

public interface Step {}

public class StepWithSimpleProperty : Step
{
    public string Bar { get; set; }
}

public class StepWithComplexProperty : Step
{
    public List<Complex> MyList { get; set; }
}

Проблема, с которой я сталкиваюсь, заключается в том, что при связывании класса с Complex Это свойство, похоже, не связывает также список Complex экземпляров.

Теперь, пытаясь go немного подробнее рассказать о моем конкретном случае c, моя страница Razor имеет Таблица примерно такая:

@model Project.Pages.StepWithComplexProperty

<table>
    <tr>
        <th>Prop1</th>
        <th>Prop2</th>
    </tr>
    @for (var i = 0; i < Model.MyList.Count; i++)
    {
        <tr>
            <input type="hidden" asp-for="MyList[@i].ID"/>
            <td>
                <input type="text" asp-for="MyList[@i].Prop1"/>
            </td>
            <td>
                <input type="text" asp-for="MyList[@i].Prop2"/>
            </td>
        </tr>
    }
</table>

Это фактическая страница бритвы:

@page
@model Project.Pages.WizardViewModel
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

@{
    var currentStep = Model.Steps[Model.CurrentStepIndex];
    var viewName = currentStep.ToString().Substring(currentStep.ToString().LastIndexOf('.') + 1);
}

<h3>Step @(Model.CurrentStepIndex + 1) out of @Model.Steps.Count</h3>

@using (Html.BeginForm())
{
    @Html.Hidden("StepType", Model.Steps[Model.CurrentStepIndex].GetType())
    @Html.HiddenFor(m => m.CurrentStepIndex, Model.CurrentStepIndex)
    @Html.Partial("" + viewName + "", currentStep);

    if (Model.CurrentStepIndex > 0)
    {
        <input type="submit" value="Previous" name="prev" asp-page-handler="Previous" class="btn btn-secondary" />
    }

    if (Model.CurrentStepIndex < (Model.Steps.Count - 1))
    {
        <input type="submit" value="Next" name="next" asp-page-handler="Next" class="btn btn-primary" />
    }
    else
    {
        <input type="submit" value="Finish" name="finish" asp-page-handler="Finish" class="btn btn-primary" />
    }
}

И модель представления страницы бритвы:

public class WizardViewModel : PageModel
{
    [BindRequired]
    [BindProperty(SupportsGet = true)]
    public int CurrentStepIndex { get; set; }

    public IList<Step> Steps { get; set; }

    // Constructor and methods, below, but not relevant for the problem ...
}
``

In that `PageModel` it has the property of type `List<Step>`, which is each element representing a step of a wizard, a big form. Each of the classes that implement `Step` are simply _View Models_. And, like mentioned, moving back and forth through the wizard works fine... except for the step which as a list of complex objects, which the model binder seems to ignore. In case it helps, I'll post the code of the model binder below:

---

_WizardModelBinder.cs_

```csharp
    public class WizardModelBinder : IModelBinder
    {
        private Dictionary < Type, (ModelMetadata, IModelBinder) > binders;

        public WizardModelBinder(Dictionary < Type, (ModelMetadata, IModelBinder) > binders)
        {
            this.binders = binders;
        }

        public async Task BindModelAsync(ModelBindingContext bindingContext)
        {
            var modelTypeValue = bindingContext.ValueProvider.GetValue("StepType").FirstValue;
            var modelType = Type.GetType(modelTypeValue, true);

            IModelBinder modelBinder;
            ModelMetadata modelMetadata;
            if (modelTypeValue.Contains("Step"))
            {
                (modelMetadata, modelBinder) = binders[modelType];
            }
            else
            {
                bindingContext.Result = ModelBindingResult.Failed();
                return;
            }

            var newBindingContext = DefaultModelBindingContext.CreateBindingContext(
                bindingContext.ActionContext,
                bindingContext.ValueProvider,
                modelMetadata,
                bindingInfo : null,
                bindingContext.ModelName);

            await modelBinder.BindModelAsync(newBindingContext);
            bindingContext.Result = newBindingContext.Result;

            if (newBindingContext.Result.IsModelSet)
            {
                // Setting the ValidationState ensures properties on derived types are correctly 
                bindingContext.ValidationState[newBindingContext.Result] = new ValidationStateEntry
                {
                    Metadata = modelMetadata,
                };
            }
        }
    }

    public class WizardModelBinderProvider : IModelBinderProvider
    {
        public IModelBinder GetBinder(ModelBinderProviderContext context)
        {
            if (context.Metadata.ModelType != typeof(Step))
            {
                return null;
            }

            var subclasses = new []
            {
                typeof(StepWithSimpleProperty),
                typeof(StepWithComplexProperty),
            };

            var binders = new Dictionary < Type,
                (ModelMetadata, IModelBinder) > ();

            foreach (var type in subclasses)
            {
                var modelMetadata = context.MetadataProvider.GetMetadataForType(type);
                binders[type] = (modelMetadata, context.CreateBinder(modelMetadata));
            }

            return new WizardModelBinder(binders);
        }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...