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

标签 c# .net asp.net-mvc asp.net-core .net-core

我想直接更改(ViewModel 的)某些属性的显示名称,而不使用 [DisplayName("prop name")]。这应该在返回 View 之前直接在 Controller 内部发生,或者在 ViewModel 类本身内发生。

我不想更改 View 中的任何内容,也不想使用任何数据注释。我怎样才能做到这一点?

是否有任何流畅的语法可以做到这一点?

我正在使用:ASP.Net Core 2.0

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

更新:

问这个问题的主要原因是找到一种包装IStringLocalizer 的方法,尤其是它在本地化数据注释时的行为。接受的答案解释了该井的基础知识。

最佳答案

@Tseng, sorry I should have said that more clearly, I meant that we should use either naming convention, or SharedResources. but not both, I have many cases where I have a lot of shared resources, and many ViewModel-specific strings (so a mixture). That is not achievable with .Net Core localization solution.

如果您唯一担心的是您可以或不能确定是否选择了一个或多个资源文件,那么可以很容易地进行配置。我不得不在源代码中挖掘一点,它似乎是可能的。

正如我们所见here localizer 由配置中定义的工厂决定

if (_stringLocalizerFactory != null && _localizationOptions.DataAnnotationLocalizerProvider != null)
{
    localizer = _localizationOptions.DataAnnotationLocalizerProvider(containerType, _stringLocalizerFactory);
}

_localizationOptionsMvcDataAnnotationsLocalizationOptions

MvcDataAnnotationsLocalizationOptions 的默认实现是 here :

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

    options.DataAnnotationLocalizerProvider = (modelType, stringLocalizerFactory) =>
        stringLocalizerFactory.Create(modelType);
}

因此它默认使用每个模型的资源。

如果您愿意,您可以将其更改为所有数据注释的 SharedResource 文件,在您的 Startup.ConfigureServices 中使用以下内容(未经测试,但应该可以工作):

services.AddMvc()
    .AddDataAnnotationsLocalization(options =>
    {
        options.DataAnnotationLocalizerProvider = (type, factory) =>
            factory.Create(typeof(SharedResource));
    });

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

当然,您可以在那里添加任何逻辑,并根据类型决定要使用哪种本地化器。

编辑

如果这还不够,您可以实现自己的自定义 IDisplayMetadataProvider,它会按照您想要的方式处理它。但实际上使用 DisplayAttribute 应该就足够了。 DisplayAttribute 具有其他参数,可让您定义资源类型。

[Display(Name = "StringToLocalize", ResourceType = typeof(SharedResource))]

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

编辑 2:使用包装的 IStringLocalizer 回退到每个 View 模型资源

更优雅的解决方案涉及使用上面的 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));
}

还有以下选项

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

现在,首先从共享资源解析字符串,如果在那里找不到字符串,它将从 View 模型类型(传递给工厂方法的类型参数)中解析它。

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

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

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

关于c# - 如何在不使用 `DisplayNameAttribute` 的情况下更改 ViewModel 显示名称?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48034655/

相关文章:

javascript - 从 onClick() 加载完整 View

asp.net - CSS 文件未在 Visual Studio 2008 SP 中下载?

c# - Dapper 输出参数被 mysql 解释为 null

c# - 一般异常处理问题

c# - 无法在 WPF 应用程序中创建自定义命令

javascript - 如何在 JavaScript 中从 Razor 模型对象获取 JSON 对象

c# - 如何访问传递到部分 View 中的匿名类型对象?

c# - 使用 dll 方法时出现 FileNotFoundException

c# - Google Pay 通行证

c# - 当绑定(bind)属性声明为接口(interface)类型与类类型时,WPF 绑定(bind)行为不同?