如何在不使用DisplayNameAttribute的情况下更改ViewModel显示名称? [英] How to change ViewModel display name without using `DisplayNameAttribute`?
问题描述
我想直接更改某些属性(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屋!