Предположительно, ваши атрибуты проверки находятся в свойстве MyItems
вашей родительской модели, которая здесь не указана. Например,
public class ParentModel {
[Required]
public IEnumerable<SelectListItem> MyItems { get; set; }
}
Таким образом, проблема заключается в том, что ваше представление больше не привязывается к этой модели или ее свойству MyItems
. Вместо этого он привязывается к свойству Items
в модели DropdownViewComponent
, которая не имеет каких-либо атрибутов проверки. Оба эти свойства могут указывать на одну и ту же IEnumerable<SelectListItem>
ссылку на объект , но их метаданные совершенно разные. Таким образом, ASP. NET Ядро правильно отображает атрибуты проверки, связанные со свойством DropdownViewComponent.Items
.
Я вижу это как неудачное ограничение компонентов представления - но одно это концептуально трудно понять. Для получения дополнительной информации см. мой ответ на аналогичный вопрос, который я ранее задал, Как связать ModelExpression
с ViewComponent
в ASP. NET Core .
Тем не менее, учитывая ваши конкретные требования, вы можете обойти эту проблему, передав модель, которая содержит целевое свойство для вашего компонента представления, вместо передачи значения из само свойство target, а затем ретранслировать , что через модель представления вашего компонента представления:
public class DropdownViewComponent : ViewComponent
{
public ParentModel ParentModel { get; set; }
public ModelExpression SelectedItem { get; set; }
public async Task<IViewComponentResult> InvokeAsync(ModelExpression selectedItem, ParentModel model)
{
ParentModel = model;
SelectedItem = selectedItem;
return View(this);
}
}
Примечание: Обычно я создаю отдельный, легкий модель представления для вашего компонента представления, вместо того, чтобы передавать объект компонента представления вниз к его представлению. Но я поддерживаю эту структуру для согласованности с вашим исходным кодом.
После этого вы сможете привязаться к исходному свойству в представлении компонента представления, тем самым поддерживая все исходные атрибуты проверки:
<select asp-for="SelectedItem" asp-items="@Model.ParentModel.MyItems"></select>
В первом приближении это не очень выгодно для вас - и может даже победить всю вашу причину преследования компонента представления в первую очередь - поскольку заставляет вас работать против одной модели. Если несколько представлений с разными моделями хотят использовать этот компонент представления, это создает некоторые проблемы. Однако вы можете смягчить их, введя уровень абстракции.
Существует несколько подходов к этому, таких как создание интерфейса, но я рекомендую разработать специализированный класс списка, который вы используете для моделирования IEnumerable<SelectListItem>
:
public class DropdownList: List<SelectListItem> {
public virtual IEnumerable<SelectListItem> Items { get; } = this;
}
И затем отработка этого в вашем DropdownViewComponent
:
public class DropdownViewComponent : ViewComponent
{
public DropdownList DropdownList { get; set; }
public ModelExpression SelectedItem { get; set; }
public async Task<IViewComponentResult> InvokeAsync(ModelExpression selectedItem, DropdownList dropdownList)
{
DropdownList = dropdownList;
SelectedItem = selectedItem;
return View(this);
}
}
И, наконец, реализация этого следующим образом в представлении компонента вашего представления:
<select asp-for="SelectedItem" asp-items="@Model.DropdownList.Items"></select>
Это позволит вам переопределить этот класс для добавления атрибутов по мере необходимости. Например,
public class RequiredDropdownList : DropdownList {
[Required]
public override Items { get; } = this;
}
Примечание: Если вам нужно использовать различные коллекции на разных моделях представлений, и для их объединения вы использовали IEnumberable<SelectListItem>
, этот подход не сработает. В этом случае создание чего-то вроде IDropdownList
интерфейса имеет больше смысла. Несмотря на это, концепция практически идентична.