Validate Data Passed to Properties and Communicate Errors to Developers

Chia sẻ: Bui Tuan | Ngày: | Loại File: PDF | Số trang:7

lượt xem

Validate Data Passed to Properties and Communicate Errors to Developers

Mô tả tài liệu
  Download Vui lòng tải xuống để xem tài liệu đầy đủ

Qua xác minh dữ liệu để tiết và Truyền đạt lỗi để phát triển Để thực hiện một lớp học mà kết thúc tốt đẹp truy cập vào một bảng, nó là rất quan trọng mà các lớp học và không phải là phát triển những người đang sử dụng các lớp bảo đảm rằng tất cả dữ liệu có giá trị trước khi viết nó cơ sở dữ liệu

Chủ đề:

Nội dung Text: Validate Data Passed to Properties and Communicate Errors to Developers

  1. 9.6 Validate Data Passed to Properties and Communicate Errors to Developers To make a class that wraps up access to a table, it is critical that the class-and not the developer who is using the class-makes sure that all data is valid before writing it to the database. For example, in the Customers table, the CustomerID field must be five characters in length-no more, no less-and, of course, it must be unique. Phone numbers and fax numbers must also be validated. Although we don't necessarily know how many digits are in a phone number (France has eight-digit numbers, Spain has nine, and the U.S. has ten), we do know that only numbers and characters such as periods, parentheses, and hyphens are allowed. You also need to communicate with other developers when their data is not valid. It is the data class's job to make sure that invalid data doesn't make it to the database. This section presents three tasks: adding code to make sure that data passed to an object matches the column properties in the database, adding code that validates complex data types and business rules, and communicating errors to the class's consumers. Technique First, you need to pass errors back up the call stack. In Visual Basic 6, the accepted method was to use Err.Raise, which propagates an error number and an error message. This technique is still available to Visual Basic developers, but it retains all the old problems. If you aren't careful, programmers who are working on different components can raise the same error numbers, making error handling a potential nightmare. (If you aren't careful, you can end up raising the same error number in your own code.) You learned how to use structured exception handling in earlier chapters, but the beauty is that you can define your own exceptions as needed, with almost no code. Also, because exceptions have a name as opposed to a number, it is far easier for developers to work with your code. Finally, because exceptions are defined with a Namespace, even if two projects define an InvalidCustomerIDException, each exception will be unique. Second, you need to write code to check whether a value is valid. For the CustomerID, you simply need to check the length of the string. (Later on, you'll need to add code to check whether the ID already exists.) For phone numbers, you'll need to examine the string for invalid characters. Steps
  2. Because validating the maximum allowable length for all of our properties is simplest, tackle this task first. 1. Define a new exception class by inheriting from System.ApplicationException. As mentioned before, Microsoft recommends that all custom exceptions inherit from ApplicationException rather than Exception. System.ApplicationException has most of the methods and properties that you will need to communicate an exception to consumers of this class. The only property that a consumer might find useful is one that exposes what the maximum length of the property is. This would allow the consumer to communicate the error back to the user or truncate the string without having to hard-code the maximum length into his code. Adding the name of the property and the value is also a good idea. Some developers who are using your class might write one long Try...Catch block, so this information will help debug going forward. Paste the code in Listing 9.33 defining the MaximumStringLengthExceededException into your code. Listing 9.33 frmHowTo9_6.vb: Class Declaration for the MaximumStringLengthExceededException Public Class MaximumStringLengthExceededException Inherits System.ApplicationException Private nMaxLen As Integer Public Sub New(ByVal pMaxLen As Integer, ByVal pPropertyName As String, ByVal pValue As String) ' You need to initialize the base class of this exception. ' If you do not specifically call a constructor of the base ' class, the default constructor (the one without parameters) ' will be called, if it exists. MyBase must precede the call to ' the base class's constructor so that the .NET runtime knows not ' to call a constructor in the derived class."The value specified, " & pValue & _ ", exceeds the maximum " & "length of " & pMaxLen & " allowable by the " & _ pPropertyName & " property.") End Sub Public ReadOnly Property MaxLength() As Integer Get Return nMaxLen End Get End Property
  3. End Class 2. Next, modify the set block of each property in the class to check the length of the new value to see if it exceeds the maximum length of the column in the Customers table. If the length of the new value does exceed the maximum length, throw a new instance of the MaximumStringLengthExceededException. To do this, simply create an If...Then block that checks the maximum length into your class. When you have modified all of your properties, they should look like the ContactName property in Listing 9.34. Listing 9.34 frmHowTo9_6.vb: The ContactName Property Validates for the Maximum Length of the Column and Throws an Exception if the Value Passed to the Property Exceeds That Maximum Value Public Property ContactName() As String Implements ICustomer9_6.ContactName Get Return mstrContactName End Get Set(ByVal Value As String) If Value.Length
  4. End Sub End Class 4. Next, add a private method called ValidatePhoneNumber to check a phone number string for invalid characters, such as letters or punctuation marks, as shown in Listing 9.36. Listing 9.36 frmHowTo9_6.vb: A Function That Validates Phone Numbers Private Function ValidatePhoneNumber(ByVal pstrPhone As String) As Boolean ' Create a string array that contains the numbers 0 to 9, as well as ' a hyphen, period, space, and parentheses. Dim cValidChars() As String cValidChars = New String(14) {"1", "2", "3", "4", "5", _ "6", "7", "8", "9", "0", "(", ")", "-", " ", "."} Dim i As Integer = 0 Dim nUBound As Integer = cValidChars.GetUpperBound(0) Dim nLBound As Integer = cValidChars.GetLowerBound(0) ' Loop through the array of valid characters and remove them ' from a phone number string. If characters are left ' in the string, the phone number is invalid. For i = nLBound To nUBound Step 1 pstrPhone = pstrPhone.Replace(cValidChars(i), "") Next pstrPhone = pstrPhone.Trim() If pstrPhone.Length > 0 Then Return False Else Return True End If End Function 5. Modify the set blocks of the Fax and Phone properties to call the ValidatePhoneNumber method and throw an InvalidPhoneNumberException if the phone number is not valid. Your code should look like Listing 9.37. Listing 9.37 frmHowTo9_6.vb: The Phone Property That Validates Phone Numbers
  5. Public Property Phone() As String Implements ICustomer.Phone Get Return mstrPhone End Get Set(ByVal Value As String) If Value.Length
  6. ' Strip out any leading or trailing spaces. pstrID = pstrID.Trim ' A CustomerID must have five characters. If pstrID.Length = 5 Then Return True Else Return False End If End Function 8. Now add code like that in Listing 9.40 to your constructor that validates the CustomerID, and, if that value is invalid, throws an InvalidCustomerIDException. Listing 9.40 frmHowTo9_6.vb: An If...Then Block to Wrap Around Your Constructor Code If ValidateCustomerID(pCustomerIDToRetrieve) Then ' Your original constructor code goes here. Else Throw New InvalidCustomerIDException(pCustomerIDToRetrieve) End If 9. The final piece of validation code you need to add is a function that checks for the existence of a CustomerID before a new Customer object is instantiated. You could use the data access objects you defined in CCustomerData, but for performance purposes, you should create a new command object and use the ExecuteScalar method as shown in Listing 9.41; this method requires less interaction with the database. Listing 9.41 frmHowTo9_6.vb: The DoesCustomerIDExist Function Private Function DoesCustomerIDExist(ByVal pstrID As String) As Boolean Dim strSQL As String = "SELECT COUNT(*) FROM Customers " & _ "WHERE CustomerID = '" & pstrID & "'" Dim cmd As New System.Data.OleDb.OleDbCommand(strSQL, oleCnn) Dim fExists As Boolean
  7. oleCnn.Open() fExists = CBool(cmd.ExecuteScalar()) oleCnn.Close() Return fExists End Function Add an extra If...Then block in the constructor used to create a new Customer row and you're ready to start testing your new code. Your existing code for frmHowTo9_6 should suffice for testing. You made sure to handle exceptions thrown from properties in section 9.2. How It Works Much of the code in this section qualifies and extends the properties you have already defined and implemented. This section has two key concepts. Validating data is a critical part of any application, although the examples in this section use validation techniques you should already be familiar with. The next section will take data validation to a new level. The most important concept is declaring your own exceptions. Communicating errors to other parts of the application is, perhaps, more important than the business logic you implement. When it works, it works, but when something goes wrong, providing enough information about the error is far more important. Creating new exceptions is an elegant and readable way to communicate and handle errors. Comments Hardcoding the maximum column length into the property set block as recommended in this section isn't necessarily the best solution to this problem. If you decide to increase or decrease the length of the column in the database, you must search through all of your source code to find every place that value was hard coded. Using constants is one way around this, but your options still are fairly limited. The strongly typed dataset you created earlier has the potential to provide an elegant solution to this problem. The XSD that underlies the dataset could define many of the data rules in your tables, including the maximum length of columns. Also, the actual dataset exposes this information in its properties and methods. The problem is that Visual Studio .NET does not generate XSDs with such strict definitions.
Đồng bộ tài khoản