C# 业务对象、验证和异常

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/88541/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 11:29:31  来源:igfitidea点击:

Business Objects, Validation And Exceptions

提问by user11355

I've been reading a few questions and answers regarding exceptions and their use. Seems to be a strong opinion that exceptions should be raised only for exception, unhandled cases. So that lead me to wondering how validation works with business objects.

我一直在阅读一些关于异常及其使用的问题和答案。似乎是一种强烈的意见,即只应为异常、未处理的情况引发异常。所以这让我想知道验证如何与业务对象一起工作。

Lets say I have a business object with getters/setters for the properties on the object. Let's say I need to validate that the value is between 10 and 20. This is a business rule so it belongs in my business object. So that seems to imply to me that the validation code goes in my setter. Now I have my UI databound to the properties of the data object. The user enters 5, so the rule needs to fail and the user is not allowed to move out of the textbox. . The UI is databound to the property so the setter is going to be called, rule checked and failed. If I raised an exception from my business object to say the rule failed, the UI would pick that up. But that seems to go against the preferred usage for exceptions. Given that it's a setter, you aren't really going to have a ‘result' for the setter. If I set another flag on the object then that would imply the UI has to check that flag after each UI interaction.

假设我有一个业务对象,其中包含对象属性的 getter/setter。假设我需要验证该值是否介于 10 和 20 之间。这是一个业务规则,因此它属于我的业务对象。所以这对我来说似乎暗示验证代码在我的 setter 中。现在我将 UI 数据绑定到数据对象的属性。用户输入 5,因此规则需要失败,并且不允许用户移出文本框。. UI 数据绑定到属性,因此将调用 setter,检查规则并失败。如果我从我的业务对象中提出一个异常来说明规则失败,UI 会选择它。但这似乎与例外的首选用法背道而驰。鉴于它是一个二传手,你不会真的有二传手的“结果”。

So how should the validation work?

那么验证应该如何工作呢?

Edit: I've probably used an over-simplified example here. Something like the range check above could be handled easily by the UI but what if the valdation was more complicated, e.g. the business object calculates a number based on the input and if that calculated number is out of range it should be recjected. This is more complicated logic that should not be in th UI.

编辑:我可能在这里使用了一个过于简化的例子。UI 可以轻松处理类似于上面的范围检查的事情,但是如果验证更复杂,例如业务对象根据输入计算一个数字,如果计算出的数字超出范围,则应该拒绝它。这是更复杂的逻辑,不应出现在 UI 中。

There is also the consideration of further data entered based on a field already entered. e.g.I have to enter an item on the order to get certain informaion like stock on hand, current cost, etc. The user may require this information to make decisions on further entry (liek how many units to order) or it may be required in order for further validation to be done. Should a user be able to enter other fields if the item isn't valid? What would be the point?

还需要考虑根据已输入的字段输入更多数据。例如,我必须在订单上输入一个项目才能获得某些信息,例如现有库存、当前成本等。用户可能需要此信息来决定进一步输入(例如要订购多少件),或者可能需要这些信息以进行进一步验证。如果项目无效,用户是否应该能够输入其他字段?重点是什么?

回答by Kimoz

I my opinion this is an example where throwing an exception is okay. Your property probably does not have any context by which to correct the problem, as such an exception is in order and the calling code should handle the situation, if possible.

我认为这是一个可以抛出异常的例子。您的财产可能没有任何上下文来纠正问题,因为这样的异常是有序的,如果可能,调用代码应该处理这种情况。

回答by Ludvig A. Norin

If the input goes beyond the business rule implemented by the business object, I'd say it's a case not handled by the busines object. Therefore I'd throw an exception. Even though the setter would "handle" a 5 in your example, the business object won't.

如果输入超出业务对象实现的业务规则,我会说这是业务对象未处理的情况。因此我会抛出一个异常。即使 setter 会在您的示例中“处理”一个 5,业务对象也不会。

For more complex combinations of input, a vaildation method is required though, or else you'll end up with quite complex validations scattered about all over the place.

但是,对于更复杂的输入组合,需要使用验证方法,否则最终会得到散布在各处的相当复杂的验证。

In my opinion you'll have to decide which way to go depending on the complexity of the allowed/disallowed input.

在我看来,您必须根据允许/不允许输入的复杂性来决定走哪条路。

回答by Andrew Swan

You might like to consider the approach taken by the Spring framework. If you're using Java (or .NET), you can use Spring as-is, but even if you're not, you could still use that pattern; you'd just have to write your own implementation of it.

您可能想考虑Spring 框架采用的方法。如果您使用的是 Java(或 .NET),则可以按原样使用 Spring,但即使不是,您仍然可以使用该模式;你只需要编写自己的实现。

回答by Odd

Throwing an exception in your case is fine. You can consider the case a true exception because something is trying to set an integer to a string (for example). The business rules lack of knowledege of your views means that they should consider this case exceptonal and return that back to the view.

在您的情况下抛出异常很好。您可以将这种情况视为真正的异常,因为某些东西试图将整数设置为字符串(例如)。业务规则对您的视图缺乏了解意味着他们应该将此情况视为例外并将其返回给视图。

Whether or not you validate your input values before you send them through to the business layer is up to you, I think that as long as you follow the same standard throughout your application then you will end up with clean and readable code.

在将输入值发送到业务层之前是否对其进行验证取决于您,我认为只要您在整个应用程序中遵循相同的标准,那么您最终将得到干净可读的代码。

You could use the spring framework as specified above, just be careful as much of the linked document was indicating writing code that is not strongly typed, I.E. you may get errors at run time that you could not pick up at compile time. This is something I try to avoid as much as possible.

您可以使用上面指定的 spring 框架,但要小心,因为链接文档的大部分内容都表明编写的代码不是强类型的,IE 您可能会在运行时遇到错误,而这些错误在编译时无法找到。这是我尽量避免的事情。

The way we do it here currently is that we take all the input values from the screen, bind them to a data model object and throw an exception if a value is in error.

我们目前的做法是从屏幕上获取所有输入值,将它们绑定到数据模型对象,如果值出错则抛出异常。

回答by Matt Howells

Perhaps you should look at having both client-side and server-side validation. If anything slips past the client-side validation you can then feel free to throw an exception if your business object would be made invalid.

也许您应该同时考虑客户端和服务器端验证。如果有任何事情通过客户端验证,如果您的业务对象无效,您可以随意抛出异常。

One approach I've used was to apply custom attributes to business object properties, which described the validation rules. e.g.:

我使用的一种方法是将自定义特性应用于描述验证规则的业务对象属性。例如:

[MinValue(10), MaxValue(20)]
public int Value { get; set; }

The attributes can then be processed and used to automatically create both client-side and server-side validation methods, to avoid the problem of duplicating business logic.

然后可以处理这些属性并用于自动创建客户端和服务器端验证方法,以避免重复业务逻辑的问题。

回答by Rob Gray

I'd definitely advocate both client and server-side validation (or validating at the various layers). This is especially important when communicating across physical tiers or processes, as the cost of throw exceptions becomes increasingly expensive. Also, the further down the chain you wait for validation, the more time is wasted.

我绝对提倡客户端和服务器端验证(或在各个层进行验证)。这在跨物理层或进程通信时尤其重要,因为抛出异常的成本变得越来越昂贵。此外,您等待验证的链条越远,浪费的时间就越多。

As to use Exceptions or not for data validation. I think it's ok to use exception in process (though still not preferrable), but outside of process, call a method to validate the business object (eg before saving) and have the method return the success of the operation along with any validation errors. Errors arent' exceptional.

至于是否使用 Exceptions 进行数据验证。我认为在进程中使用异常是可以的(虽然仍然不是最好的),但在进程之外,调用一个方法来验证业务对象(例如在保存之前)并让该方法返回操作的成功以及任何验证错误。错误不是例外。

Microsoft throw exceptions from business objects when validation fails. At least, that's how the Enterprise Library's Validation Application Block works.

当验证失败时,Microsoft 会从业务对象中抛出异常。至少,这就是企业库的验证应用程序块的工作方式。

using Microsoft.Practices.EnterpriseLibrary.Validation;
using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;
public class Customer
{
  [StringLengthValidator(0, 20)]
  public string CustomerName;

  public Customer(string customerName)
  {
    this.CustomerName = customerName;
  }
}

回答by Daniel Auger

In my experience, validation rules are seldom universal across all screens/forms/processes in an application. Scenarios like this are common: on the add page, it may be ok for a Person object not to have a last name, but on the edit page it must have a last name. That being the case I've come to believe that validation should happen outside of an object, or the rules should be injected into the object so the rules can change given a context. Valid/Invalid should be an explicit state of the object after validation or one that can be derived by checking a collection for failed rules. A failed business rule is not an exception IMHO.

根据我的经验,验证规则很少适用于应用程序中的所有屏幕/表单/流程。像这样的场景很常见:在添加页面上,Person 对象可以没有姓氏,但在编辑页面上它必须有姓氏。在这种情况下,我开始相信验证应该发生在对象之外,或者规则应该被注入到对象中,这样规则就可以在给定上下文的情况下改变。Valid/Invalid 应该是验证后对象的显式状态,或者可以通过检查集合中的失败规则得出的状态。恕我直言,失败的业务规则不是例外。

回答by Brad at Kademi

Exceptions should not be thrown as a normalpart of validation. Validation invoked from within business objects is a last line of defense, and should only happen if the UI fails to check something. As such they can be treated like any other runtime exception.

不应将异常作为验证的正常部分抛出。从业务对象内部调用的验证是最后一道防线,只有在 UI 无法检查某些内容时才会发生。因此,它们可以像任何其他运行时异常一样对待。

Note that here's a difference between defining validation rules and applying them. You might want to define (ie code or annotate) your business rules in your business logic layer but invoke them from the UI so that they can handled in a manner appropriate to that particular UI. The manner of handling will vary for different UI's, eg form based web-apps vs ajax web-apps. Exception-on-set validation offers very limited options for handling.

请注意,定义验证规则和应用它们之间存在差异。您可能希望在业务逻辑层中定义(即代码或注释)业务规则,但从 UI 调用它们,以便它们可以以适合该特定 UI 的方式进行处理。对于不同的 UI,处理方式会有所不同,例如基于表单的 Web 应用程序与 ajax Web 应用程序。异常设置验证提供了非常有限的处理选项。

Many applications duplicate their validation rules, such as in javascript, domain object constraints and database constraints. Ideally this information will only be defined once, but implementing this can be challenge and requires lateral thinking.

许多应用程序复制它们的验证规则,例如在 javascript、域对象约束和数据库约束中。理想情况下,这些信息只会被定义一次,但实施这可能是一个挑战,需要横向思考。

回答by Mike Thompson

Assuming that you have separate validation and persist (i.e. save to database) code, I would do the following:

假设您有单独的验证和持久化(即保存到数据库)代码,我将执行以下操作:

  1. The UI should perform validation. Don't throw exceptions here. You can alert the user to errors and prevent the record from being saved.

  2. Your database save code should throw invalid argument exceptions for bad data. It makes sense to do it here, since you cannot proceed with the database write at this point. Ideally this should never happen since the UI should prevent the user from saving, but you still need it to ensure database consistency. Also you might be calling this code from something other than the UI (e.g. batch updates) where there is no UI data validation.

  1. UI 应该执行验证。不要在这里抛出异常。您可以提醒用户注意错误并防止保存记录。

  2. 您的数据库保存代码应该为坏数据抛出无效参数异常。在这里这样做是有意义的,因为此时您无法继续进行数据库写入。理想情况下,这应该永远不会发生,因为 UI 应该阻止用户保存,但您仍然需要它来确保数据库一致性。此外,您可能会从没有 UI 数据验证的 UI(例如批量更新)以外的地方调用此代码。

回答by Jeromy Irvine

Have you considered raising an event in the setter if the data is invalid? That would avoid the problem of throwing an exception and would eliminate the need to explicitly check the object for an "invalid" flag. You could even pass an argument indicating which field failed validation, to make it more reusable.

如果数据无效,您是否考虑过在 setter 中引发事件?这将避免引发异常的问题,并消除显式检查对象是否存在“无效”标志的需要。您甚至可以传递一个参数,指示哪个字段验证失败,以使其更具可重用性。

The handler for the event should be able to take care of putting focus back onto the appropriate control if needed, and it could contain any code needed to notify the user of the error. Also, you could simply decline to hook up the event handler and be free to ignore the validation failure if needed.

如果需要,事件的处理程序应该能够负责将焦点放回到适当的控件上,并且它可以包含通知用户错误所需的任何代码。此外,您可以简单地拒绝连接事件处理程序,并在需要时随意忽略验证失败。