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

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

问题描述

我想直接更改某些属性(ViewModel的)的显示名称,而不使用 [DisplayName( prop name)] 。该操作应直接在控制器内部发生,然后再返回View,或者在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不想更改视图中的任何内容,也不想使用任何数据注释。我该如何实现?

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.

我们可以看到此处 本地化程序由配置中定义的工厂确定

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

_localizationOptions MvcDataAnnotationsLocalizationOptions

MvcDataAnnotationsLocalizationOptions 的默认实现是< a href = https://github.com/aspnet/Mvc/blob/rel/2.0.0/src/Microsoft.AspNetCore.Mvc.DataAnnotations/Internal/MvcDataAnnotationsLocalizationOptionsSetup.cs#L22-L23 rel = nofollow noreferrer >此处:

/// <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天全站免登陆