如何在不使用DisplayNameAttribute的情况下更改ViewModel显示名称?

Moh*_*din 4 .net c# asp.net-mvc .net-core asp.net-core

我想直接更改某些属性(ViewModel的)的显示名称,而无需使用[DisplayName("prop name")]。这应该在返回View之前直接在控制器内部执行,或者在ViewModel类本身内部进行

我不想更改视图中的任何内容,也不想使用任何数据注释。我该如何实现?

有没有流利的语法可以做到这一点?

我正在使用:ASP.Net Core 2.0

数据注释的问题是我想在运行时获取显示名称(数据注释已预先编译)。

更新:

提出此问题的主要原因是找到一种方法来包装IStringLocalizer本地化数据注释时的行为,尤其是其行为。公认的答案很好地说明了这一点。

Tse*_*eng 5

@Tseng,对不起,我应该说得更清楚些,我的意思是我们应该使用命名约定或SharedResources。但不是两者兼有,在很多情况下,我有很多共享资源以及许多ViewModel特定的字符串(因此是混合的)。.Net Core本地化解决方案无法实现这一点。

如果您唯一担心的是,您可以确定是否选择了一个或多个资源文件,则可以轻松进行配置。我不得不在源代码中进行挖掘,似乎有可能。

正如我们在这里看到的localizer,由配置中定义的工厂决定

if (_stringLocalizerFactory != null && _localizationOptions.DataAnnotationLocalizerProvider != null)
{
    localizer = _localizationOptions.DataAnnotationLocalizerProvider(containerType, _stringLocalizerFactory);
}
Run Code Online (Sandbox Code Playgroud)

_localizationOptionsMvcDataAnnotationsLocalizationOptions

默认的实现MvcDataAnnotationsLocalizationOptions在这里

/// <inheritdoc />
public void Configure(MvcDataAnnotationsLocalizationOptions options)
{
    if (options == null)
    {
        throw new ArgumentNullException(nameof(options));
    }

    options.DataAnnotationLocalizerProvider = (modelType, stringLocalizerFactory) =>
        stringLocalizerFactory.Create(modelType);
}
Run Code Online (Sandbox Code Playgroud)

因此,默认情况下它使用每个模型的资源。

您可以根据需要将其更改SharedResource为所有数据注释的文件,并在其中添加以下内容Startup.ConfigureServices(未经测试,但应该可以使用):

services.AddMvc()
    .AddDataAnnotationsLocalization(options =>
    {
        options.DataAnnotationLocalizerProvider = (type, factory) =>
            factory.Create(typeof(SharedResource));
    });
Run Code Online (Sandbox Code Playgroud)

这将有效地忽略传递的类型,并始终返回共享的字符串本地化器。

当然,您可以在此处添加任何逻辑,并根据要使用的本地化程序确定每种类型的情况。

编辑

如果这还不够,您可以实现自己的自定义IDisplayMetadataProvider样式,以所需的方式对其进行处理。但是使用实际DisplayAttribute应该足够了。DisplayAttribute具有其他参数,可用于定义资源类型。

[Display(Name = "StringToLocalize", ResourceType = typeof(SharedResource))]
Run Code Online (Sandbox Code Playgroud)

使用,ResourceType您可以选择用于查找本地化的类(以及资源​​文件名)。

编辑2:IStringLocalizer对每个ViewModel资源使用后退包装

更为优雅的解决方案涉及使用以上MvcDataAnnotationsLocalizationOptions选项文件返回自己的文件,该文件将IStringLocalizer查找一个资源文件,然后退回到另一个资源文件。

public class DataAnnotationStringLocalizer : IStringLocalizer
{
    private readonly IStringLocalizer primaryLocalizer;
    private readonly IStringLocalizer fallbackLocalizer;

    public DataAnnotationStringLocalizer(IStringLocalizer primaryLocalizer, IStringLocalizer fallbackLocalizer)
    {
        this.primaryLocalizer = primaryLocalizer ?? throw new ArgumentNullException(nameof(primaryLocalizer));
        this.fallbackLocalizer = fallbackLocalizer ?? throw new ArgumentNullException(nameof(fallbackLocalizer));
    }

    public LocalizedString this[string name]
    {
        get
        {
            LocalizedString localizedString = primaryLocalizer[name];
            if (localizedString.ResourceNotFound)
            {
                localizedString = fallbackLocalizer[name];
            }

            return localizedString;
        }
    }

    public LocalizedString this[string name, params object[] arguments]
    {
        get
        {
            LocalizedString localizedString = primaryLocalizer[name, arguments];
            if (localizedString.ResourceNotFound)
            {
                localizedString = fallbackLocalizer[name, arguments];
            }

            return localizedString;
        }
    }

    public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures)
        => primaryLocalizer.GetAllStrings(includeParentCultures).Concat(fallbackLocalizer.GetAllStrings(includeParentCultures));

    public IStringLocalizer WithCulture(CultureInfo culture)
        => new DataAnnotationStringLocalizer(primaryLocalizer.WithCulture(culture), fallbackLocalizer.WithCulture(culture));
}
Run Code Online (Sandbox Code Playgroud)

并具有以下选项

services.AddMvc()
    .AddDataAnnotationsLocalization(options =>
    {
        options.DataAnnotationLocalizerProvider = (type, factory) =>
        {
            return new DataAnnotationStringLocalizer(
                factory?.Create(typeof(SharedResource)),
                factory?.Create(type)
            );
        };
    });
Run Code Online (Sandbox Code Playgroud)

现在,首先从共享资源中解析该字符串,如果在共享资源中找不到该字符串,它将从视图模型类型(将类型参数传递给factory方法)解析它。

如果您不喜欢该逻辑,并且希望它首先查看视图模型资源文件,则只需将顺序更改为

services.AddMvc()
    .AddDataAnnotationsLocalization(options =>
    {
        options.DataAnnotationLocalizerProvider = (type, factory) =>
        {
            return new DataAnnotationStringLocalizer(
                factory?.Create(type),
                factory?.Create(typeof(SharedResource))
            );
        }
    });
Run Code Online (Sandbox Code Playgroud)

现在,视图模型是主要的解析器,共享资源是次要的解析器