Những phần mềm hỗ trợ Hay dành cho Visual Studio

Là môi trường phát triển tích hợp mạnh mẽ và phổ biến nhất hiện nay, Visual Studio (VS) cung cấp cho các nhà phát triển những công cụ hết sức hữu ích trong việc xây dựng phần mềm. Tuy nhiên, Visual Studio không phải là hoàn hảo, nó không thể bao hết mọi “ngóc ngách” của việc lập trình. May mắn thay, các nhà phát triển trên khắp thế giới đã cung cấp rất nhiều công cụ bổ sung, có thể giúp bạn làm việc hiệu quả hơn với Visual Studio.
Bài viết này sẽ giới thiệu những công cụ như vậy, chúng hoàn toàn miễn phí, thậm chí còn là mã nguồn mở, bạn có thể sử dụng mà không phải trả bất cứ một khoản lệ phí nào (dù vẫn có những thỏa thuận bản quyền phải tuân theo).
Code Project Browser

Trang chủ: http://www.codeproject.com (http://www.codeproject.com/) Loại dự án: Mã nguồn mở

Nếu là “tín đồ” của lập trình Windows/.NET, bạn không thể không biết đến The Code Project, trang web hàng đầu cung cấp hàng chục nghìn bài báo/dự án về lập trình. Đây quả là kho dữ liệu phong phú với mọi lập trình viên. Còn gì tuyệt hơn khi bạn có thể tham khảo các bài báo của Code Project ngay trong VS?

Sau khi cài đặt, Code Project Browser sẽ tích hợp vào menu Tools của VS, bạn chỉ việc nhấn vào đây để bắt đầu. Chú ý là trong lần chạy đầu tiên, bạn cần nhập địa chỉ email và mật khẩu mà bạn đã đăng kí trên The Code Project.

Không chỉ là trình duyệt, công cụ này còn cho phép bạn mở mã nguồn mẫu của các bài báo chỉ với vài cú nhấn chuột. Thay vì phải tải về mã nguồn, giải nén và mở bằng VS, Code Project Browse làm tất cả việc này cho bạn.

Khi nhấn vào một link file .zip chứa mã nguồn, bạn sẽ được hỏi có mở dự án này không. Nếu có, add-in này sẽ tải dự án về, giải nén trong thư mục My Documents/ My Code Project Downloads và mở nó trong VS. Hơn thế nữa, bạn còn có thể quản lý các bài báo/dự án yêu thích trên Code Project thông qua công cụ này.

Bạn sẽ không phải duyệt Code Project bằng trình duyệt thông thường thêm một lần nào nữa! Bạn có thể tải và cài đặt add-in này từ địa chỉ http://www.codeproject.com/csharp/cpbrowser.asp (http://www.codeproject.com/csharp/cpbrowser.asp)

Ankhsvn: Quản lý phiên bản với Visual Studio

Trang chủ: http://ankhsvn.tigris.org (http://ankhsvn.tigris.org/) Loại dự án: Mã nguồn mở.

Nếu bạn làm việc theo nhóm trong các dự án lớn thì quản lý phiên bản mã nguồn là một công việc rất quan trọng. Có rất nhiều công cụ giúp bạn thực hiện việc này, nhưng nếu sử dụng chính Visual Studio thì sẽ tiện hơn nhiều. AnkhSVN là một bổ sung cho Visual Studio (từ phiên bản .NET 2002 trở đi) để hỗ trợ hệ thống quản lý mã nguồn Subversion. Nó cho phép bạn thực hiện hầu hết các thao tác quản lý phiên bản, ngay bên trong VS IDE. Hiện AnkhSVN chưa hỗ trợ tất cả các chức năng của SVN, nhưng phần lớn các thao tác sử dụng trong công việc hàng ngày đều đã được cung cấp.

AnknSVN phiên bản mới nhất là 1.0.1.2736 (stable), dung lượng 3.88 MB, tải về tại địa chỉ:

http://ankhsvn.tigris.org/servlets/ProjectDocumentList?folderID=7315 (http://ankhsvn.tigris.org/servlets/ProjectDocumentList?folderID=7315)

Chú ý là để sử dụng AnkhSVN bạn cần tải và cài đặt Subversion trước. Phiên bản hiện tại là 1.45, tải từ địa chỉ: http://subversion.tigris.org/project_packages.html (http://subversion.tigris.org/project_packages.html)

Code Style Enforcer: Chuẩn hóa mã nguồn

Trang chủ: http://joel.fjorden.se/static.php?page=CodeStyleEnforcer (http://joel.fjorden.se/static.php?page=CodeStyleEnforcer) Loại dự án: Miễn phí.

Chuẩn hóa mã nguồn theo những qui tắc nhất định luôn là yêu cầu bắt buộc đối với các lập trình viên chuyên nghiệp. Tuy nhiên, nếu không muốn bận tâm với việc này, bạn có thể để Code Style Enforcer làm việc đó cho bạn. Bổ sung này sẽ phát hiện những chỗ mã nguồn không hợp chuẩn và đưa ra phương án chỉnh sửa
Code Style Enforce là một plug-in của DXCode cho Visual Studio 2005, giúp kiểm tra mã nguồn thông qua các quy tắc đã được định nghĩa sẵn. Được phát triển cho C#, nhưng CSE có thể làm việc với VB .NET (chưa được kiểm tra). Chuẩn mã nguồn hiện có thể cấu hình với những quy tắc nhất định. Quy tắc mặc định dựa trên chuẩn viết mã C# của IDesign (http://www.idesign.net), đây là chuẩn viết mã hoàn chỉnh nhất hiện có và có thể tải về miễn phí.

Mỗi khi mở một dự án mới, Code Style Enforcer sẽ hỏi bạn sử dụng các quy tắc chung dành cho tất cả các dự án hay dành riêng cho từng dự án một. Điều này rất có ích nếu bạn cùng lúc tham gia làm nhiều dự án với nhiều nhóm khác nhau.

Code Style Enforcer sẽ gạch chân những biến, method không tuân theo quy tắc, tất cả những gì bạn cần làm là nhấn chuột phải và chọn Correct CSE Violation, với 2 tùy chọn nhỏ hơn: Sửa ngay lập tức theo những gì mà công cụ đề xuất, hay xem trước những thay đổi đối với mã của bạn (chỉ xuất hiện trong một số trường hợp):

Code Style Enforcer hiện chỉ chạy được trên Visual Studio 2005, do công cụ này hoàn toàn dựa trên .NET 2.0. Phiên bản mới nhất là 2.1.29, dung lượng 564KB. Cần chú ý, để cài đặt, trước tiên bạn cần tải và cài đặt bổ sung DXCore cho Visual Studio, phiên bản mới nhất là 2.5.1, dung lượng 17.3MB, tải về từ địa chỉ http://www.devexpress.com/Downloads/NET/IDETools/DXCore/ (http://www.devexpress.com/Downloads/NET/IDETools/DXCore/)

Pinvoke.net: Đơn giản hóa Windows API

Trang chủ: http://www.Pinvoke.net (http://www.devexpress.com/Downloads/NET/IDETools/DXCore/)Loại dự án: miễn phí (yêu cầu đăng kí)

Mặc dù .NET framework cung cấp hầu hết các hàm và công cụ mà lập trình viên cần, nhưng không tránh khỏi những khi phải “cầu viện” đến Windows APIs thông qua dịch vụ PInvoke. Việc này tuy không khó, nhưng đòi hỏi lập trình viên phải khai báo khá nhiêu khê và phức tạp. PInvoke.net là một plug-in giúp bạn giải quyết rắc rối này bằng cách truy cập đến trang web PInvoke.net để lấy các dữ liệu cần thiết về hàm API cần dùng và khai báo giúp bạn.

Sau khi cài đặt, PInvoke sẽ xuất hiện trong một menu riêng của Visual Studio. Khi cần sử dụng hàm API nào, bạn sẽ có 2 tùy chọn: Insert PInvoke signature: sử dụng các signature đã có sẵn của plug-in này hay là truy cập đến PInvoke.net để sử dụng các đoạn mã (có thể) chính xác và đầy đủ hơn.

PInvoke.Net tương thích với Visual Studio.NET 2003 (7.1) trở lên, chưa hỗ trợ VS 2008, dung lượng chỉ có 288 KB.

Power Toys Pack Installer: Luôn cập nhật Visual Studio của bạn

Trang chủ: http://www.codeplex.com/ (http://www.codeplex.com/) Loại dự án: mã nguồn mở.

Thay vì phải liên tục theo dõi những gói bổ sung cho Visual Studio từ trang web của Microsoft và các trang khác, tải về và cài đặt, Power Toys Pack Installer sẽ làm việc đó cho bạn. Chương trình kiểm tra các bổ sung mới hoặc được cập nhật. Tất cả những gì bạn cần làm chỉ là đánh dấu chọn bổ sung thích hợp với mình và nhấn Install! Power Toys Pack Installer sẽ tự động tải về và cài đặt cho bạn.

Chương trình hiện có phiên bản mới nhất 1.0.1, dung lượng 504 KB, không cần cài đặt. Tải về từ địa chỉ: http://www.codeplex.com/PackInstaller/Release/ProjectReleases.aspx?ReleaseId=4274 (http://www.codeplex.com/PackInstaller/Release/ProjectReleases.aspx?ReleaseId=4274)

GhostDoc: Tạo tài liệu từ mã nguồn

Trang chủ: http://www.roland-weigelt.de/ghostdoc (http://www.roland-weigelt.de/ghostdoc) .Loại dự án: miễn phí.

Với comment (chú thích) dạng XML của C#, việc tạo tài liệu từ mã nguồn trở nên dễ dàng hơn rất nhiều, và có không ít công cụ giúp bạn thực hiện công việc đó. Tuy nhiên, chính việc comment theo định dạng XML chưa chắc đã “thú vị”. GhostDoc sẽ giải phóng bạn khỏi sự “nhàm chán”.

Công cụ này cho phép tự động tạo comment theo định dạng XML cho một method từ kiểu, tham số, tên của nó cũng như các thông tin ngữ cảnh khác. Tất cả những việc bạn cần làm là chọn một method cần chú thích, nhấn chuột phải và chọn Document This. Điều tuyệt vời là GhostDoc cho phép bạn thiết lập định dạng Comment và những quy tắc đoán tên kiểu, tham số… cho phù hợp với phong cách viết mã của bạn.

Sau khi cài đặt, GhostDoc sẽ bổ sung thêm vào Menu Tools của VS một Submenu với 2 mục nhỏ hơn: Configure GhostDoc cho phép cấu hình GhostDoc và Document This (đôi khi sẽ disable) sẽ tự động comment cho file .cs hiện tại.

Mặc dù GhostDoc không phải lúc nào cũng làm việc chính xác, nhưng công cụ này rất đáng cho bạn sử dụng.

Hiện phiên bản mới nhất của GhostDoc là 2.1.1, dung lượng 898KB. Có 2 phiên bản dành riêng cho Visual Studio 2005 và 2008, đều hỗ trợ VB (có hạn chế) và Windows Vista. Phiên bản gần nhất dành cho VS 2003 là 1.30, chỉ hỗ trợ C# và không hỗ trợ Vista.

CodeKeep: Quản lý và chia sẻ Codesnippets

Trang chủ: http://www.codekeep.net (http://www.codekeep.net/) .Loại dự án: Miễn phí.

Một trong những thế mạnh của Visual Studio chính là Codesnippets, giúp lập trình viên sử dụng các đoạn mã có sẵn một cách nhanh chóng, thay vì phải code một cách thủ công.

CodeKeep nâng Code Snippets lên một tầm cao mới khi cho phép các lập trình viên trao đổi, chia sẻ các Snippet mà mình tạo ra. Điều này giúp bạn tiết kiệm được rất nhiều thời gian và sức lực, đồng thời đặc biệt hữu ích cho các bạn lập trình viên trẻ muốn học hỏi kinh nghiệm từ các “Guru”.

Để có thể sử dụng CodeKeep, bạn phải tạo một account mới tại trang chủ của dự án này. Account này cho phép bạn tải lên và chia sẻ các snippet dễ dàng hơn.
CodeKeep sẽ thêm một submenu với 5 tính năng thiết lập, quản lý, duyệt, tìm kiếm và bổ sung để bạn làm việc với snippet. Trong đó đáng kể nhất là chức năng tìm kiếm. Mỗi khi cần bổ sung đoạn mã làm công việc nhất định mà không muốn phải tự viết code, bạn có thể sử dụng tính năng này để tìm kiếm các snippet thực hiện công việc tương tự. Chỉ việc gõ từ khóa, chẳng hạn Shutdown Windows, nhấn nút Go, đợi trong giây lát và chọn snippet phù hợp để sử dụng. Thật là tiện lợi!

Phiên bản mới nhất của công cụ này là 2.5, chỉ tương thích với Visual Studio 2005, dung lượng 85KB, tải tại địa chỉ: http://www.codekeep.net/downloads/CodeKeepVS2005Addin2.5.zip (http://www.codekeep.net/downloads/CodeKeepVS2005Addin2.5.zip).

Lời kết: trên đây chỉ là những bổ sung phổ biến nhất cho Visual Studio. Chắc chắn còn nhiều bổ sung hữu ích khác mà bài viết chưa nói tới, rất mong được các bạn trao đổi, chia sẻ kinh nghiệm. Ngoài ra, các bổ sung tuy hay, nhưng đồng thời chúng cũng làm Visual Studio nặng nề hơn, dễ bị “crash” hơn, vì vậy các bạn nên thận trọng và chỉ sử dụng các bổ sung thật sự cần thiết với mình.

Posted in .Net. Tags: . Leave a Comment »

Rules for Good Program Design

If you are doing it twice, you are doing it wrong. Every piece of code should be designed only once. When setting to a new project, look for places to reuse your previous code. Allow old code to be modified to serve multiple purposes. As you work in any language, you should be building a large toolbox of subroutines that you can bring to bear on each new task.

Borrow code. When able, pick a programming language that is popular, so you can go download code that others have already created. Check around before you commit hours to writing code that someone else probably already wrote.

Make the computer do the work for you. Work to make any repetitive task into an automated one. Some tasks will actually take longer to accomplish when not done manually, but over time you will recoup those lost minutes when performing similar tasks.

Find short cuts. If you expect that you will type the same command over and over, make an alias for it that is one or two characters long. Use an editor that allow easy insertion of long variable and command names. Fewer keystrokes, even for fast typists, can save a lot of time.

Make code legible. Make variable names and procedure names long and clear. Usually variables should be nouns, procedures should be verbs, and procedures that return a value of importance should be nouns. This is a critical part of good internal documentation. The time saved in making short variable and procedure names will be lost many times over when trying to read the code later. Add comments that say why you are doing what you are doing, and make sure that your programs are clear enough about how you are doing it.

Find elegant solutions. Perhaps the hardest part of good programming is creating abstract data structures and programs that can handle any problem that is likely to be presented. Code that has exceptions for unusual requests are difficult to maintain and fail to scale well when those unusual requests become common tasks.

Test. Always test out changed code in many different situations. Consider how a change might affect other programs. Make all your tests automated, or you will not want to run them all the time.

There is no good writing, only good rewriting. Read over your code frequently to see how it can be improved. After a project has matured, a full rewrite will often sweep out all old bugs and accurately capture the evolved program requirements. Get other good programmers to read your code and offer suggestion. If you would be embarrassed to have your work critiqued, get busy rewriting it.

Creating Custom Exceptions in .NET

http://blog.gurock.com/articles/creating-custom-exceptions-in-dotnet/

Introduction
Minimal Exceptions Types
Serialization Basics
Customizing Serialization
Closing Words

Introduction

Although the .NET framework contains all kinds of exception types which are sufficient in most cases, it can make sense to define custom exceptions in your own applications. They can greatly simplify and improve the error handling and thus increase the overall code quality. Whatever your reasons are for using custom exceptions, this article shows you how to create them and what to pay attention to when it comes to serialization, .NET guidelines and analysis tools.

System or Application?

Any custom exception you create needs to derive from the System.Exception class. You can either derive directly from it or use an intermediate exception like SystemException or ApplicationException as base class. According to the .NET framework documentation, SystemException is meant only for those exceptions defined by the common language runtime whereas ApplicationException is intended to be used by user code:

“ApplicationException is thrown by a user program, not by the common language runtime. If you are designing an application that needs to create its own exceptions, derive from the ApplicationException class. ApplicationException extends Exception, but does not add new functionality. This exception is provided as means to differentiate between exceptions defined by applications versus exceptions defined by the system.”

So, ApplicationException seems like a good choice as base class for custom exceptions at first. But there has been quite a lot of discussion about this topic and according to this posting by a Microsoft employee working on .NET, the usage of ApplicationException is no longer recommended. It seems that the current .NET framework and its documentation is already outdated when it comes to the ApplicationException class:

“We added ApplicationException thinking it would add value by grouping exceptions declared outside of the .NET Framework, but there is no scenario for catching ApplicationException and it only adds unnecessary depth to the hierarchy. […] You should not define new exception classes derived from ApplicationException; use Exception instead. In addition, you should not write code that catches ApplicationException.”

So, the ApplicationException exception class will eventually be deprecated. I therefore let the custom exceptions here in this article derive directly from the Exception class.

Minimal Exceptions Types

The absolute minimum a new custom exception class needs to have is a name. Let’s say you are designing the login mechanism for a database application and as part of this job you need to create a custom exception which is thrown if a login attempt fails. A good name for such an exception would be LoginFailedException. An absolute minimum implementation in C# then looks like:

public class LoginFailedException: System.Exception
{
}

As you can see, all you need to do to create a basic custom exception is to derive from the Exception class. There’s only one problem with this definition. Since C# unfortunately doesn’t inherit constructors of base classes, this new type only has the standard constructor with no parameters and is therefore relatively useless. So, we need to add at least one constructor which does something useful:

public class LoginFailedException: System.Exception
{
   // The default constructor needs to be defined
   // explicitly now since it would be gone otherwise.

   public LoginFailedException()
   {
   }

   public LoginFailedException(string message): base(message)
   {
   }
}

By now we can use our custom exception like most other exceptions. We can throw an instance of LoginFailedException and pass a message which describes the occurred error. But .NET exceptions can do more. You can normally pass a so called inner exception to one of the constructors which indicates that the created exception is a direct result of a previous one. This inner exception can then be retrieved via the InnerException property. This way you can build entire exceptions chains. Since this can be quite useful sometimes, we extend our existing implementation with this additional constructor:

public class LoginFailedException: System.Exception
{
   // …

   public LoginFailedException(string message,
      Exception innerException): base(message, innerException)
   {
   }
}

Looks good so far. We now have a working custom exception which is capable of handling error messages and inner exceptions. Great!

Serialization Basics

Let’s now have a look at what FxCop says to our exception type. In case you don’t know what FxCop is, it’s a nice analysis tool which checks .NET assemblies for conformance to the .NET framework design guidelines. FxCop doesn’t seem to like our exception yet:

  • Error for ImplementStandardExceptionConstructors:
    Add the following constructor:

    LoginFailedException(SerializationInfo, StreamingContext)

  • Error for MarkISerializableTypesWithSerializable:
    Add [Serializable] as this type implements ISerializable

Doesn’t look that great anymore, does it? However, the good news is that this can be fixed quite easily. Both errors deal with the serialization mechanism of .NET in some way. In case you don’t know what serialization is, it’s basically a way to convert an object into a form which can easily be transported or persisted. The counter part to serialization is the deserialization which is responsible for restoring the original object.

Let’s begin with the second error. Any class that is intended to be serializable needs to be marked with the Serializable attribute and since our base class Exception implements the ISerializable interface which is used to customize the serialization process, FxCop wonders why we didn’t add it. So, in order to comply with the serialization guidelines, we simply need to add the Serializable attribute and the second error is already fixed:

[Serializable]
public class LoginFailedException: System.Exception
{
   // …
}

Correcting the first error is a bit more tedious. FxCop complains that we forgot to add an additional standard exception constructor. This constructor is used to customize the serialization process of objects. And since the base Exception class defines such a constructor and C# still lacks the support of constructor inheritance, we are better off adding such a beast:

[Serializable]
public class LoginFailedException: System.Exception
{
   // …

   protected LoginFailedException(SerializationInfo info,
      StreamingContext context): base(info, context)
   {
   }
}

Looks good. FxCop keeps quiet now and we finally have a fully functional exception type which can be used like any predefined exception. It can handle simple error messages and inner exceptions and can even be serialized and deserialized correctly.

Customizing Serialization

Let’s pretend we want to extend our exception by being able to store no only the error message why the login attempt failed, but also which username caused the error. This is pretty straightforward. We just add a new field and a new property to our existing class:

[Serializable]
public class LoginFailedException: System.Exception
{
   private string fUserName;

   // …

   public string UserName
   {
      get { return this.fUserName; }
      set { this.fUserName = value; }
   }
}

We are now able to set and get the name of the user specified during the login sequence. So far so good, but what about the serialization now? Does it still work as expected, that is, will the username be preserved when being serialized and deserialized? Unfortunately, the answer is ‘no’. You need to take care of it yourself.

Before we extend our exception to do exactly this, I tell you how the customization of the serialization and deserialization works in general. At first, you need to implement the ISerializable interface. This interface contains a single method, named GetObjectData, which is responsible for storing object fields during the serialization process. To do this, you just need to populate a passed SerializationInfo object.

The deserialization process works exactly the other way round. When an object is being deserialized, the previously filled SerializationInfo object is passed to the constructor of your class in which you are responsible for restoring the original object fields. Translated to our example exception, this looks like:

[Serializable]
public class LoginFailedException: System.Exception
{
   private string fUserName;

   // …

   protected LoginFailedException(SerializationInfo info,
      StreamingContext context): base(info, context)
   {
      if (info != null)
      {
         this.fUserName = info.GetString(”fUserName”);
      }
   }

   public override void GetObjectData(SerializationInfo info,
      StreamingContext context)
   {
      base.GetObjectData(info, context);

      if (info != null)
      {
         info.AddValue(”fUserName”, this.fUserName);
      }
   }
}

Let’s begin with the serialization. Since our base Exception class already implements the ISerializable interface we can omit it in the list of implemented interfaces and base classes and only need to override and fill the GetObjectData method. At first, we call the GetObjectData method of the base class. This is needed to save the fields common to all exceptions, like the error message, stacktrace information or the inner exception. Then we simply store our username by adding it to the passed SerializationInfo object.

The deserialization works similarly. We just restore our username field in the constructor with the aid of the previously filled SerializationInfo object. To verify that our custom exception works like expected and to demonstrate how an entire serialization and deserialization process looks like, I wrote the following unit test:

using System.Runtime.Serialization.Formatters.Binary;

// …

[Test]
public void TestUserName()
{
   LoginFailedException e = new LoginFailedException();
   e.UserName = “dotnet user”;

   using (Stream s = new MemoryStream())
   {
      BinaryFormatter formatter = new BinaryFormatter();
      formatter.Serialize(s, e);
      s.Position = 0; // Reset stream position
      e = (LoginFailedException) formatter.Deserialize(s);
   }

   Assert.AreEqual(e.UserName, “dotnet user”);
}

// …

During the serialization and deserialization process, the username field is now preserved correctly and thus the Assert class remains silent and the test passes. That this is not the case if we haven’t customized the serialization process can be confirmed quite easily. Simply comment out the GetObjectData method and the call to GetString in the constructor, rerun the test and you’ll see it failing since the UserName property will now return null instead of the correct username.

Closing Words

Aside from the ApplicationException versus Exception discussion, this article has been written with the .NET 1.1 version in mind. However, I don’t expect upcoming versions of the .NET framework to differ (greatly) from the techniques described here in this article, so that the information given here should still be of value in the future. As usual, if you have any comments or questions feel free to contact me at tg@gurock.com. Oh, and by the way, you can download the source of the LoginFailedException class and its test here.

Exception Handling Best Practices in .NET

(http://www.codeproject.com/dotnet/exceptionbestpractices.asp)

Contents

Introduction

“My software never fails”. Can you believe it? I’m almost hearing you all, screaming that I’m a liar. “Software that never fails is something near to impossible!”

Contrary to common belief, creating reliable, robust software is not something near to impossible. Notice that I’m not referring to bug-free software, intended to control nuclear power plants. I’m referring to common business software, which can run unattended on a server, or even a desktop machine, for long periods of time (weeks or months) and work predictably without any significant glitch. By predictably, I mean that it has a low failure rate, you can easily understand failure conditions to fix it quickly, and it never damages data in response of an external failure.

In other words, software that is stable.

Having a bug in your software is forgivable, and even expected. What’s unforgivable is having a recurring bug you can’t fix because you don’t have enough information.

To understand better what I’m saying, I’ve seen countless business software that, in an out of disk space error in the DBMS, reports something like this:

“Could not update customer details. Contact the system administrator and try again later”.

While this message may be an adequate of reporting an unknown resource failure to a business user, all too often this is the whole debugging information that is available to debug the error cause. Nothing was logged, and understanding what is happening will be time-consuming and often programmers will guess a lot of possible causes until they find the real error cause.

Notice that in this article, I will concentrate only in how to make a better use of .NET exceptions: I won’t discuss how to properly report error messages, because I believe this belongs to UI domain, and it depends heavily on the interface being developed and the target audience; a blog text editor targeting teenagers should report error messages in a way completely different than a socket server, which will only be used directly by programmers.

Plan for the worst

A few basic design concepts will make your program much more robust, and will improve the user experience in the presence of an unexpected error. What do I mean by “improve the user experience in the presence of an unexpected error”? It’s not that the user will be thrilled by the marvelous dialog box you’ll show him. It’s more about don’t corrupting data, don’t crashing the computer, and behaving safely. If your program can pass through an out of disk space error without doing any harm, you improved the user experience.

Check it early

Strong type checking and validation are powerful tools to prevent unexpected exceptions and to document and test code. The earlier in execution you detect a problem, the easier is to fix. Trying to understand what a CustomerID is doing on the ProductID column on the InvoiceItems table after a few months isn’t fun neither easy. If you used classes for storing customer data, instead of using primitives (e.g., int, string, etc), chances are that the compiler would never allow you to do such a thing.

Don’t trust external data

External data is not reliable. It must be extensively checked. It doesn’t matter if the data is coming from the registry, database, from a disk, from a socket, from a file you just wrote or from the keyboard. All external data should be checked and only then you can rely on it. All too often I see programs that trust configuration files because the programmer never thought that someone would edit the file and corrupt it.

The only reliable devices are: the video, the mouse and keyboard.

Anytime you need external data, you can have the following situations:

  • Not enough security privileges
  • The information is not there
  • The information is incomplete
  • The information is complete, but invalid

It really doesn’t matter if it’s a registry key, a file, a socket, a database, a web service or a serial port. All external data sources will fail, sooner or later. Plan for a safe failure and minimize damage.

Writes can fail, too

Unreliable data sources are also unreliable data repositories. When you’re saving data, similar situations can happen:

  • Not enough security privileges
  • The device isn’t there
  • There’s not enough space
  • The device has a physical fault

That’s why compression programs create a temporary file and rename it after they’re done, instead of changing the original one: if the disk (or even the software) fails for some reason, you won’t lose your original data.

Code Safely

A friend of mine often says: “A good programmer is someone who never introduce bad code in his projects”. I don’t believe that this is all that it’s needed to be a good programmer, but surely it will put you almost there. Below, I compiled a list of the most common “bad code” that you can introduce in your projects, when it comes to exception handling.

Don’t throw new Exception()

Please, don’t do it. Exception is a too broad class, and it’s hard to catch without side-effects. Derive your own exception class, but derive it from ApplicationException. This way you could set a specialized exception handler for exceptions thrown by the framework and another for exceptions thrown by yourself.Revision note: David Levitt wrote me, in the comments section below, that although Microsoft still touts using System.ApplicationException as a base class in MSDN docs, this is no longer considered a good practice, as noted by Brad Adams, as you can see in his blog. The idea is creating exception class hierarchies that are as shallow and wide as possible, as you often do with class hierarchies. The reason I didn’t change the article immediately was because I needed to do more research before I introduced it here. After all this research, I could not decide yet whether shallow class hierarchies are a good idea in Exception handling or not, so I decided to leave both opinions here. But, no matter what you do, don’t throw new Exception() and derive your own Exception class when needed.

Don’t put important exception information on the Message field

Exceptions are classes. When you return the exception information, create fields to store data. If you fail on doing it, people will need to parse the Message field to get the information they need. Now, think what will happen to the calling code if you need to localize or even just correct a spelling error in error messages. You may never know how much code you’ll break by doing it.

Put a single catch (Exception ex) per thread

Generic exception handling should be done in a central point in your application. Each thread needs a separate try/catch block, or you’ll lose exceptions and you’ll have problems hard to understand. When an application starts several threads to do some background processing, often you create a class for storing processing results. Don’t forget to add a field for storing an exception that could happen or you won’t be able to communicate it to the main thread. In “fire and forget” situations, you probably will need to duplicate the main application exception handler on the thread handler.

Generic Exceptions caught should be published

It really doesn’t matter what you use for logging – log4net, EIF, Event Log, TraceListeners, text files, etc. What’s really important is: if you caught a generic Exception, log it somewhere. But log it only once – often code is ridden with catch blocks that log exceptions and you end up with a huge log, with too much repeated information to be useful.

Log Exception.ToString(); never log only Exception.Message!

As we’re talking about logging, don’t forget that you should always log Exception.ToString(), and never Exception.Message. Exception.ToString() will give you a stack trace, the inner exception and the message. Often, this information is priceless and if you only log Exception.Message, you’ll only have something like “Object reference not set to an instance of an object”.

Don’t catch (Exception) more than once per thread

There are rare exceptions (no pun intended) to this rule. If you need to catch an exception, always use the most specific exception for the code you’re writing.

I always see beginners thinking that good code is code that doesn’t throw exceptions. This is not true. Good code throws exceptions as needed, and handles only the exceptions it knows how to handle.

As a sample of this rule, look at the following code. I bet that the guy who wrote it will kill me when he read this, but it was taken from a real-world example. Actually, the real-world code was a bit more complicated – I simplified it a lot for didactic reasons.

The first class (MyClass) is on an assembly, and the second class (GenericLibrary) is on another assembly, a library full of generic code. On the development machine the code ran right, but on the QA machines, the code always returned “Invalid number”, even if the entered number was valid.

Can you say why this was happening?

public class MyClass
{
    public static string ValidateNumber(string userInput)
    {
        try
        {
            int val = GenericLibrary.ConvertToInt(userInput);
            return "Valid number";
        }
        catch (Exception)
        {
            return "Invalid number";
        }
    }
}

public class GenericLibrary
{
    public static int ConvertToInt(string userInput)
    {
        return Convert.ToInt32(userInput);
    }
}

The problem was the too generic exception handler. According to the MSDN documentation, Convert.ToInt32 only throws ArgumentException, FormatException and OverflowException. So, those are the only exceptions that should be handled.

The problem was on our setup, which didn’t include the second assembly (GenericLibrary). Now, we had a FileNotFoundException when the ConvertToInt was called, and the code assumed that it was because the number was invalid.

The next time you write “catch (Exception ex)“, try to describe how your code would behave when an OutOfMemoryException is thrown.

Don’t ever swallow exceptions

The worst thing you can do is catch (Exception) and put an empty code block on it. Never do this.

Cleanup code should be put in finally blocks

Ideally, since you’re not handling a lot of generic exceptions and have a central exception handler, your code should have a lot more finally blocks than catch blocks. Never do cleanup code, e.g., closing streams, restoring state (as the mouse cursor), outside of a finally block. Make it a habit.

One thing that people often overlook is how a try/finally block can make your code both more readable and more robust. It’s a great tool for cleanup code.

As a sample, suppose you need to read some temporary information from a file and return it as a string. No matter what happens, you need to delete this file, because it’s temporary. This kind of return & cleanup begs for a try/finally block.

Let’s see the simplest possible code without using try/finally:

string ReadTempFile(string FileName)
{
    string fileContents;
    using (StreamReader sr = new StreamReader(FileName))
    {
        fileContents = sr.ReadToEnd();
    }
    File.Delete(FileName);
    return fileContents;
}

This code also has a problem when an exception is thrown on, e.g., the ReadToEnd method: it leaves a temporary file on the disk. So, I’ve actually saw some people trying to solve it coding as this:

string ReadTempFile(string FileName)
{
    try
    {
        string fileContents;
        using (StreamReader sr = new StreamReader(FileName))
        {
            fileContents = sr.ReadToEnd();
        }
        File.Delete(FileName);
        return fileContents;
    }
    catch (Exception)
    {
        File.Delete(FileName);
        throw;
    }
}

The code is becoming complex and it’s starting to duplicate code.

Now, see how much cleaner and robust is the try/finally solution:

string ReadTempFile(string FileName)
{
    try
    {
        using (StreamReader sr = new StreamReader(FileName))
        {
            return sr.ReadToEnd();
        }
    }
    finally
    {
        File.Delete(FileName);
    }
}

Where did the fileContents variable go? It’s not necessary anymore, because we can return the contents and the cleanup code executes after the return point. This is one of the advantages of having code that can run after the function returns: you can clean resources that may be needed for the return statement.

Use “using” everywhere

Simply calling Dispose() on an object is not enough. The using keyword will prevent resource leaks even on the presence of an exception.

Don’t return special values on error conditions

There are lots of problems with special values:

  • Exceptions makes the common case faster, because when you return special values from methods, each method return needs to be checked and this consumes at least one processor register more, leading to slower code
  • Special values can, and will be ignored
  • Special values don’t carry stack traces, and rich error details
  • All too often there’s no suitable value to return from a function that can represent an error condition. What value would you return from the following function to represent a “division by zero” error?
public int divide(int x, int y)
{
    return x / y;
}

Don’t use exceptions to indicate absence of a resource

Microsoft recommends that you should use return special values on extremely common situations. I know I just wrote the opposite and I also don’t like it, but life is easier when most APIs are consistent, so I recommend you to adhere to this style with caution.

I looked at the .NET framework, and I noticed that the almost only APIs that use this style are the APIs that return some resource (e.g., Assembly.GetManifestStream method). All those APIs return null in case of the absence of some resource.

Don’t use exception handling as means of returning information from a method

This is a bad design. Not only exceptions are slow (as the name implies, they’re meant only to be used on exceptional cases), but a lot of try/catch blocks in your code makes the code harder to follow. Proper class design can accommodate common return values. If you’re really in need to return data as an exception, probably your method is doing too much and needs to be split.

Use exceptions for errors that should not be ignored

I’ll use a real world example for this. When developing an API so people could access Crivo (my product), the first thing that you should do is calling the Login method. If Login fails, or is not called, every other method call will fail. I chose to throw an exception from the Login method if it fails, instead of simply returning false, so the calling program cannot ignore it.

Don’t clear the stack trace when re-throwing an exception

The stack trace is one of the most useful information that an exception carries. Often, we need to put some exception handling on catch blocks (e.g., to rollback a transaction) and re-throw the exception. See the right (and the wrong) way of doing it: The wrong way:

try
{
    // Some code that throws an exception
}
catch (Exception ex)
{
    // some code that handles the exception
    throw ex;
}

Why is this wrong? Because, when you examine the stack trace, the point of the exception will be the line of the “throw ex;“, hiding the real error location. Try it.

try
{
    // Some code that throws an exception
}
catch (Exception ex)
{
    // some code that handles the exception
    throw;
}

What has changed? Instead of “throw ex;“, which will throw a new exception and clear the stack trace, we have simply “throw;“. If you don’t specify the exception, the throw statement will simply rethrow the very same exception the catch statement caught. This will keep your stack trace intact, but still allows you to put code in your catch blocks.

Avoid changing exceptions without adding semantic value

Only change an exception if you need to add some semantic value to it – e.g., you’re doing a DBMS connection driver, so the user doesn’t care about the specific socket error and wants only to know that the connection failed.

If you ever need to do it, please, keep the original exception on the InnerException member. Don’t forget that your exception handling code may have a bug too, and if you have InnerException, you may be able to find it easier.

Exceptions should be marked [Serializable]

A lot of scenarios needs that exceptions are serializable. When deriving from another exception class, don’t forget to add that attribute. You’ll never know when your method will be called from Remoting components or Web Services.

When in doubt, don’t Assert, throw an Exception

Don’t forget that Debug.Assert is removed from release code. When checking and doing validation, it’s often better to throw an Exception than to put an assertion in your code.

Save assertions for unit tests, for internal loop invariants, and for checks that should never fail due to runtime conditions (a very rare situation, if you think about it).

Each exception class should have at least the three original constructors

Doing it is easy (just copy & paste the definitions from other exception classes) and failing to do that won’t allow users of your classes to follow some of these guidelines.

Which constructors I am referring to? The last three constructors described on this page.

Be careful when using the AppDomain.UnhandledException event

Revision note: I was pointed by Phillip Haack in my blog of this important omission. Other common source of mistakes is the Application.ThreadException event. There are lots of caveats when using them:

  • The exception notification occurs too late: when you receive the notification your application will not be able to respond to the exception anymore.
  • The application will finish if the exception occurred on the main thread (actually, any thread that started from unmanaged code).
  • It’s hard to create generic code that works consistently. Quoting MSDN: “This event occurs only for the application domain that is created by the system when an application is started. If an application creates additional application domains, specifying a delegate for this event in those applications domains has no effect.”
  • When the code is running those events, you won’t have access to any useful information other than the exception itself. You won’t be able to close database connections, rollback transactions, nor anything useful. For beginners, the temptation of using global variables will be huge.Indeed, you should never base your whole exception handling strategy on those events. Think of them as “safety nets”, and log the exception for further examination. Later, be sure to correct the code that is not handling properly the exception.

Don’t reinvent the wheel

There are lots of good frameworks and libraries to deal with exceptions. Two of them are provided by Microsoft and I mention here:

Notice, though, that if you don’t follow strict design guidelines, like those I showed here, those libraries will be nearly useless.

VB.NET

If you read through this article, you’ll notice that all the samples I used were in C#. That’s because C# is my preferred language, and because VB.NET has a few guidelines of its own.

Emulate C# “using” statement

Unfortunately, VB.NET still doesn’t have the using statement. Whidbey will have it, but until it’s released, everytime you need to dispose an object, you should use the following pattern:

Dim sw As StreamWriter = Nothing
Try
    sw = New StreamWriter("C:\crivo.txt")
    ' Do something with sw
Finally
    If Not sw is Nothing Then
        sw.Dispose()
    End if
End Finally

If you’re doing something different when calling Dispose, probably you’re doing something wrong, and your code can fail and/or leak resources.

Don’t use Unstructured Error Handling

Unstructured Error Handling is also known as On Error Goto. Prof. Djikstra did it very well in 1974 when he wrote “Go To statement considered harmful”. It was more than 30 years ago! Please, remove all traces of Unstructured Error Handling from your application as soon as possible. On Error Goto statements will bite you, I’ll assure you.

Conclusion

I hope that this article helps someone to code better. More than a closed list of practices, I hope that this article be a starting point for a discussion of how to deal with exceptions in our code, and how to make our programs more robust.

I can’t believe that I wrote all of this without any mistake or controversial opinion. I’d love to hear your opinion and suggestions about this topic.

History

  • 9 Feb 2005 – Initial version
  • 21 Feb 2005 – Added information about ApplicationException, AppDomain.UnhandledException and Application.ThreadException

SQL Server DO’s and DON’Ts

SQL Server DO’s and DON’Ts

So, you are now the leader of a SQL Server based project and this is your first one, perhaps migrating from Access. Or maybe you have performance problems with your SQL Server and don’t know what to do next. Or maybe you simply want to know of some design guidelines for solutions using SQL Server and designing Database Access Layers (DAL): this article is for you.

Even if you are not using SQL Server, most of these design guidelines apply to other DBMS, too: Sybase is a very similar environment for the programmer, and Oracle designs may benefit from this too. I won’t show here how to use specific T-SQL tricks, nor won’t give you miracle solutions for your SQL Server problem. This is by no means a complete, closed issue. What I intend to do is give you some advices for a sound design, with lessons learned through the last years of my life, seeing the same design errors being done again and again.

DO know your tools.

Please, don’t underestimate this tip. This is the best of all of those you’ll see in this article. You’d be surprised of how many SQL Server programmers don’t even know all T-SQL commands and all of those effective tools SQL Server has.

“What? I need to spend a month learning all those SQL commands I’ll never use???” you might say. No, you don’t need to. But spend a weekend at MSDN and browse through all T-SQL commands: the mission here is to learn a lot of what can and what can’t be done. And, in the future, when designing a query, you’ll remember “Hey, there’s this command that does exactly what I need”, and then you’ll refer again to MSDN to see its exact syntax.

In this article I’ll assume that you already know the T-SQL syntax or can find about it on MSDN.

DON’T use cursors

Let me say it again: DON’T use cursors. They should be your preferred way of killing the performance of an entire system. Most beginners use cursors and don’t realize the performance hit they have. They use memory; they lock tables in weird ways, and they are slow. Worst of all, they defeat most of the performance optimization your DBA can do. Did you know that every FETCH being executed has about the same performance of executing a SELECT? This means that if your cursor has 10,000 records, it will execute about 10,000 SELECTs! If you can do this in a couple of SELECT, UPDATE or DELETE, it will be much faster.

Beginner SQL programmers find in cursors a comfortable and familiar way of coding. Well, unfortunately this lead to bad performance. The whole purpose of SQL is specifying what you want, not how it should be done.

I’ve once rewritten a cursor-based stored procedure and substituted some code for a pair of traditional SQL queries. The table had only 100,000 records and the stored procedure used to take 40 minutes to process. You should see the face of the poor programmer when the new stored procedure took 10 seconds to run!

Sometimes it’s even faster to create a small application that gets all the data, proccess it and update the server. T-SQL was not done with loop performance in mind.

If you are reading this article, I need to mention: there is no good use for cursors; I have never seen cursors being well used, except for DBA work. And good DBAs, most of the time, know what they are doing. But, if you are reading this, you are not a DBA, right?

DO normalize your tables

There are two common excuses for not normalizing databases: performance and pure laziness. You’ll pay for the second one sooner or later; and, about performance, don’t optimize what’s not slow. Often I see programmers de-normalizing databases because “this will be slow”. And, more frequent than the inverse, the resulting design is slower. DBMSs were designed to be used with normalized databases, so design with normalization in mind.

DON’T SELECT *

This is hard to get used, I know. And I confess: often I use it; but try to specify only the columns you’ll need. This will:

  1. Reduce memory consumption and network bandwidth
  2. Ease security design
  3. Gives the query optimizer a chance to read all the needed columns from the indexes

DO know how your data will be/is being acessed

A robust index design is one of the good things you can do for your database. And doing this is almost an art form. Everytime you add an index to a table, things get faster on SELECT, but INSERT and DELETE will be much slower. There’s a lot of work in building and mantaining indexes. If you add several indexes to a table to speed your SELECT, you’ll soon notice locks being held for a long time while updating indexes. So, the question is: what is being done with this table? Reading or Updating data? This question is tricky, specially with the DELETE and UPDATE, because they often involve a SELECT for the WHERE part and after this they update the table.

DON’T create an index on the “Sex” column

This is useless. First, let’s understand how indexes speed up table access. You can see indexes as a way of quickly partitioning a table based on a criteria. If you create an index with a column like “Sex”, you’ll have only two partitions: Male and Female. What optimization will you have on a table with 1,000,000 rows? Remember, mantaining an index is slow. Always design your indexes with the most sparse columns first and the least sparse columns last, e.g, Name + Province + Sex.

DO use transactions

Specially on long-running queries. This will save you when things get wrong. Working with data for some time you’ll soon discover some unexpected situation which will make your stored procured crash.

DO beware of deadlocks

Always access your tables on the same order. When working with stored procedures and transactions, you may find this soon. If you lock the table A then table B, always lock them in this very same order in all stored procedures. If you, by accident, lock the table B and then table A in another procedure some day you’ll have a deadlock. Deadlocks can be tricky to find if the lock sequence is not carefully designed.

DON’T open large recordsets

A common request on programming forums is: “How can I quickly fill this combo with 100,00 items?”. Well, this is an error. You can’t and you shouldn’t. First, your user will hate browsing through 100,000 records to find the right one. A better UI is needed here, because you should ideally show no more that 100 or 200 records to your users.

DON’T use server side cursors

Unless you know what your are doing. Client side cursors often (not always) put less overhead on the network and on the server, and reduce locking time.

DO use parametrized queries

Sometimes I see in programming forums, questions like: “My queries are failing with some chars, e.g. quotes. How can I avoid it?”. And a common answer is: “Replace it by double quotes”. Wrong. This is only a workaround and will still fail with other chars, and will introduce serious security bugs. Besides this, it will trash the SQL Server caching system, which will cache several similar queries, instead of caching only one. Learn how to use parameterized queries (in ADO, through the use of the Command Object, or in ADO.NET the SqlCommand) and never have these problems again.

DO always test with large databases

It’s a common pattern programmers developing with a small test database, and the end user using large databases. This is an error: disk is cheap, and performance problems will only be noticed when it’s too late.

DON’T import bulk data with INSERT

Unless strictly necessary. Use DTS or the BCP utility and you’ll have both a flexible and fast solution.

DO beware of timeouts

When querying a database, the default timeout is often low, like 15 seconds or 30 seconds. Remember that report queries may run longer than this, specially when your database grows.

DON’T ignore simultaneous editing

Sometimes two users will edit the same record at the same time. When writing, the last writer wins and some of the updates will be lost. It’s easy to detect this situation: create a timestamp column and check it before you write. If possible, merge changes. If there is a conflict, prompt the user for some action.

DON’T do SELECT max(ID) from Master when inserting in a Detail table.

This is another common mistake, and will fail when two users are inserting data at the same time. Use one of SCOPE_IDENTITY, IDENT_CURRENT, and @@IDENTITY. Avoid @@IDENTITY if possible because it can introduce some nasty bugs with triggers.

DO Avoid NULLable columns

When possible. They consume an extra byte on each NULLable column in each row and have more overhead associated when querying data. The DAL will be harder to code, too, because everytime you access this column you’ll need to check

I’m not saying that NULLs are the evil incarnation, like some people say. I believe they can have good uses and simplify coding when “missing data” is part of your business rules. But sometimes NULLable columns are used in situations like this:

CustomerName1
CustomerAddress1
CustomerEmail1
CustomerName2
CustomerAddress2
CustomerEmail3
CustomerName1
CustomerAddress2
CustomerEmail3

This is horrible. Please, don’t do this, normalize your table. It will be more flexible and faster, and will reduce the NULLable columns.

DON’T use the TEXT datatype

Unless you are using it for really large data. The TEXT datatype is not flexible to query, is slow and wastes a lot of space if used incorrectly. Sometimes a VARCHAR will handle your data better.

DON’T use temporary tables

Unless strictly necessary. Often a subquery can substitute a temporary table. They induce overhead and will give you a big headache when programming under COM+ because it uses a database connection pool and temporary tables will last forever. In SQL Server 2000, there are alternatives like the TABLE data type which can provide in-memory solutions for small tables inside stored procedures too.

DO learn how to read a query execution plan

The SQL Server query analyzer is your friend, and you’ll learn a lot of how it works and how the query and index design can affect performance through it.

DO use referential integrity

This can be a great time saver. Define all your keys, unique constraints and foreign keys. Every validation you create on the server will save you time in the future.

Conclusion

As I’ve said before, this is by no means a complete SQL Server performance and best practices guide. This would take a complete book to cover. But I really believe that this is a good start, and if you follow these practices, surely you will have much less trouble in the future.

(http://www.codeproject.com/cs/database/sqldodont.asp)

Exception Management Architecture Guide

 

 

Building Distributed Applications

Exception Management Architecture Guide

(http://msdn2.microsoft.com/en-us/library/ms954599(d=printer).aspx)

Patterns and Practices home [ http://www.microsoft.com/practices ]

Related Linkspatterns and practices Index [ http://www.microsoft.com/practices ]

Application Architecture for .NET: Designing Applications and Services [ http://msdn2.microsoft.com/en-us/library/ms954595.aspx ]

Kenny Jones, Edward Jezierski, Jason Hogg, Roberta Leibovitz (Modeled Computation) and Colin Campbell (Modeled Computation)
Microsoft Corporation

August 2001

Updated June 2003

Summary: This document discusses design and implementation guidelines for exception management systems that use .NET technologies. It focuses on the process of handling exceptions within .NET-connected applications in a highly maintainable and supportable manner. (38 printed pages)

Download

Download the Exception Management Architecture Guide from the MS.com Download Center [ http://www.microsoft.com/downloads/details.aspx?familyid=73742594-db15-4703-8892-75a569c4eb83&displaylang=en ]

Contents

Exception Management

Exception Handling Process

Exception Detection

Exception Propagation

Custom Exceptions

Managing Unhandled Exceptions

Gathering information

Application Instrumentation

Specific Technology Considerations

Exception Management

To build successful and flexible applications that can be maintained and supported easily, you must adopt an appropriate strategy for exception management. You must design your system to ensure that it is capable of the following:

  • Detecting exceptions.
  • Logging and reporting information.
  • Generating events that can be monitored externally to assist system operation.

Spending the time at the beginning to design a clear and consistent exception management system frees you from having to piece one together during development, or worse still, from having to retrofit exception handling back into an existing code base.

An exception management system should be well encapsulated and should abstract the details of logging and reporting from the application’s business logic. It should also be capable of generating metrics that can be monitored by operators to provide an insight into the current health and status of the application. This helps to create an application that can quickly and accurately notify operators of any problems it is experiencing, and can provide valuable information to assist developers and support services with problem resolution.

Understanding Exceptions and Exception Hierarchies

The Microsoft® .NET common language runtime provides a means for notifying programs of exceptions in a uniform way, even if the module generating the exception is written in a different language than the one handling the exception.

Exceptions represent a breach of an implicit assumption made within code. For example, if your code tries to access a file that is assumed to exist, but the file is missing, an exception would be thrown. However, if your code does not assume that the file exists and checks for its presence first, this scenario would not necessarily generate an exception.

Exceptions are not necessarily errors. Whether or not an exception represents an error is determined by the application in which the exception occurred. An exception that is thrown when a file is not found may be considered an error in one scenario, but may not represent an error in another.

All exception classes should derive from the Exception base class within the System namespace for compliance with Common Language Specification (CLS). The .NET Framework class library) provides an extensive hierarchy of exception classes to handle various types of common exceptions, all of which ultimately derive from Exception. Exceptions can be generated by the .NET runtime or they can be created programmatically. You will find more information on how to create your own exception hierarchies later in this document.

ApplicationException serves as the base class for all application-specific exception classes. It derives from Exception but does not provide any extended functionality. You should derive your custom application exceptions from ApplicationException. Figure 1 illustrates the basic exception class hierarchy.

Figure 1. Exception class hierarchy

Exception Handling Process

There are two main processes for handling exceptions. The process flowchart shown in Figure 2 illustrates the basic steps that your application should perform to handle an exception. Each method or procedure within your application code should follow this process to ensure that exceptions are handled within the appropriate context defined by the scope of the current method.

Figure 2. Exception handling process

This process continues to occur as an exception propagates up the call stack. The “Exception Detection” and “Exception Propagation” sections discuss this process in detail. Figure 3 shows the process your application should perform when the exception is propagated to the last point or boundary at which your application can handle the exception before returning to the user.

Figure 3. Processing of unhandled exceptions

This process is vitally important to your application’s ability to persist exception information, notify operational resources of problems, and properly manage the user experience. This process is discussed in the Managing Unhandled Exceptions, Gathering Information, Logging, and Notification sections of this document.

Exception Detection

The .NET Framework allows all .NET languages to take advantage of structured exception handling. Structured exception handling provides a control structure that includes exceptions, protected blocks of code, and filters to allow your code to handle exceptions robustly and efficiently. You can use try, catch, and finally blocks to detect exceptions thrown within your code and to react to them appropriately.

 

 

Copy Code

try
{
   // Some code that could throw an exception.
}
catch(SomeException exc)
{
   // Code to react to the occurrence
   // of the exception
}
finally
{
   // Code that gets run always, whether or not
   // an exception was thrown. This is usually
   // clean up code that should be executed
   // regardless of whether an exception has
   // been thrown.
}

The try block contains code that could throw an exception. When an exception is thrown within this block, the first catch block whose filter matches the class of the exception catches it. Note that the filter is specified within the parentheses following the catch keyword. If you have multiple catch blocks, you should ensure that these are ordered from the most specific type to the most generic type. This ensures the most specific type catch block is executed for any given exception. The finally statement executes, allowing clean up and other code to be executed, regardless of whether an exception is thrown.

You should only catch exceptions when you need to specifically perform any of the following actions:

  • Gather information for logging
  • Add any relevant information to the exception
  • Execute cleanup code
  • Attempt to recover

If a particular method does not need to perform any of these actions, it should not catch the exception; rather, it should allow it to propagate back up the call stack. This keeps your code clean and explicit as you only catch the exceptions that need to be handled within the scope of a particular method and allow all others to continue to propagate.

Use Exceptions Appropriately

You should only throw exceptions when a condition outside of your code’s assumptions occurs. In other words, you should not use exceptions as a means to provide your intended functionality. For example, a user might enter an invalid user name or password while logging on to an application. While this is not a successful logon, it should be a valid and expected result, and therefore should not throw an exception. However, an exception should be generated if an unexpected condition occurs, such as an unavailable user database. Throwing exceptions is more expensive than simply returning a result to a caller. Therefore they should not be used to control the normal flow of execution through your code. In addition, excessive use of exceptions can create unreadable and unmanageable code.

Exceptions Intercepted by the Runtime

In certain scenarios, an exception you throw may be caught by the runtime and an exception of another type might be thrown up the call stack in place of the original. For example, consider the case in which you call the Sort method on an ArrayList of your objects. If one of your objects throws an exception in the CompareTo method of its IComparable interface, the exception is caught by the runtime and a System.InvalidOperationException exception is thrown to the code that calls the Sort method. In addition, any exception thrown by a method that you invoke through reflection will be caught by the runtime and a System.Reflection.TargetInvocationException will be thrown to your code. In these scenarios your original exception is not lost. It is set as the InnerException of the exception thrown by the runtime (the InnerException property is discussed in the “Exception Propagation Section”). Situations like this can occur in a number of scenarios. You should be aware of this and test your applications thoroughly to minimize the impact of these scenarios.

For more information on these topics, see the following:

Exception Propagation

There are three main ways to propagate exceptions:

  • Let the exception propagate automaticallyWith this approach, you do nothing and deliberately ignore the exception. This causes the control to move immediately from the current code block up the call stack until a catch block with a filter that matches the exception type is found.
  • Catch and rethrow the exceptionWith this approach, you catch and react to the exception, and clean up or perform any other required processing within the scope of the current method. If you cannot recover from the exception, you rethrow the same exception to your caller.
  • Catch, wrap, and throw the wrapped exceptionAs an exception propagates up the call stack, the exception type becomes less relevant. When an exception is wrapped, a more relevant exception can be returned to the caller. This is illustrated in Figure 4. With this approach, you catch the exception, which allows you to react to it, clean up, or perform any other required processing within the scope of the current method. If you cannot recover, wrap the exception in a new exception, and throw the new exception back to the caller. The InnerException property of the Exception class explicitly allows you to preserve a previously caught exception. This allows the original exception to be wrapped as an inner exception inside a new and more relevant outer exception. The InnerException property is set in the constructor of an exception class.

Figure 4. Exception propagation

As the exception propagates up the call stack, catch blocks can only catch the outer exception. Inner exceptions are programmatically accessible through the InnerException property, but they cannot be matched to a catch block.

The following example shows how these methods are implemented.

 

 

Copy Code

try
{
   // Some code that could throw an exception.
}

catch(TypeAException e)
{
   // Code to do any processing needed.
   // Rethrow the exception
   throw;
}

catch(TypeBException e)
{
   // Code to do any processing needed.

   // Wrap the current exception in a more relevant
   // outer exception and rethrow the new exception.
   throw(new TypeCException(strMessage, e));
}

finally
{
   // Code that gets executed regardless of whether
   // an exception was thrown.
}

The first catch block catches an exception of type TypeAException, performs any processing needed, and rethrows the same exception back up the call stack. The second catch block shows how you could wrap the TypeBException in a more relevant outer exception of type TypeCException and throw the new outer exception up the call stack. In this example the code only cares about exceptions of either the TypeAException or TypeBException type. For all other exception types, the code lets the exception propagate automatically.

Table 1 lists the three approach to propagate exceptions and the benefits and limitations of each approach.

Table 1. Exception Propagation Summary

Means of Propagation Allows you to react to the exception Allows you to add relevancy

When to Use InnerException

You should wrap an exception only when there is a compelling reason to do so. When an exception is initially thrown, it provides information about the exact cause of the exception. As an exception propagates up the call stack, the exception type becomes less relevant. Wrapping an exception can provide a more relevant exception to the caller.

For example, consider a hypothetical method called LoadUserInfo. This method may load a user’s information from a file that is assumed to exist when the method tries to access it. If the file does not exist, a FileNotFoundException is thrown, which has meaning within the context of the LoadUserInfo method. However, as the exception propagates back up the call stack—for example, to a LogonUser method—a FileNotFoundException exception does not provide any valuable information. If you wrap the FileNotFoundException in a custom exception class (discussed later in this document)—for example, one called FailedToLoadUserInfoException—and throw the wrapper exception, it provides more information and is more relevant to the calling LogonUser method. You can then catch the FailedToLoadUserInfoException exception in the LogonUser method and react to that particular exception type rather than having to catch the FileNotFoundException, which is an implementation detail of another method.

Custom Exceptions

The .NET Framework is a type-based system that relies on the exception type for identification, rather than using method return codes such as HRESULTs. You should establish a custom hierarchy of application-specific exception classes that inherit from ApplicationException, as illustrated in Figure 5.

Figure 5. Application exception hierarchy

This hierarchy allows your application to benefit from the following:

  • Easier development because you can define properties and methods on your base application exception class that can be inherited by your other application exception classes.
  • New exception classes created after your application has been deployed can derive from an existing exception type within the hierarchy. Existing exception handling code that catches one of the base classes of the new exception object will catch the new exception without any modifications to the existing code, interpreting it as one of the object’s base classes.

Designing Your Application Exception Hierarchy

The .NET Framework provides an extensive hierarchy of exception classes. If a suitable exception class is already provided by the .NET Framework, use it instead of creating a new class. You should only create a new application exception class for an exception type that you need to react to and handle within your code that is not already available in your application exception hierarchy or in the Framework. Most application exception hierarchies should be fairly flat with grouping used for organization purposes or to allow some set of application exceptions to inherit common properties or functionality.

As you create your hierarchy, use the following questions to help you to decide if you need to create a new exception class.

  • Does an exception exist for this condition?If an exception exists either within your current hierarchy or within the Framework, use it instead of creating your own exception class.
  • Does a particular exception need discrete handling?If so, you should create an exception class to allow your code to catch the specific exception and handle it explicitly. This eliminates the need to catch a more generic exception and then use conditional logic to determine what action to take.
  • Do you need specific behavior or additional information for a particular exception?If so, you can use a new application exception class to include additional information or functionality to suit a specific need.

You should store your application exception hierarchy in a single assembly (or small group of assemblies for larger systems) that can be referenced throughout your application code. This helps to centralize the management and deployment of your exception classes.

Creating a Custom Exception Class

To ensure standardized naming, you should always end your custom exception class names with the word Exception. It is also good practice for each of your custom exception classes to provide the three constructors shown in the following class definition:

 

 

Copy Code

using System;
public class YourBaseApplicationException : ApplicationException
{
   // Default constructor
   public YourBaseApplicationException ()
   {
   }

   // Constructor accepting a single string message
   public YourBaseApplicationException (string message) : base(message)
   {
   }

   // Constructor accepting a string message and an
   // inner exception which will be wrapped by this
   // custom exception class
   public YourBaseApplicationException(string message,
            Exception inner) : base(message, inner)
   {
   }
}

Creating a Base Application Exception Class

Your application should have a single base exception class that derives from ApplicationException. This class serves as the base class for all of your application’s exception classes, as shown earlier in Figure 5.

You should add fields to this base class to capture specific information, such as the date and time the exception occurred, the machine name, and so on. This encapsulates the common exception details into the single base class and makes this information available to your exception classes through inheritance.

Remoting Custom Exceptions

The Exception class implements the ISerializable interface, allowing it to manage its own serialization. To allow your exceptions to be marshaled across remoting boundaries, you need to attribute your class with the [Serializable] attribute and include the additional constructor shown below.

 

 

Copy Code

protected YourBaseApplicationException(SerializationInfo info,
      StreamingContext context) : base(info, context)
{
}

If your exceptions add fields to the base ApplicationException class, you will need to persist these values programmatically into the serialized data stream. This can be done by overriding the GetObjectData method and adding these values to the SerializationInfo object as shown below.

 

 

Copy Code

 [Serializable]
public class ExampleException : ApplicationException
{
   public ExampleException() : base()
   {
   }
   public ExampleException(string message) : base(message)
   {
   }
   public ExampleException(string message,Exception inner) :
                  base(message,inner)
   {
   }
   protected ExampleException(SerializationInfo info, StreamingContext context) :
                  base(info,context)
   {
      m_strMachineName = info.GetString("m_strMachineName");
   }

   public override void GetObjectData( SerializationInfo info,
                  StreamingContext context )
   {
      info.AddValue("m_strMachineName", m_strMachineName,
                  typeof(String));

      base.GetObjectData(info,context);
      }

   private string m_strMachineName = Environment.MachineName;

   public string MachineName
   {
      get
      {
         return m_strMachineName;
      }
      set
      {
         m_strMachineName = value;
      }
   }
}

The values can be retrieved from the SerializationInfo object in the constructor of your exception object using info.GetValue or one of the Get methods of the SerializationInfo object (such as the GetString method shown above). Your custom exceptions should be able to maintain their state as they are marshaled across local and remote boundaries. Even if your application does not currently span remoting boundaries, providing serialization support ensures that exception information will not be lost if your application is later modified to do so. If you do not programmatically serialize your exception’s custom values, they will not be set properly when they are deserialized at the client. Providing serialization support also allows your application to serialize all the information about your exception and store it for logging purposes.

For more information on these topics, see the following:

Managing Unhandled Exceptions

When an unhandled exception propagates to the last point or boundary at which your application can handle the exception before returning to the user, your application can no longer recover from the exception. At this point, it must gather information, log information, send any notifications, perform any cleanup, and do any other processing needed before managing the communication of the exception condition to the user. In most Web-based applications, this boundary is controlled by your ASP.NET code through either Web pages to end users or Web services to other applications. This section discusses the tools available to manage unhandled exceptions at the system boundary.

ASP.NET

With ASP.NET, you are no longer restricted by the limited capabilities of scripting languages. ASP.NET code is compiled and can be written in any .NET Framework language. As opposed to the limited techniques available in the traditional ASP world, ASP.NET allows your pages to use all of the exception handling techniques that your components can. ASP.NET provides some specific functionality to allow your application to manage exceptions and configure how information should be displayed back to the user.

Configuring Web.config Settings

You should configure exception management settings within your application’s Web.config file. The following is an example of the exception settings in a Web.config file.

 

 

Copy Code

<customErrors defaultredirect="http://hostname/error.aspx" mode="on">
  <error statuscode="500" redirect="/errorpages/servererror.aspx" />
  <error statuscode="404" redirect="/errorpages/filenotfound.htm" />
</customErrors>

In the customErrors element, specify a default redirect page. There are three modes for the default redirect page:

  • onUnhandled exceptions will redirect the user to the specified defaultredirect page. This is used mainly in production.
  • offUsers will see the exception information and not be redirected to the defaultredirect page. This is used mainly in development.
  • remoteonlyOnly users accessing the site on the local machine (using localhost) will see the exception information while all other users will be redirected to the defaultredirect page. This is used mainly for debugging.

In addition to the default redirect page, you can set specific pages for certain HTTP error codes. For example, you can specify that all 404 errors result in a certain error page, while all 500 errors result in another (as shown above).

Before .NET was available, these settings had to be configured within the Internet Information Services (IIS) metabase. The ASP.NET Web.config file centralizes the configuration settings for your application into a file, allowing xcopy deployment of your application together with its settings.

You should be aware that these settings only apply to ASP.NET files (that is, files with specific extensions, such as .aspx and .asmx). For example, if a user requests an .htm or .asp file that does not exist on your Web server, IIS will redirect the user based on the HTTP error settings defined within the IIS metabase and not the Web.config file. If you want consistency between ASP.NET and non-ASP.NET files, you should configure IIS and the Web.config file to redirect users to the same pages. Any settings in the IIS metabase must be deployed along with your application files.

Using the @ Page Directive

The Web.config file settings apply to its directory and any child directories. These settings can be overridden for specific pages using the ErrorPage attribute of the Page directive. For example:

 

 

Copy Code

<%@ Page ErrorPage="customerror.aspx" %>

This sample will redirect the user to customerror.aspx if an unhandled exception occurs within the current page regardless of the Web.config settings.

Handling ASP.NET Exceptions

ASP.NET provides two main events for reacting to unhandled exceptions that propagate up from your application code:

  • Page_ErrorThis event is fired when an exception is not handled at the page level. In order for this event to be fired, you should include a Page_Error event handler in your page’s code and either verify that the AutoEventWireup attribute of the @ Page directive is set to true (the default), or manually hook up the event using the following code.

     

     

    Copy Code

    Page.Error += new System.EventHandler(Page_Error);
  • Application_ErrorThis is located in the global.asax file and as a result has application-wide scope. It occurs if an exception is not handled by a particular page. You should use this event to do any logging, notifications, and any other processing needed before you gracefully return an error to the user.

     

     

    Copy Code

    // In global.asax file
    protected void Application_Error(Object sender, EventArgs e)
    {
          Exception exc = Server.GetLastError();
          // Perform logging, send any notifications, etc.
    }

In these events, you use the Server.GetLastError method to obtain a reference to the unhandled exception object. A Server.ClearError can also be used to clear the exception and stop any further propagation. For example, your page might have a Page_Error event that uses Server.GetLastError to access the unhandled exception object. If you do not clear the exception by using the Server.ClearError, the unhandled exception will continue to propagate up to the Application_Error event. If you use Server.ClearError to clear the exception in Application_Error, the defaultredirect setting in the Web.config file will not redirect the user because the unhandled exception no longer exists. If you want the defaultredirect setting to properly redirect users, do not clear the exception in Application_Error.

For more information on these topics, see the following:

Web Services

Web services are limited in their ability to propagate exceptions using the Simple Object Access Protocol (SOAP). The .NET Framework provides the SoapException class as the only means to raise exceptions using SOAP. The CLR throws a SoapException automatically when it receives a malformed SOAP request from the client.

When a Web service method throws any unhandled exception, the exception is repackaged as a SoapException and is returned to the Web service client via the SOAP response. The ServerFaultCode is used to indicate that an error that was not a result of the message format or contents occurred during the processing of the request. The SOAP response that the server runtime creates is in a standard SOAP format allowing non-.NET clients to detect that an error has occurred. On a .NET-based client, the Framework captures the SOAP message and deserializes the SoapException so that the client can detect exceptions through standard exception handling techniques, just as it would for a non-Web service call. However, the exception that is detected on the client is a SoapException, not the exception type that was originally thrown by the Web service. Information about the original exception is included in the Message property of the SoapException.

Hiding Exception Information

For Web services that are exposed to external companies and systems, you may not want to expose all of your exception information to your clients. You should throw an exception to your clients so that they can react, but you may not want all of the details of your specific exception to be sent outside of your company. You may only want to throw an exception that indicates that the service is currently experiencing problems.

In this case, you should create a generic application exception that contains any information you want to convey. After catching an unhandled exception in your Web service, you should log the exception and perform any necessary processing, then construct a new instance of the generic application exception. You can then set any desired information in the generic application exception and throw it to the client. This allows you to log detailed information in the Web service and throw a less detailed exception to your clients.

For more information, see the SoapException Class documentation in the .NET Framework Class Library [ http://msdn2.microsoft.com/en-us/library/system.web.services.protocols.soapexception.aspx ] .

The UnhandledExceptionEventHandler Delegate

The UnhandledExceptionEventHandler delegate can be used as a means to handle exceptions that are not handled by your application code. The method you associate with the UnhandledExceptionEventHandler delegate will be executed when an unhandled exception occurs.

In most ASP.NET Web applications, your application will use Application_Error, instead of the UnhandledExceptionEventHandler, to serve as your application’s last chance to handle the exception.

For more information, see the UnhandledExceptionEventHandler Delegate documentation in the .NET Framework Class Library [ http://msdn2.microsoft.com/en-us/library/system.unhandledexceptioneventhandler.aspx ] .

Gathering Information

You should ensure that you always capture all the appropriate information that accurately represents the exception condition. What you do with this information depends on the needs of the recipient of the information. Potential recipients of this information include the following:

  • End usersThey require a meaningful and well presented description.
  • Application developersThey require more detailed information to assist with problem diagnosis.
  • OperatorsThey require relevant information that allows them to react appropriately and take the necessary recovery steps.

Capturing Appropriate Information

It is important that each group receive the information that is relevant to them, allowing them to react appropriately. Not all information is appropriate for all audiences. For example, your users should not see cryptic messages that include source code line numbers or conditions relating to programmatic conditions, such as an index being out of range. Equally annoying for end users are generic messages such as “Error Occurred. Please contact the system administrator.” Your users should be shown a helpful message that includes any actions that they can take to rectify the problem. Always capture all of the relevant information and filter the display of this information based on the recipient’s needs. Table 2 provides a summary of required information by audience level.

Table 2. Exception Message Information by Audience

Audience Required Information

To allow your application to provide rich information that can be tailored to suit the various groups, you should capture the details presented in the Table 3. Note that the Source column indicates the class and the class member that can generate this information. Unless otherwise noted, all classes are located within the System namespace.

Table 3. Exception Information to Capture

Data Source

Accessing All Exception Data

Exceptions can have various structures and expose different public properties that offer valuable information about the cause of the exception. It is important to capture all of this information in order to obtain a complete picture of what went wrong. By using reflection (implemented in the System.Reflection namespace) you can easily walk the public properties of an exception and access all of its values. This is useful for generic logging routines that accept an argument of type Exception. Such routines can use reflection to interrogate and access the detailed and specific exception information. Sufficient exception detail is critical to allow developers to determine the source of the exception.

For more information on these topics, see the following:

Application Instrumentation

Instrumentation is the act of incorporating code into your program that reveals application-specific data to someone monitoring that application. Raising events that help you to understand an application’s performance or allow you to audit the application are two common examples of instrumentation.

The Enterprise Instrumentation framework (EIF) provides a flexible way of handling many application events, including exceptions. It uses event sources and event sinks to decouple an application’s logging functionality from its notification delivery system. However, for exception handling specifically, it is also possible to build custom logging and notification solutions using the various techniques discussed in the “Logging” and “Notification” sections of this document. Each of these options for instrumentation are discussed in turn.

Enterprise Instrumentation Framework (EIF)

EIF is a comprehensive instrumentation application programming interface (API) especially targeted at multi-tier .NET applications that may scale to multiple application servers. EIF provides a unified programming model for publishing application events. It is recommended for instrumenting enterprise applications.

EIF benefits include the following:

  • EIF greatly simplifies the coding required to publish application events.
  • EIF simplifies coordination between system developers, application developers, and operations staff by using an event schema that defines what events may be raised by the application and what data they will contain.
  • EIF is a single, uniform interface that builds upon Windows Management Instrumentation (WMI), the Windows Event Log service, and Windows Event Trace. This allows for easier migration between the various reporting mechanisms. Also, application developers need not become familiar with the details of each of these interfaces.
  • EIF handles many different types of events, such as security-related events and error-related events.
  • The operations team can configure EIF applications. They can select which events they are interested in and filter out others. This frees developers from modifying code to support the changing monitoring needs of the operations staff.

To use EIF, you must have the following components installed on your development workstation:

  • You must be running Windows XP Professional or Windows 2000 with service pack (SP) 2 or later installed (all editions. However, if you are using Windows 2000, it is recommended that you upgrade to SP3 or later.
  • The .NET Framework needs to be installed so that EIF can install and run. It is compatible with both .NET Framework version 1.0 SP2 or later and.NET Framework 1.1.
  • Microsoft Visual Studio® .NET Enterprise Architect, Visual Studio .NET Enterprise Developer, or Visual Studio .NET Professional (or later) are recommended as an authoring environment but are not required for instrumented applications to run.

You can obtain EIF through Enterprise and Universal MSDN® Subscriptions.

EIF Basics

EIF introduces event sources and event sinks that decouple events being raised from the underlying eventing, tracing, or logging mechanism. In Enterprise Instrumentation, events are always raised from a specific event source. The event source configuration ultimately determines whether the event is raised or not, what information the event contains, and to which eventing, tracing, or logging mechanisms the event is routed. In all cases, information about the event source, such as event source ID, is passed as property values in the event object.

An event sink is responsible for taking an event and firing or logging it using a specific mechanism, such as WMI, the Windows Event Log service, or Windows Event Trace

The EIF configuration file, which is written in XML and is external to the application code, allows you to map connections between event sources and event sinks. This feature provides a great deal of flexibility. It allows operations staff to specify what events will be raised, what data fields will be included, and which event sink(s) will receive the data.

The format of the data passed between event sources and event sinks is given by an event schema. The event schema lists all of the event types that may arise from a given application.

EIF Event Schema

The standard event schema is a starting point for defining the set of events that an application (or all applications) can publish. The following are the types of events defined in that schema (but you will often want to add to these or create entirely new kinds of events):

  • Trace events monitor the progress of program execution. They are placed at the programmer’s discretion.
  • Notification events log events, such as the use of secure resources or application-level warnings.
  • Error events signal abnormal conditions or conditions that require user intervention.

Using EIF to Add Instrumentation

This section gives a conceptual overview of how to add instrumentation to your application using EIF. More detailed information can be found in Chapter 4 of the.NET Operations Guide and in the EIF documentation.

There are two steps to adding EIF instrumentation to your application. The first is to define one or more event sources by instantiating one or more event source objects in your code. Event sources act as conduits through which your application can raise events to the outside world. Any EIF events that you raise within your application must be raised through an event source. If you wish, you can use the default Application event source object that is provided for all instrumented applications.

Once the event sources are defined, then you need to add code to generate events for these sources. When raising an event in your code, you can specify the event source through which it should be raised. If you do not specify an event source when raising an event, it will be raised through the default Application event source.

Configuring EIF Instrumentation

Once your application has been instrumented, you need to create an initial EIF configuration that routes the events to event sinks. An application’s EIF configuration is controlled by the EIF configuration file, which is called EnterpriseInstrumentation.config. This is an XML file that contains a number of XML configuration elements. The five main elements of the EIF configuration file are: eventSource, eventSink, eventCategory, filter, and filterBindings, and are explained below:

  • One eventSource element is needed for each event source that is defined in the application.
  • An eventSink element allows you to define groups of events based on their object type. Events can then be filtered to event sinks using the event categories.
  • An eventCategory element allows you to define groups of events based on their object type. Events can then be filtered to event sinks according to these categories.
  • A filter element connects event categories to event sinks.
  • The filterbinding elements are used to connect event sources to event filters.

You must first generate the EIF configuration file and then edit it you suit your needs. You can use the InstallUtil.exe utility, which is part of the .NET Framework SDK, to generate a default EIF configuration file as a starting point. The file generated by this tool is not a complete configuration file. Next you need to complete the definition of the default filter elements in order to map events to event sinks. You can also add additional filters that allow you to select which events are processed and how they are processed.

The EIF configuration file can be edited manually or with a scriptable configuration API provided by EIF. Once an initial configuration file has been created, it is recommended that you develop scripts that perform common configuration tasks or changes. For example, one script might enable a filter that sends events to a Windows Event Trace sink, while another script might turn this filter off. Developers and operations staff should work together to determine the set of tasks that should be handled by configuration scripts.

Choosing Event Sinks

EIF provides built-in event sinks for WMI, the Windows Event Log service, and Windows Event Trace. Additionally, by implementing custom event sinks, events can be passed to other mechanisms. For example, application developers can develop custom event sinks to support logging events to a database.

The choice of the event sink(s) that you use is based on the monitoring requirements. These are typically determined by the operations team responsible for the deployed application. You are not limited to a single event sink but can use any combination of the three different types. Here are some things to consider when deciding which type of event sink should handle which type of event:

  • The Trace Event Sink provides the best performance but has limited tools for reporting and analyzing the logs it generates. It is best suited for high-volume events that may generate hundreds of events per second.
  • The WMI Event Sink is the richest eventing mechanism, with robust publish/subscribe support for consumers. Although WMI is not normally suitable for high-frequency eventing on Windows 2000, it is significantly more efficient on Windows Server 2003. WMI has a rich set of management tools.
  • The Log Event Sink is suited for lower-volume events with a medium level of visibility. Examples are high-level audit messages and application warnings. The Windows Event Log service does not offer a highly structured format like WMI.

Request Tracing

Request Tracing is a key feature of EIF that allows you to trace business processes and application services by following an execution path across the tiers of a distributed .NET application. This path is defined by start and end points that have been inserted into the application’s code by the developer. Any events that occur along this execution path are tagged with context-related fields that identify them as part of a particular request.

The Request Event Source is the conduit for the entire business process or service. It is also the control point for turning tracing on and off for that process.

Logging

If you want to use logging instead of EIF to store information about exceptions, then there are three possible options:

  • The Windows Event Log service
  • A central relational database, such as Microsoft SQL Server™ 2000
  • A custom log file

Using the Windows Event Log

The Windows 2000 and Windows NT® operating systems provide a set of event logs. For example, the operating system places error details in the system event log, while an application event log is provided for applications to use. You can also isolate your application’s log entries by creating a separate event log, rather than using the shared application event log. The event log provides a consistent and central storage repository for error, warning, and informational messages on a single machine. The .NET Framework provides event log related classes, making it easy to log and monitor.

Advantages

  • It is a highly reliable method of logging information.
  • It is provided by both Windows 2000 and Windows NT.
  • It features log file size management. You can configure a maximum log size and specify the number of days for which records are to be maintained or whether log files are to be cleared manually.
  • Tools such as the Event Viewer allow you to view and manipulate log entries.
  • The .NET Framework classes, such as the EventLog class within the System.Diagnostics namespace, make the event log easy to write to and maintain programmatically.
  • Most monitoring tools support the event log as an event source.
  • The event log has tight integration with Windows Management Instrumentation.
  • Systems administrators and operators are generally familiar with the event log and its associated tools.
  • The operating system and many applications log to the event log, which makes it easy to compare the sequence of system and application events.

Disadvantages

  • Each machine logs to a local event log. This can cause monitoring and maintenance problems for large server farms. You can write entries to a log on another machine; however, this increases the risk of failure and should be avoided.

Note that Microsoft Application Center Server, along with several third-party software packages, allows you to centralize the local event logs from multiple servers. This allows you to view multiple event logs as a single entity.

For more information on using the Windows Event Log service, see the following Web sites:

Using a Central Relational Database

To solve the problem that the single-machine nature of the event log can cause, you can use a centralized database, such as SQL Server 2000, to store information.

Advantages

  • All information is stored in a single centralized location and is accessible from remote machines.
  • You can use database tools for querying and reporting the error data.
  • You can structure the storage of the information in application specific tables.

Disadvantages

  • Database logging introduces an element of risk. If your system is unable to access the database, perhaps due to a network failure, you run the risk of losing the information. To guarantee that your exception is logged, you may decide to write information to the local event log if the database update fails. This is not an ideal solution because you are then forced to monitor two locations and deal with two distinct error formats.
  • Your application entries will be stored in a different location than the operating system’s entries. This can make it difficult to compare the sequence of events between your applications logs and the operating system’s logs.
  • You must develop the tools to view and administer the data.
  • You must customize monitoring systems to interact with the database.

Using a Custom Log File

Some applications log information to a custom log file. This solution should only be used in scenarios where the Event Log cannot satisfy some specific logging need.

Advantages

  • You have complete flexibility when choosing the log format.

Disadvantages

  • It is not trivial to build a custom logging mechanism than can handle concurrent access.
  • You must create and implement processes to manage the log size.
  • You must develop the tools to view and administer the log files.
  • This can cause monitoring and maintenance problems for large server farms since many monitoring tools, including Microsoft Application Center Server, cannot aggregate custom log files.
  • Monitoring tools must be configured to monitor the log files.

Using Asynchronous Delivery with Message Queuing

In conjunction with the options above, you can use Microsoft Message Queuing to provide a reliable transport mechanism to deliver data asynchronously to a data store. While Message Queuing does not represent the final data store, it can be used as a reliable delivery mechanism to ensure that the logged data reaches its ultimate storage repository, which may be any of the locations discussed above. The transactional nature of Message Queuing ensures that log data will not be lost, even in the event of a server or network failure.

  • Message Queuing represents an excellent choice of transport where you are striving for a centralized logging solution. By taking advantage of transactional Message Queuing queues, you can ensure that data will not be lost or duplicated and will be delivered reliably to the destination system.
  • Message Queuing requires a larger coding effort. The code to convert the Message Queuing messages into the final store must be developed.
  • Since Message Queuing is an asynchronous mechanism, the messages may not arrive to the final store in the order in which they occurred.
  • Data may not be stored immediately, particularly if Message Queuing is configured to route messages through several queues. This may be an issue in situations where information is required immediately.

Notification

If you are using logging instead of EIF to keep track of exceptions, then you must also develop a notification solution. Notification is a critical component of any exception management system. While logging is essential in helping you understand what went wrong and what needs to be done to rectify the problem, notification informs you about the condition in the first place. Without proper notification, exceptions can go undetected.

The notification processes adopted by your application should be decoupled from your application code. You should not have to change code every time you need to modify your notification mechanism, for example to alter the list of recipients. Your application should communicate error conditions and rely on a decoupled monitoring system to identify those errors and take the appropriate action.

If your application is not going to run in an environment that utilizes a monitoring system, you have several options for creating notifications from your application. Either way, you need to work closely with any operations personnel or the monitoring system developers to define the correct procedures for monitoring and sending notifications.

Using a Monitoring Application

When working in an environment with a monitoring system, you can provide notifications in two ways.

  • Your application can rely on the monitoring system to watch your log and deal with the detected error conditions appropriately.
  • You can implement code to proactively raise events to notify the monitoring system. The preferred method for proactively notifying a monitoring system of a certain condition is to use WMI.

Monitoring the Log Data Store

You can configure monitoring systems to watch your log and monitor the arrival of messages. You may further fine tune the monitor to react only to a specific set of messages, or perhaps only to those that match a certain criteria, for example those that exceed a defined error level threshold. Tools such as Application Center Server can detect certain information logged to the event log and then take action based on preconfigured settings.

Advantages

  • Your monitoring system is decoupled from your application code.
  • You do not require any additional code beyond your logging implementation.

Disadvantages

  • The monitoring system must be configured to poll the log and filter records to find those it needs to handle.
  • Any other systems that want to handle these events must be configured to watch your chosen log.

For more information, see the Application Center Server [ http://www.microsoft.com/applicationcenter/ ] .

Creating WMI Events

WMI is the Microsoft implementation of an industry standard for accessing management information in an enterprise environment. The WMI event infrastructure provides the ability for applications to raise WMI events that can be monitored and handled. This approach decouples the notification rules and procedures from the code in your application. Your application needs to raise only certain events that a monitoring system can catch, and then implement the correct notification procedures. .NET provides classes within the System.Management and System.Management.Instrumentation namespaces to simplify WMI programming.

You should not raise a WMI event for every exception. WMI events provide a hook into your application to allow other systems to react. They should therefore be used when something occurs within your application that could warrant action from an external process or application.

Advantages

  • Using the WMI event infrastructure decouples the operational procedures carried out as a result of an unhandled exception from your application code.
  • The WMI event infrastructure integrates well with monitoring applications.
  • The WMI event infrastructure allows monitoring applications to group your application events with other application events to provide an enterprise-wide notification system.

Disadvantages

  • An application may raise WMI events from any number of machines comprising the application. In most cases, a monitoring tool must be installed on each of the machines it needs to monitor in order to detect these events. Product licensing may become an issue in a distributed system.

For more information on WMI and creating WMI events, see the following resources:

Creating Notifications

If your application does not have the advantage of working with a monitoring tool, there are several ways to create notifications. Some common options include:

  • Sending mail using SMTP
  • Developing a custom notification system.

Sending Mail Using SMTP

One approach to sending notifications is to send mail to a distribution list that includes the appropriate support personnel. This approach is common because it is easy to implement, but is not the most robust or stable choice.

Advantages

  • A solution is quick and easy to implement using SMTP. The .NET Framework provides the System.Web.Mail namespace to simplify the generation and transmission of mail.

Disadvantages

  • This is not a resilient solution. If the e-mail fails, the notification will is lost.
  • When the number of errors is high, you can generate many e-mail notifications, which makes it difficult to solve the problem at hand.

Developing a Custom Notification System

You can also create a notification program that can receive messages from your application and then take some configurable action based on the type of the exception. Message Queuing provides a reliable asynchronous delivery mechanism to pass data to a notification system (in the same way that it can be employed to transfer data to a centralized error log). By taking advantage of transactional Message Queuing queues, you can ensure that data is not lost or duplicated and is delivered reliably to the destination system. The custom notification application only needs to provide a subset of the functionality of a monitoring system.

Advantages

  • Using a custom notification program that uses transactional Message Queuing queues decouples the notification procedures from your application code.
  • A custom notification program that uses transactional Message Queuing queues can support multiple applications.
  • A custom notification program eliminates potential licensing issues associated with a monitoring system.
  • Using a custom notification program that uses transactional Message Queuing queues provides asynchronous, reliable delivery of notifications.

Disadvantages

  • You must develop and maintain the notification system.
  • Similar functionality is already provided in production monitoring systems such as Application Center Server.

Specific Technology Considerations

This section examines some specific considerations for exception management when combined with certain .NET technologies.

Interoperability

The runtime provides a variety of interoperability features that allow your managed and unmanaged code to work together. This also applies to exception handling. When a COM client calls a managed class that throws an exception, the runtime catches and translates it into an HRESULT that can be understood by the unmanaged COM code. Similarly, if a COM component returns a failed HRESULT to managed code, the runtime translates an exception from the HRESULT and incorporates any additional information provided by the COM IErrorInfo interface into the exception object. If the runtime does not recognize the HRESULT, it returns a generic COMException object with its ErrorCode property set to the unrecognized HRESULT value.

Many of the .NET Framework class library exceptions return new HRESULT values to COM components. This causes existing COM code not to recognize the HRESULTs. If your COM code takes some action based on certain failed HRESULTs, you should change the HRESULT value of the exception before propagating it to your COM code. The HResult property of the exception object represents the HRESULT value returned to unmanaged code. It is a read/write property and can be set to any value to ensure the correct operation of your COM code. In addition, any application exceptions you create that interact with unmanaged COM code should override the HResult property and default the value to an HRESULT value that accurately represents the exception condition.

For more information on interoperability, refer to the following:

Localization

Localization is required to provide the right information to the right audience. Your application should provide localization to ensure that your information is presented in a format that your audiences can understand. .NET provides mechanisms, such as assembly cultures and satellite assemblies, to integrate localization features into your application.

  • CulturesA culture is part of the assembly identity and is involved in the binding process. For example, an assembly could be strictly developed for a Spanish audience.
  • Satellite AssembliesThese are resource-only assemblies that do not contain common intermediate language (CIL) code. Satellite assemblies can specify a culture that accurately reflects the culture of the resources placed in the assembly.

For more information on localization, see the following:

Collaborators

Many thanks to the following contributors and reviewers:

Anders Hejlsberg, Jeffrey Richter (Wintellect), Rob Howard, Susan Warren, Bart Robertson, Edward Jezierski, Alex Mackman (Content Master Ltd.), Tony Surma, Chris Brooks, Brad Abrams, Paul Bates, David Keogh, Jayesh Rege, Ann Chung, Ken Argo, Bernard Chen (Sapient), Steve Busby, Jeff Kercher, Amitabh Srivastava, Peter Laudati.

To learn more about .NET best practices, please visit the patterns & practices [ http://www.microsoft.com/resources/practices/ ] Web page.

To participate in an online collaborative development environment on this topic, join the GotDotNet workspace: Microsoft Patterns & Practices Exception Management & Instrumentation Workspace [ http://www.gotdotnet.com/community/workspaces/workspace.aspx?id=c1146b3a-3f9b-47b8-899e-f42e667cdccf ] . Please share your Exception Management Block questions, suggestions, and customizations with the community in this workspace.

Questions? Comments? Suggestions? For feedback on the content of this article, please e-mail us at devfdbck@microsoft.com.

Patterns and Practices home [ http://www.microsoft.com/practices ]

Custom Controls in Visual C# .NET

More Information on installing the .Net Framework click here.
Download full Visual Studio C# .NET Examples from this Article.

Contents

Overview

Types of Custom Controls

User controls
Inherited controls
Owner-drawn controls
Extender providers

Communication between User Controls and subscribing Applications

Publising and Subscribing Events
Events and Delegates

Submit Button User Control

Create the Submit Button User Control
Using the Submit User Control in a Windows Application

Login Validation User Control

Create the Login Validation User Control
Using the Login Validation User Control in a Windows Application

Format Mask Control

Create the Format Mask Control
Using the Edit Mask User Control in a Windows Application

Toggle Button User Control

Create the Toggle Button User Control
Using the Toggle Button User Control in a Windows Application


Overview

Embedding user controls in a Windows form is just like adding a simple button or text box that are already provided with .NET. These basic controls were written essentially like you code your own controls. Typically the controls you design are to be used in multiple forms or to modularize your code. These reasons help reduce the amount of code you have to type as well as make it easier for you to change your implementation. There should almost never be any reason to duplicate code because it leaves a lot of room for bugs. So, implementing functionality specific to your control in the control’s source code is a good idea. This reduces code duplication as well as modularize your code, which is a good programming guideline.

Custom controls are a key theme in .NET development. They can help your programming style by improving encapsulation, simplifying a programming model, and making user interface more “pluggable” (i.e., making it easier to swap out one control and replace it with a completely different one without rewriting your form code). Of course, custom controls can have other benefits, including the ability to transform a generic window into a state-of-the-art modern interface.

Generally, developers tackle custom control development for one of three reasons:

  • To create controls that abstract away unimportant details and are tailored
    for a specific type of data. You saw this model in Chapter 6 with custom
    ListView and TreeView examples.
  • To create controls that provide entirely new functionality, or just combine
    existing UI elements in a unique way.
  • To create controls with a distinct original look, or ones that mimic popular
    controls in professional applications (like Microsoft’s Outlook bar) that
    aren’t available to the masses.

In .NET, creating a custom control is as easy as creating an ordinary class. You simply inherit from the best possible ancestor and add the specific features you need. Best of all, you can create a custom control class as part of an existing project, and then decide later to place it in a separate assembly that can be shared with other programmers.

Types of Custom Controls

Developers often make a distinction between three or four types of controls:

  • User controls are the simplest type of control. They inherit from the
    System.Windows.Forms.UserControl class, and follow a model of composition.
    Usually, user controls combine more than one control in a logical
    unit (like a group of text boxes for entering address information).
  • Inherited controls are generally more powerful and flexible. With an inherited
    control, you choose the existing .NET control that is closest to what you
    want to provide. Then, you derive a custom class that overrides or adds
    properties and methods. The examples you’ve looked at so far in this book,
    including the custom TreeViews and ListViews, have all been inherited
    controls.
  • Owner-drawn controls generally use GDI+ drawing routines to generate
    their interfaces from scratch. Because of this, they tend to inherit from a
    base class like System.Windows.Forms.Control. Owner-drawn controls
    require the most work and provide the most customizable user interface.
  • Extender providers, which aren’t necessarily controls at all. These components
    add features to other controls
    on a form, and provide a remarkable way to implement
    extensible user
    interface.

Communication between User Controls and subscribing Applications

Because the basic .NET controls are contained within our user control, events are not fired for the contained applications. Our user control is treated like any other and must implement it’s own properties (besides those inherited from System.Windows.Forms.Control) and events.

Publising and Subscribing Events

The Event model in C# finds its roots in the event programming model that is popular in asynchronous programming. The basic foundation behind this programming model is the idea of “publisher and subscribers.” In this model, you have publishers who will do some logic and publish an “event.” Publishers will then send out their event only to subscribers who have subscribed to receive the specific event.

In C#, any object can publish a set of events to which other applications can subscribe. When the publishing class raises an event, all the subscribed applications are notified. The following figure shows this mechanism.

Events and Delegates

At the heart of Events in C# are Delegates. When an object generates an events, it must send the event out. The way that events are dispatched is through the use of delegates. Let’s look how Events are declared in C#.

[attributes] [modifier] event type member-name;

  • Modifier is any allowable scope modifier.
  • Type must be a delegate.
  • Member-name is the Name of the Event with which you will refer to the event in your code.

The important thing to note here is the delegate type that events should use. In the strictest sense, the delegate can be any legal delegate. But there is a convention that you should follow and is one that Window Forms uses. By Convention, the delegate should accept two parameters:

  1. The object that generated the event

  2. The parameters for the specific event

An example of an event / delegate is as follows:

public delegate void SubmitClickedHandler(object sender, EventArgs e);
public event
SubmitClickedHandler SubmitClicked;

SubmitClickedHandler is the name of the delegate, sender is self explanatory. EventArgs is defined under the System namespace and is a very plain class. SubmitClicked is the name of the event, which is published to the Subscriber.

Submit Button User Control

Create the Submit Button User Control

The control we will create will contain a text box for your name and a button that will fire an event. To begin, open Visual Studio .NET and begin a new C#  Windows Control Library. You may name it whatever you like, for this sample the project name will be SubmitButton.


using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;

namespace Akadia
{
    namespace SubmitButton
    {
        // User Control which contain a text box for your
        // name and a button that will fire an event.
        public class SubmitButtonControl : System.Windows.Forms.UserControl
        {
            private System.Windows.Forms.TextBox txtName;
            private System.Windows.Forms.Label lblName;
            private System.Windows.Forms.Button btnSubmit;
            private System.ComponentModel.Container components = null;
            // Declare delegate for submit button clicked.
            //
            // Most action events (like the Click event) in Windows Forms
            // use the EventHandler delegate and the EventArgs arguments.
            // We will define our own delegate that does not specify parameters.
            // Mostly, we really don't care what the conditions of the
            // click event for the Submit button were, we just care that
            // the Submit button was clicked.
            public delegate void SubmitClickedHandler();
            // Constructor           public SubmitButtonControl()
            {
                // Create visual controls
                InitializeComponent();
            }

            // Clean up any resources being used.
            protected override void Dispose( bool disposing )
            {
                if( disposing )
                {
                    if( components != null )
                        components.Dispose();
                }
                base.Dispose( disposing );
            }
            .....
            .....
            // Declare the event, which is associated with our
            // delegate SubmitClickedHandler(). Add some attributes
            // for the Visual C# control property.
            [Category("Action")]
            [Description("Fires when the Submit button is clicked.")]
            public event SubmitClickedHandler SubmitClicked;
            // Add a protected method called OnSubmitClicked().
            // You may use this in child classes instead of adding
            // event handlers.
            protected virtual void OnSubmitClicked()
            {
                // If an event has no subscribers registerd, it will
                // evaluate to null. The test checks that the value is not
                // null, ensuring that there are subsribers before
                // calling the event itself.
                if (SubmitClicked != null)
                {
                    SubmitClicked();  // Notify Subscribers
                }
            }
            // Handler for Submit Button. Do some validation before
            // calling the event.
            private void btnSubmit_Click(object sender, System.EventArgs e)
            {
                if (txtName.Text.Length == 0)
                {
                    MessageBox.Show("Please enter your name.");
                }
                else
                {
                    OnSubmitClicked();
                }
            }
            // Read / Write Property for the User Name. This Property
            // will be visible in the containing application.
            [Category("Appearance")]
            [Description("Gets or sets the name in the text box")]
            public string UserName
            {
                get { return txtName.Text; }
                set { txtName.Text = value; }
            }
        }
    }
}

Using the Submit User Control in a Windows Application

Using the control in a Windows form is trivial. It’s just like adding any other control like a button or a DataGrid. First, create a new Windows Application project named: TestApp. Add a reference to the Submit Button User Control DLL named: SubmitButton.dll. Now you are ready to customize the Toolbox: Right-Click the Toolbox, .NET Framework Components, Browse, select the SubmitButton.dll.

The Submit Button User Control is now added to the Toolbox and can be inserted in Windows Form as any other control. Now we want to handle the SubmitClicked event for the user control. This will simply close the form. The control itself will take care of validation and the event won’t be fired unless the text is valid. Click on the lightning-looking button (for events) with the control selected and you’ll see the event, SubmitClicked, listed under the “Action” category. Click on it once and you’ll see the description we added previously. Now double-click it and VS.NET will add an event handler SubmitClicked() which displays the name from the user control and close the form when the event is fired.

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace TestApp
{
   // Test Application for the Submit Button User Control
    public class TestApp : System.Windows.Forms.Form
    {
        private Akadia.SubmitButton.SubmitButtonControl submitButtonControl;
        private System.ComponentModel.Container components = null;
        ....
        .....
        [STAThread]
        static void Main()
        {
            Application.Run(new TestApp());
        }
        // Handle the SubmitClicked Event
        private void SubmitClicked()
        {
            MessageBox.Show(String.Format("Hello, {0}!",
                submitButtonControl.UserName));
            this.Close();
        }
    }
}

Login Validation User Control

Create the Login Validation User Control

The following sample shows how to implement a Login user control. When the user clicks the Login button, the control will validate the data entered by the user. If the user has left either the User name or the Password text boxes empty, the loginError validation control will display an error icon against the offending control. The Password will then be checked by a “secret algorithm”, if the Password is valid, the user control will raise an event called LoginSuccess; otherwise it will fire a different event called LoginFailed.

In this sample we use the predefined System.EventHandler delegate. This delegate is useful if you want to define an event that has no additional data. The event will be passed an empty System.EventArgs parameter instead. This is the delegate used by many of the Windows Forms.

using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;

namespace Akadia
{
namespace LoginControl
{
// Implementation of a Login User Control
public class LoginControl : System.Windows.Forms.UserControl
{
private System.Windows.Forms.Label lblUserName;
private System.Windows.Forms.Label lblPassword;
private System.Windows.Forms.TextBox txtUserName;
private System.Windows.Forms.TextBox txtPassword;
private System.Windows.Forms.Button btnLogin;
private System.Windows.Forms.ErrorProvider erpLoginError;
private System.Windows.Forms.StatusBar stbMessage;
private System.ComponentModel.Container components = null;
 
// Here we use the predefined System.EventHandler delegate.
// This delegate is useful if you want to define an event
// that has no additional data. The event will be passed an
// empty System.EcentArgs parameter instead. This is the
// delegate used by many of the Windows Forms.

public delegate void EventHandler(Object sender, EventArgs e);
public event EventHandler LoginSuccess;
public event EventHandler LoginFailed;
 

// Constructor
public LoginControl()
{
InitializeComponent();
}
 
….
….
 
// This is the very simple Login Check Validation
// The Password mus be … “secret” …..

private bool LoginCheck(string pName, string pPassword)
{
return pPassword.Equals(“secret”);
}
 
// Validate Login, in any case call the LoginSuccess or
// LoginFailed event, which will notify the Application’s
// Event Handlers.

private void loginButtonClicked(object sender, System.EventArgs e)
{
// User Name Validation
if (txtUserName.Text.Length == 0)
{
erpLoginError.SetError(txtUserName,”Please enter a user name”);
stbMessage.Text = “Please enter a user name”;
return;
}
else
{
erpLoginError.SetError(txtUserName,””);
stbMessage.Text = “”;
}
 
// Password Validation
if (txtPassword.Text.Length == 0)
{
erpLoginError.SetError(txtPassword,”Please enter a password”);
stbMessage.Text = “Please enter a password”;
return;
}
else
{
erpLoginError.SetError(txtPassword,””);
stbMessage.Text = “”;
}
 
// Check Password
if (LoginCheck(txtUserName.Text, txtPassword.Text))
{
// If there any Subscribers for the LoginSuccess
// Event, notify them …

if (LoginSuccess != null)
{
LoginSuccess(this, new System.EventArgs());
}
}
else
{
// If there any Subscribers for the LoginFailed
// Event, notify them …

if (LoginFailed != null)
{
LoginFailed(this, new System.EventArgs());
}
}
}
 
// Read-Write Property for User Name Label
public string LabelName
{
get
{
return lblUserName.Text;
}
set
{
lblUserName.Text = value;
}
}
 
// Read-Write Property for User Name Password
public string LabelPassword
{
get
{
return lblPassword.Text;
}
set
{
lblPassword.Text = value;
}
}
 
// Read-Write Property for Login Button Text
public string LoginButtonText
{
get
{
return btnLogin.Text;
}
set
{
btnLogin.Text = value;
}
}
 
// Read-Only Property for User Name
[Browsable(false)]
public string UserName
{
set
{
txtUserName.Text = value;
}
}
 
// Read-Only Property for Password
[Browsable(false)]
public string Password
{
set
{
txtPassword.Text = value;
}
}

}
}
}

Using the Login Validation User Control in a Windows Application

Create a new Windows Application project named: TestApp. Add a reference to the Login Validation User Control DLL named: LoginControl.dll. Now you are ready to customize the Toolbox: Right-Click the Toolbox, .NET Framework Components, Browse, select the LoginControl.dll.

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace TestApp
{
// Test Application for the Login Validation User Control
public class TestApp : System.Windows.Forms.Form
{
private Akadia.LoginControl.LoginControl loginControl;
private System.ComponentModel.Container components = null;
 
….
….

 
[STAThread]
static void Main()
{
Application.Run(new TestApp());
}
 
// This Event is fired by the Login Validation User Control
private void LoginFailed(object sender, System.EventArgs e)
{
MessageBox.Show(“Login falied ….”, “Login Validation”,
MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}

 
// This Event is fired by the Login Validation User Control
private void LoginSuccess(object sender, System.EventArgs e)
{
MessageBox.Show(“Login success ….”, “Login Validation”,
MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}

}
}

Format Mask Control

Create the Format Mask Control

An inherited control example is one for a custom masked text box. A masked text box is one that automatically formats the user’s input into the correct format. For example, it may add dashes or brackets to make sure it looks like a phone number. This task is notoriously difficult. One useful tool is Microsoft’s masked edit text box, which is provided as an ActiveX control with previous versions of Visual Studio.

The example of a masked text box is important because it demonstrates how features (rather than data) might be added to an existing control by subclassing. The example is still quite limited-notably, it restricts deletions and the
use of the arrow keys. Tracking the cursor position, which is required to allow inline masked edits, results in a good deal of tedious code that only obscures the point.

using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;

namespace Akadia
{
namespace FormatMask
{
// Extended User Control to implement an Edit Mask Text Box
public class EditMask : System.Windows.Forms.TextBox
{
 // Fields
private string _mask;
 
// Properties
public string Mask
{
get { return _mask; }
set
{
_mask = value;
this.Text = “”;
}
}
 
// To use the masked control, the application programmer chooses
// a mask and applies it to the Mask property of the control.
// The number sign (#) represents any number, and the period (.)
// represents any letter. All other characters in the mask
// are treated as fixed characters, and are inserted automatically
// when needed. For example, in the phone number mask (###) ###-####
// the first bracket is inserted automatically when the user types
// the first number.

protected override void OnKeyPress(KeyPressEventArgs e)
{
if (Mask != “”)
{
// Suppress the typed character.
e.Handled = true;
 
string newText = this.Text;
 
// Loop through the mask, adding fixed characters as needed.
// If the next allowed character matches what the user has
// typed in (a number or letter), that is added to the end.

bool finished = false;
for (int i = this.SelectionStart; i < _mask.Length; i++)
{
switch (_mask[i].ToString())
{
case “#” :
// Allow the keypress as long as it is a number.
if (Char.IsDigit(e.KeyChar))
{
newText += e.KeyChar.ToString();
finished = true;
break;
}
else
{
// Invalid entry; exit and don’t change the text.
return;
}
case “.” :
// Allow the keypress as long as it is a letter.
if (Char.IsLetter(e.KeyChar))
{
newText += e.KeyChar.ToString();
finished = true;
break;
}
else
{
// Invalid entry; exit and don’t change the text.
return;
}
default :
// Insert the mask character.
newText += _mask[i];
break;
}
if (finished)
{ break; }
}
 
 // Update the text.
this.Text = newText;
this.SelectionStart = this.Text.Length;
}
// base.OnKeyPress(e);
}
 
// Stop special characters.
protected override void OnKeyDown(KeyEventArgs e)
{
e.Handled = true;
}
}
}
}

Using the Edit Mask User Control in a Windows Application

Create a new Windows Application project named: TestApp. Add a reference to the Edit Mask User Control DLL named: FormatMask.dll. Now you are ready to customize the Toolbox: Right-Click the Toolbox, .NET Framework Components, Browse, select the FormatMask.dll.

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace TestApp
{
// Test Application for the Edit mask User Control
public class TestApp : System.Windows.Forms.Form
{
private Akadia.FormatMask.EditMask editMask;
private System.Windows.Forms.Label lblText;
private System.ComponentModel.Container components = null;
 
public TestApp()
{
InitializeComponent();
}
……
        private void InitializeComponent()
{
….
this.editMask.Location = new System.Drawing.Point(93, 63);
this.editMask.Mask = “[###]-(##)-#####”;
this.editMask.Name = “editMask”;
this.editMask.TabIndex = 0;
….
}
 
static void Main()
{
Application.Run(new TestApp());
}
}
}

Toggle Button User Control

Create the Toggle Button User Control

The Toggle Button User Control is an inherited control When the user clicks a toggle Button, the Text and BackColor properties should be set according to the Checked state of the button. The natural place to do this is the Click event. However, keep in mind that you only want to extend the default Click event supplied with the CheckBox class rather than replacing is. In the .NET Framework documentation, you will be notice that controls typically have a protected OnXXX method that raises each event (where XXX is the name of the event) – for example the Click event is raised by the OnClick method. The Control call these methods when an event occurs. If you want to extend the Click event, the Trick is therefore to override the OnClick method.

If the Appearance value is set to Appearance.Normal, then the check box has a typical appearance. If the value is set to Button, the check box appears like a toggle button, which may be toggled to an up or down state.

using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;

namespace Akadia
{
namespace ToggleButton
{
// The ToggleButton class is inherited from the
// System.Windows.Forms.CheckBox Class

public class ToggleButton : System.Windows.Forms.CheckBox
{
// Fields
private string _checkedText;
private string _uncheckedText;
private Color _checkedColor;
private Color _uncheckedColor;
 
  // Constructor
public ToggleButton()
{
// If Appearance value is set to Appearance.Normal,
// the check box has a typical appearance.
// If the value is set to Button, the check box appears
// like a toggle button, which may be toggled to
// an up or down state.

this.Appearance = Appearance.Button;
 
// Set Default toggled Text
this._checkedText = “Checked”;
this._uncheckedText = “Unchecked”;
 
// Set Default toggled Color
this._checkedColor = Color.Gray;
this._uncheckedColor = this.BackColor;
}
 
// Public Properties, can be accessed in Property Panel
public string CheckedText
{
get { return this._checkedText; }
set { this._checkedText = value; }
}
 
public string UncheckedText
{
get { return this._uncheckedText; }
set { this._uncheckedText = value; }
}
 
public Color CheckedColor
{
get { return this._checkedColor; }
set { this._checkedColor = value; }
}
 
public Color UncheckedColor
{
get { return this._uncheckedColor; }
set { this._uncheckedColor = value; }
}
 
 // When the user clicks a toggle Button, the Text and
// BackColor properties should be set according to the Checked
// state of the button. The natural place to do this is
// the Click event. However, keep in mind that you only
// want to extend the default Click event supplied with
// the CheckBox class rather than replacing is. In the .NET
// Framework documentation, you will be notice that controls
// typically have a protected OnXXX method that raises each
// event (where XXX is the name of the event) – for example
// the Click event is raised by the OnClick method. The Control
// call these methods when an event occurs. If you want to
// extend the Click event, the Trick is therefore to override
// the OnClick method.

protected override void OnClick(EventArgs e)
{
base.OnClick(e); // Call the CheckBox Baseclass
 
 // Set Text and Color according to the
// current state

if (this.Checked)
{
this.Text = this._checkedText;
this.BackColor = this._checkedColor;
}
else
{
this.Text = this._uncheckedText;
this.BackColor = this._uncheckedColor;
}
}
}
}
}

Using the Toggle Button User Control in a Windows Application

Create a new Windows Application project named: TestApp. Add a reference to the Toggle Button User Control DLL named: ToggleButton.dll. Now you are ready to customize the Toolbox: Right-Click the Toolbox, .NET Framework Components, Browse, select the ToggleButton.dll.

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace TestApp
{
// Test Application for the toggled CheckBox und Button
public class TestApp : System.Windows.Forms.Form
{
private Akadia.ToggleButton.ToggleButton btnToggle1;
private Akadia.ToggleButton.ToggleButton btnToggle2;
private Akadia.ToggleButton.ToggleButton btnToggle3;

private System.Windows.Forms.Label lblText1;
private System.Windows.Forms.Label lblText2;
private Akadia.ToggleButton.ToggleButton btnToggle4;
private System.ComponentModel.Container components = null;
 
public TestApp()
{
InitializeComponent();
 
// Set Appearance to CheckBox
btnToggle1.Appearance = Appearance.Normal;
btnToggle2.Appearance = Appearance.Normal;

}
        ……
private void InitializeComponent()
{
this.btnToggle1 = new Akadia.ToggleButton.ToggleButton();
this.btnToggle2 = new Akadia.ToggleButton.ToggleButton();
this.btnToggle3 = new Akadia.ToggleButton.ToggleButton();

…..
        }
 
static void Main()
{
Application.Run(new TestApp());
}
}
}

 (http://www.akadia.com/services/dotnet_user_controls.html#

Publising%20and%20Subscribing%20Events)