简体   繁体   English

如何使用 c#.Net 资源文件类型安全地实现与语言无关的错误处理?

[英]How to implement language independent error handling using c# .Net ressource files type-safe?

Problem: I'm trying to implement a language independent error handling using.Net resource files.问题:我正在尝试使用.Net 资源文件实现与语言无关的错误处理。 Error messages must be displayed in the UI language and may be logged in a different log language.错误消息必须以 UI 语言显示,并且可能以不同的日志语言记录。 Error messages might have the content of variables inserted (String.Format).错误消息可能会插入变量的内容 (String.Format)。 Different errors can be accumulated for example when validating objects.例如,在验证对象时可能会累积不同的错误。 The accumulated errors will then be logged and passed to the UI.然后将记录累积的错误并将其传递给 UI。

Framework: I want to use Resource resx files and the Visual Studio resource editor, because the language resources are compiled into a class which allows type-safe access to the keys in code.框架:我想使用 Resource resx 文件和 Visual Studio 资源编辑器,因为语言资源被编译成 class,它允许类型安全地访问代码中的键。 (a test will check if each language has a translation for each key) (测试将检查每种语言是否有每个键的翻译)

Implementation: The errors should be passed upwards in an error object.实现:错误应该在错误 object 中向上传递。 I tried different implementations, but each one has a drawback and I wonder if there is a better solution or a way to overcome the drawbacks.我尝试了不同的实现,但每个都有一个缺点,我想知道是否有更好的解决方案或克服这些缺点的方法。 The codes below are simplified to get the point across.下面的代码被简化以理解这一点。

1) Create two messages, UI and LOG message at error creation 1)在错误创建时创建两条消息,UI和LOG消息

To create the messages I can call the GetString method of the ResourceManager with the UI and the LOG Culture and save those messages in the error object.要创建消息,我可以使用 UI 和 LOG Culture 调用 ResourceManager 的 GetString 方法,并将这些消息保存在错误 object 中。 Eg例如

    ResourceManager rm = new ResourceManager(typeof(ErrorText));
    
    if (!isValid) {
         string key = Resources.ErrorText.MaximumTitleLength
         string uiError = String.Format(GetString(key, uiCulture), MaxSeperatorLength.ToString());
         string logError = String.Format(GetString(key, logCulture), MaxSeperatorLength.ToString());
         Error err = new Error (uiError, logError);
    }

Drawbacks: The main drawback is that the domain class has to deal with the language concern, violating the separation of concerns.缺点:主要缺点是域 class 必须处理语言关注点,违反关注点分离。 The code is polluted with the language code.代码被语言代码污染。 The language variations are limited to two.语言变体仅限于两种。 The language decision can't be delayed to later and if log and UI languages are identical, the strings are saved twice.语言决定不能延迟到以后,如果日志和 UI 语言相同,则字符串会保存两次。

2) Pass just the key and resource type 2) 只传递密钥和资源类型

    if (!isValid) {
         Error err = new Error (resType: typeof(Resources.ErrorText),
                                key: nameof(Resources.ErrorText.MaximumTitleLength), 
                                params: MaxSeperatorLength.ToString());
    }

err.GetMessage(CultureInfo) will then return the message in the desired language. err.GetMessage(CultureInfo) 然后将以所需语言返回消息。

Drawbacks: The keys in the compiled resource class are properties of type string.缺点:编译资源 class 中的键是字符串类型的属性。 C# doesn't allow to pass on a reference to a class property. C# 不允许传递对 class 属性的引用。 Therefore I use nameof() to have the compiler be able to check the key type.因此我使用 nameof() 让编译器能够检查密钥类型。 In the error class the key is saved as a read-only string, to avoid accidental changes.在错误 class 中,密钥被保存为只读字符串,以避免意外更改。 nameof() only returns the key name, but not the class name. nameof() 仅返回密钥名称,而不返回 class 名称。 Therefore the Error class needs another parameter for the resource type and also the assembly name if the resource file is in a different assembly.因此,如果资源文件位于不同的程序集中,则错误 class 需要另一个资源类型参数以及程序集名称。 Optional factory methods for special Error classes like PersonError can have the resource type name internally to the class, making the calls simpler.特殊错误类(如 PersonError)的可选工厂方法可以在 class 内部具有资源类型名称,从而使调用更简单。 But nothing stops the caller from using a key from a different resource key, since the resource type name and the key are split.但是没有什么能阻止调用者使用来自不同资源键的键,因为资源类型名称和键是分开的。

Question: Is there any better option to pass the key including the resource type in a type-safe way?问题:有没有更好的选择以类型安全的方式传递包括资源类型在内的密钥? Is there a way to enforce that the resType parameter is a resource type?有没有办法强制 resType 参数是资源类型? It seems that the created class has no interface that could be used as a selector.创建的 class 似乎没有可以用作选择器的接口。 I thought of encapsulating the used resource classes in an own static classes that hold the reference to the concrete resource file and then use this class as a parameter for resType.我想将使用的资源类封装在自己的 static 类中,该类包含对具体资源文件的引用,然后将此 class 用作 resType 的参数。

3) Pass the key as a lambda 3) 将密钥作为 lambda 传递

    if (!isValid) {
        Error err = new Error (key: () => Resources.ErrorText.MaximumTitleLength,
                               params: MaxSeperatorLength.ToString());
    }

The benefit of a lambda is that the key and the resource type are passed in one argument. lambda 的好处是密钥和资源类型在一个参数中传递。

Drawbacks: Any lambda that produces a string can be passed.缺点:任何产生字符串的lambda都可以通过。 It can't be enforced that a resource key is used.不能强制使用资源密钥。 When the lambda will be invoked in the GetErrorMessage method Thread.CurrentThread.CurrentUICulture is used to decide which language version is used.当 lambda 会在 GetErrorMessage 方法中被调用 Thread.CurrentThread.CurrentUICulture 用于决定使用哪个语言版本。 So the thread culture has to be saved, set to the requested language, and then returned to the saved one.所以必须保存线程文化,设置为请求的语言,然后返回保存的语言。

Question: Is there any method to limit the lambda to a resource key?问题:有什么方法可以将 lambda 限制为资源键? I'm not very familiar with lambda options.我对 lambda 选项不是很熟悉。 Is there a way to get from the right side of the passed lambda "Resources.ErrorText.MaximumTitleLength" the key name and the resource type to allow obtaining the translation via GetString(CultureInfo) call via the ResourceManager.有没有办法从传递的 lambda "Resources.ErrorText.MaximumTitleLength" 的右侧获取键名和资源类型,以允许通过 ResourceManager 调用 GetString(CultureInfo) 获取翻译。

General Question: Is there a better way to accomplish the goals of multi-language and type-safe error messages?一般问题:是否有更好的方法来实现多语言和类型安全错误消息的目标?

EDIT: I found some answer to question #3.编辑:我找到了问题 #3 的一些答案。 If I treat the key in the Error class as a LambdaExpression instead of a delegate I have access to the Body property.如果我将错误 class 中的键视为 LambdaExpression 而不是委托,我可以访问 Body 属性。 Now I can get (with checking for Nulls):现在我可以得到(检查空值):

resource key as string -> key.Body.Member.Name
resource Type as string -> key.Body.Member.DeclaringType.FullName
assembly -> key.Body.Member.DeclaringType.Assembly

With those values I can read the resource files in the desired language.使用这些值,我可以读取所需语言的资源文件。

I ended up using the LambdaExpressions.我最终使用了 LambdaExpressions。 This allows me to defer the language decision and have the errors displayed in various languages.这使我可以推迟语言决定并以各种语言显示错误。

For flexibility and reduced amount of error texts I defined common error texts with placeholders.为了灵活性和减少错误文本的数量,我用占位符定义了常见的错误文本。 Eg an entry in the english resource file looks like:例如,英文资源文件中的条目如下所示:

Max_Char_Length_Exceeded -> "{0} is too long. The maximum length is {1} characters."

Additionally, I created static methods for the error message class to ensure correct parameters at compile time, also using lambda expressions for field name translations.此外,我为错误消息 class 创建了 static 方法,以确保在编译时参数正确,还使用 lambda 表达式进行字段名称翻译。

public static ErrorMessage MaxCharLengthExceeded(Expression<Func<string>> fieldKey, int maxLength)
{
  return new ErrorMessage(() => ErrorText.Max_Char_Length_Exceeded, fieldKey, maxLength.ToString());
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在C#中使用类型安全的Mongo对象ID? - How to have type-safe Mongo Object Ids in C#? 为什么这个C#代码不是类型安全的,为什么这个其他位使它类型安全? - Why is this C# code not type-safe and why does this other bit make it type-safe? 什么是.net中的类型安全? - What is type-safe in .net? 为什么类型安全是在 C# 中使用 generics 的主要好处之一? - Why type-safe is one the major benefits of using generics in C#? 在 C# 中缺少用于类型安全数据绑定的“nameof”运算符的解决方法? - Workaround for lack of 'nameof' operator in C# for type-safe databinding? C#2.0中是否存在匿名,类型安全,通用的委托签名? - Are there anonymous, type-safe, generic delegate signatures in C# 2.0? 类型安全的 Control.Invoke C# - type-safe Control.Invoke C# C#中的类型安全区分联合,或者:如何限制接口的实现数量? - Type-safe discriminated unions in C#, or: How to limit the number of implementations of an interface? 如何使用 C# MongoDb 驱动程序更新深度嵌套数组中的“类型安全”字段? - How to update a field “type-safe” in a deeply nested array with C# MongoDb driver? 以类型安全的方式处理PropertyChanged - Handling PropertyChanged in a type-safe way
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM