Установить / изменить отображение (Name = "") атрибута свойства в Controller / ViewComponent - PullRequest
0 голосов
/ 14 марта 2019

Согласно этому ответу от 2010 года, относительно mvc-2, это было невозможно.Как насчет сейчас, в asp.net-core 2.2?

Мой сценарий использования:

У меня есть BaseViewModel, который используется двумя представлениями: TableView (для пользователей) и TableManagentView (для администраторов).BaseViewModel вызывается ViewComponent.Вот несколько примеров:

BaseViewModel:

public class BaseViewModel {
    [Display(Name = "Comment")
    public string UserComment { get; set; }
}

TableView:

@await Component.InvokeAsync(nameof(Base), new { myObject = myObject, stringName = "User"})

TableManagementView:

@await Component.InvokeAsync(nameof(Base), new { myObject = myObject, stringName = "Admin"})

Base:

public class Base : ViewComponent
{
    public IViewComponentResult Invoke(BaseViewModel myObjet, string invokingView)
    {
        // here i want to do something like that
        if (invokingView == "User") {
            myObject.UserComment.SetDisplayName("My Comment");
        } 
        if (invokingView == "Admin") {
            myObject.UserComment.SetDisplayName("User's Comment");
        }


        return View("BaseViewComponent", myObject);
    }
}

BaseViewComponent:

@Html.DisplayNameFor(model => model.UserComment)

BaseViewModel упрощен, но есть намного больше атрибутов.Причина, по которой я хочу это сделать, состоит в том, чтобы избежать дублирования кода в обеих таблицах.Единственное, что должно измениться - это имена меток.

Я пробовал reflection, но безуспешно:

public IViewComponentResult Invoke(BaseViewModel myObject, string invokingView)
    {
        MemberInfo property = typeof(BaseViewModel).GetProperty("UserComment");
        property.GetCustomAttributes(typeof(DisplayAttribute)).Cast<DisplayAttribute>().Single().Name = "test";

        return View("BaseViewComponent", myObject);
    }

Name не меняется и остается "Comment" из первоначальной настройки.

Если невозможно задать имя атрибута программно, какие у меня есть другие решения?Я думаю о ViewBag/ViewData или TempData, но это решение мне не нравится.Что за и против этого?

1 Ответ

1 голос
/ 14 марта 2019

Продолжая комментарий, который я оставил, один из способов, которым вы могли бы решить эту проблему, это сделать ваш BaseViewModel абстрактным классом и иметь конкретные классы, производные от него.Так что UserViewModel и AdminViewModel.Эти два конкретных класса будут затем моделями для TableView и TableManagentView и будут отвечать за указание «внешнему миру», как маркировать поля.

Базовый класс имеет два основных аспекта (кромеваши обычные поля): аннотация Dictionary<string, string>, которая будет содержать метки и метод для получения метки из списка: string GetLabel(string propName).Так что-то вроде этого:

public abstract class BaseViewModel
{
    protected abstract Dictionary<string, string> Labels { get; }

    public string UserComment { get; set; }

    public string GetLabel(string propName)
    {
        if (!Labels.TryGetValue(propName, out var label))
            throw new KeyNotFoundException($"Label not found for property name: {propName}");

        return label;
    }
}

Затем вы создаете два производных класса User и Admin:

public sealed class UserViewModel : BaseViewModel
{
    protected override Dictionary<string, string> Labels => new Dictionary<string, string>
    {
        { nameof(UserComment), "User label" }
    };
}

public sealed class AdminViewModel : BaseViewModel
{
    protected override Dictionary<string, string> Labels => new Dictionary<string, string>
    {
        { nameof(UserComment), "Admin label" }
    };
}

Они реализуют только Dictionary<string, string> и задают соответствующий текстдля каждого поля базового класса.

Далее измените BaseViewComponent на следующее:

Вид :

@model DisplayNameTest.Models.BaseViewModel

<h3>Hello from my View Component</h3>

<!-- Gets the label via the method on the base class -->
<p>@Model.GetLabel(nameof(BaseViewModel.UserComment))</p>

<p>@Model.UserComment)</p>

Класс ComponentView (теперь проще)

public IViewComponentResult Invoke(BaseViewModel viewModel)
{
    return View(viewModel);
}

Наконец, изменив ваши представления TableView и TableManagentView на следующее:

@model WebApp.Models.AdminViewModel

@{
    Layout = null;
}

<h1>Admin View</h1>
<div>
    @await Component.InvokeAsync("Base", Model)
</div>

и контроллер на:

public IActionResult Index()
{
    var adminViewModel = new AdminViewModel { UserComment = "some comment from admin" };
    return View(adminViewModel);
}

Теперь, когда вы перейдете к TableView, вы передадите UserViewModel к BaseViewComponent, и он определит правильную метку.Введение новых полей потребует от вас изменения ваших моделей представления и добавления новой записи в Словарь.

Это не идеально, но я думаю, что это хороший способ решить эту проблему.Я далеко не эксперт по MVC, так что, возможно, другие могут придумать более естественный способ сделать это.Я также подготовил рабочий пример приложения и отправил его на GitHub.Вы можете проверить это здесь: aspnet-view-component-demo .Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...