如何在不使用“DisplayNameAttribute"的情况下更改 ViewModel 显示名称? [英] How to change ViewModel display name without using `DisplayNameAttribute`?

查看:30
本文介绍了如何在不使用“DisplayNameAttribute"的情况下更改 ViewModel 显示名称?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I want to change the display name of some properties (of ViewModel) directly without using [DisplayName("prop name")]. This should happen either directly inside the controller before returning the View, or inside the ViewModel class itself.

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

I do NOT want to change anything in the View, and I do not want to use any data annotations. How can I achieve that?

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

Is there any fluent syntax maybe to get that?

我正在使用:ASP.Net Core 2.0

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

The problem with data annotations is that I want to get my display name in run time (while data annotations are pre-compiled).

问这个问题的主要原因是找到一种方法来包装IStringLocalizer,特别是它在本地化数据注释时的行为.接受的答案很好地解释了这一点.

The main reason for asking this question was to find a way to wrap the IStringLocalizer and particularly its behavior when localizing data annotations. The accepted answer explains the basics of that well.

推荐答案

@Tseng,抱歉,我应该说得更清楚,我的意思是我们应该使用命名约定或 SharedResources.但不是两者兼而有之,我有很多情况下我有很多共享资源和许多特定于 ViewModel 的字符串(所以是混合).这是 .Net Core 本地化解决方案无法实现的.

@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.

如果您唯一担心的是可以或不能确定是选择了一个还是多个资源文件,那么可以轻松配置.我不得不深入研究源代码,似乎有可能.

If your only worries is that you can or can't determine if one or multiple resource files are chosen, that can easily be configured. I had to dig a bit in the source code, bit its seems possible.

正如我们所见这里 localizer 由工厂在配置中定义

As we can see here the localizer is determined by the factory defined in the configuration

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

_localizationOptionsMvcDataAnnotationsLocalizationOptions.

MvcDataAnnotationsLocalizationOptions 的默认实现是 此处:

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

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

所以它默认使用每个模型资源.

So it uses per model resources by default.

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

You can change that to a SharedResource file for all data annotations if you like, with the following in your Startup.ConfigureServices (untested, but should work):

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

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

This will effectively ignore the passed type and always return a shared string localizer.

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

Of course, you can add any logic there and decide on type-per-type case which localizer you are going to use.

如果这还不够,您可以实现您自己的自定义 IDisplayMetadataProvider 以您想要的方式处理它.但实际上使用 DisplayAttribute 应该就足够了.DisplayAttribute 具有允许您定义资源类型的附加参数.

If that's not enough, you can implement your own custom IDisplayMetadataProvider which handles it the way you want. But using the DisplayAttribute should be enough actually. DisplayAttribute has additional parameters which allow you to define the resource type.

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

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

With the ResourceType you can choose the class (and hence the resource file name) used to look up for the localization.

更优雅的解决方案是使用上面的 MvcDataAnnotationsLocalizationOptions 选项文件返回您自己的 IStringLocalizer,它会查看一个资源文件并回退到另一个.

The more elegant solution involves using the above MvcDataAnnotationsLocalizationOptions options file to return your own IStringLocalizer which looks into one resource file and falls back to the other one.

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)
            );
        };
    });

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

Now, the string is first resolved from the shared resource and if the string wasn't found there, it will resolve it from the view model type (type parameter passed to the factory method).

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

If you don't like the logic and you want that it first looks into the view-model resource files, you just change the order to

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

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

Now the view model is the primary resolver and shared resource the secondary

这篇关于如何在不使用“DisplayNameAttribute"的情况下更改 ViewModel 显示名称?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆