Team LiB
Previous Section Next Section

Coding Conventions

Too often projects are driven without coding conventions and good rules to follow for the developers involved. The result will often be nondesirable—different layouts of code, different implementations of error handling, and so on. Following is an overview of some areas of coding conventions that you can use out of the box in your own projects to ensure that the code produced is similar regardless of the developer behind the created class. Many of the conventions that follow are common sense, but we will list them here anyway to emphasize their importance. This section is based on Microsoft's coding conventions, and extracts the most important rules for enterprise applications. The complete coding conventions can be found at http://msdn.microsoft.com/library/en-us/vsent7/html/vxconCodingTechniquesProgrammingPractices.asp?frame=true.

Comments

Commenting the code is very important for spreading the knowledge of what the code does, and for maintenance of the code in future releases. Many programmers have problems reading their own code after a couple of months, so what if someone other than the original programmer is going to read code after the same amount of time has passed? It is better to spend ten minutes extra in a class commenting it than to spend ten hours a half-year later figuring out what the class does—or even worse, not being able to figure out what the class does and having to write a new one.

If developing in C#, use the XML Documentation feature. There exists a couple of ways you can document your code. The first one, traditionally used, is through standard templates. If you are using C#, you can use the built-in standard documenter—similar to Java's Javadoc, which gives you the ability to extract comments automatically. Unfortunately, this feature is only implemented in C#. Other languages such as VB .NET do not have this nice function. In VB .NET, you need to document the traditional way or introduce user-defined attributes on methods and functions. By using attribute-based documenting, you can extract the documentations of a class via the reflection API. Because the attributes are stored together with the code, the documentation always follows the code. This makes it possible to retrieve the documentation without having the source code, which is a great benefit compared to the C# solution or traditional boilerplates.

When modifying code, always keep the commenting around it up to date. Whatever kind of approach you have to document your code, you need to update the documentation when modifying the code. It is worse to dive into old code that has comments mismatching the logic than to watch a Police Academy sequel.

Puta comment at the beginning of every routine and class. Try to put a comment header at the beginning of every routine and class. It is helpful to provide standard comments indicating the routine's purpose, assumptions, and limitations. A comment should be a brief introduction that explains why the routine exists and what it can do. If you are not using C# you can use the following boilerplate:

'Purpose:        Traps the click event of the first group's combo box
'Assumptions:    None
'Effects:        The click event fires when the selection changes.
'Inputs:
'None
'Returns:
'None

A slightly modified version of the template should also be used for the class itself. The class-level comment header may look like this:

'Project:
'Name:           myclass
'Description: Client component of the myapp Tool
'              Provides functionality to manage very large groups
'Module
'Name             frmMain
'Description     Presentation tier of the component
'History
'30JAN2003       Mr Bond         Created from template

If you are using C#, use the built-in functionality for commenting the code. An example of a header for a function may look like this:

/// <summary>
/// Class-level summary documentation goes here.</summary>
/// <remarks>
/// Longer comments can be associated with a type or member
/// through the remarks tag</remarks>
public class SomeClass
{
    /// <summary>
    /// Store for the name property</summary>
    private string myName = null;
    /// <summary>
    /// The class constructor. </summary>
    public SomeClass()
    {        // TODO: Add Constructor Logic here
    }
Note 

All comments begin with /// and contain simple XML tags for telling the compiler what kind of data it is. By using this kind of documentation in C#, you are able to auto-generate the documentation (for example, extracting the comment tags from the code).

Avoid adding comments at the end of a line of code. Even if it is easy to create a lot of comments, and quite fun when you can auto-generate the documentation, we recommend you avoid adding comments at the end of a line of code; end-line comments make code more difficult to read. However, end-line comments are appropriate when annotating variable declarations, in which case you align all end-line comments at a common tab stop.

Avoid clutter comments. Also avoid comments that clutter, such as an entire line of asterisks. Instead, use white space to separate comments from code. Do not surround a block comment with a typographical frame. It may look attractive, but it is difficult to maintain.

Clean up comments before check-in. Before checking in a modified class to SourceSafe (or whatever kind of version handling system you are using), always remove all temporary or extraneous comments to avoid confusion during future maintenance work. This is especially important before delivering to production.

Avoid commenting too complex parts of code—rewrite instead. If you realize that you need to put several sentences to describe a complex part of your code, your code is probably too difficult and you should try to rewrite the code to something more easily understood. Even if you today understand the comments you have added, you will probably not have an idea four months from now what the code does. Although performance should not typically be sacrificed to make the code simpler for human consumption, a balance must be achieved between performance and maintainability.

Comment should clarify, not confuse. Use complete sentences when writing comments. Comments should clarify the code, not add ambiguity. Comment at the same time as you code because you will not likely have time to do this later. Also, should you get a chance to revisit code you have written, what is obvious today probably will not be obvious six weeks from now.

Avoid humoristic comments. Avoid superfluous or inappropriate comments, such as humorous asides.

Comment anything that is not readily obvious in the code. Use comments to explain the intent of the code. They should not serve as inline translations of the code.

Use comment techniques that can be auto-extracted. Use comments based on the C# comment specification to be able to auto-generate documents for classes. If working with code written in VB .NET, you can use custom attributes for documenting and thereby be able to extract the documentation programmatically.

Comment bug fixes. To prevent recurring problems, always use comments on bug fixes and workaround code, especially in a team environment. These should be applied in the class comment header and also in the version of the file that is checked into Clear Case or Visual SourceSafe.

Use comments on code that consists of loops and logic branches. These are key areas that will assist source code readers.

Separate comments from comment delimiters with white space. Doing so will make comments obvious and easy to locate when viewed without color clues.

Note 

Despite the availability of external documentation, source code listings should be able to stand on their own, because hardcopy documentation can be misplaced. External documentation should consist of specifications, design documents, change requests, bug history, and the coding standard that was used.

Naming

The naming conventions you should follow in general are those outlined by the .NET Framework Developer Specifications (see Microsoft's design guidelines for class library developers at http://msdn.microsoft.com/library/en-us/cpgenref/html/cpconnetframeworkdesignguidelines.asp and http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsent7/html/vxconcodingtechniques.asp), but here we will mention some of the rules that we think can boost the development speed and maintenance speed of an enterprise application.

Class Naming Guidelines

When creating a class, name them with nouns or noun phrases. Also use Pascal casing (for example, CalculateInvoiceTotal) and try to use abbreviations in class names sparingly. Do not use any class prefix (such as C) since that will not add any information for the programmer—the interfaces have a prefix of I, which is enough. Do not use an underscore in the name because it takes space and seldom gives any extra information to the programmer (it does not make it easier to read, either).

An example of a class following these naming guidelines is shown here:

public class Account {
}
public class Salary {
}
public class Company {
}

Interface Naming Guidelines

When creating interfaces, we recommend you follow some common rules that give your interfaces a common look and feel, which will make it easier for others to use and maintain them.

The name of the interface should be created with nouns or noun phrases, or adjectives describing behavior—for example, IComponent (descriptive noun), ICustomAttributeProvider (noun phrase), and IPersistable (adjective). In our examples, we use Pascal casing and try to write out the complete name—abbreviations mostly make it unclear what an interface is for.

Try to avoid using underscores in the interface name, since they only take up space and seldom mean anything to the observer.

Prefix the interface names with the letter I to indicate that the type is an interface and can easily be separated from the classes in your project. However, do not prefix class names with C, which has no meaning in this case because you have a prefix for the interfaces.

When you are designing class/interface pairs, try to keep the names similar. For example, the difference should be that the interface has an I in front of the interface name, as follows:


public interface IComponent {
}
public class Component : IComponent {
}
public interface IServiceProvider{
}
public interface IFormatable {
}

Enumeration Naming Guidelines

UsePascal casing for enumerations and for their enumeration value names. Try to avoid using abbreviations in enumeration names and enumeration values and do not use a prefix/suffix on enumeration names (for example, adXXX for ADO enumerations, rtfXXX for rich text enumerations, and so on). When you construct enums, try to use singular names except for bit fields that have plural names. Try to use enums for parameters where the parameter has a defined range of possible values, as shown in the following example. This allows the development environment to know the possible values for a property or parameter.

public enum FileMode{
    Create,
    CreateNew,
    Open,
    OpenOrCreate,
    Truncate
}

If you are going to use an enum in bitwise operations, add the Flags custom attribute to the enum to enable this as follows:

[Flags]
public enum Bindings {
    CreateInstance,
    GetField,
    GetProperty,
    Static
}

An exception to this rule is when encapsulating a Win32 API—it is common to have internal definitions that come from a Win32 header. It is okay to leave these with Win32 casing, which are usually all uppercase.

Try to use Int32 as the underlying type of enum. An exception to this rule is if the enum represents flags and there are many of them (over 32), or you anticipate the enum might grow too many flags in the future, or the type needs to be different from the integer type for backward compatibility. Only use enums if the value can be completely expressed as a set of bit flags. Never use them for open sets (such as operating system version and so on).

Read-Only and Const Field Names

When you are defining constants, try to name static fields with nouns, noun phrases, or abbreviations for nouns. Use the Pascal casing as for most other types, but do not use the Hungarian type notation.

Parameter Names

Parameter names should be descriptive enough so that in most scenarios the name of the parameter and its type can be used to determine its meaning. We recommend using camel casing (for example, typeName, as shown in the following code snippet) for parameter names. Aim to name a parameter based on a parameter's meaning rather than based on the parameter's type. Programmers expect development tools to provide information about type in a handy manner, so the parameter name can be put to better use describing semantics instead of type. We therefore suggest abandoning the Hungarian type notation, which takes up some space and is difficult for everyone to strive for. (We all get tired at the end of a project and start missing the notation, don't we?) Do not use reserved parameters. If more data is needed in the next version, a new overload can be added.

Type GetType (string typeName)
string Format (string format, object [] args)

Method Naming Guidelines

Method names should be named with verbs or verb phrases using Pascal casing, as demonstrated in these examples:

RemoveAll(), GetCharArray(), Invoke()

Property Naming Guidelines

Property names should be constructed from nouns or noun phrases, again with Pascal casing. We recommend not using properties and types with the same name.

Defining a property with the same name as a type can cause some ambiguity. It is best to avoid this ambiguity, unless there is a clear justification for not doing so. For example, System.WinForms has an Icon property, and even though there is an Icon class, Form.Icon is so much easier to understand than Form.FormIcon or Form.DisplayIcon, and so on.

However, System.WinForms.UI.Control has a color property. But because there is a Color class, the Color property is named BackgroundColor, as it is a more meaningful name that does not conflict.

Following are a few examples of property names:

Text, LastIndex, Value[5]

Event Naming Guidelines

When creating event handlers, name them with the EventHandler suffix as follows:

Public delegate void MouseEventHandler (object sender, MouseEvent e);

The event handler should have two parameters, sender and e. The sender parameter represents the object that raised the event. The sender parameter is always of type object, even if it is possible to employ a more specific type. The state associated with the event is encapsulated in an instance of an event class named e. Use an appropriate and specific event class for its type.

public delegate void MouseEventHandler(object sender, MouseEvent e);

Classes that handle events should be named with the EventArgs suffix as follows:

public class MouseEventArgs : EventArgs {
    int x, y;
    public MouseEventArgs(int x, int y)
        { this.x = x; this.y = y; }
    public int X { get { return x; } }
    public int Y { get { return y; } }
}

Event names that have a concept of pre- and post-operation should be prefixed using the present and past tense (do not use the BeforeXxx\AfterXxx pattern). For example, a Close event of a file that could be canceled would have events named Closing and Closed.

public event ControlEventHandler Closing {
 //..
}

Always try to name events with a verb.

Variables Naming Guidelines

Naming of variables in an enterprise application is important. It is much easier to maintain an application with variable names that describe the use of those variables than with an application that has single-character variables that do not give any clues to the programmer how those variables are used.

The following variable naming guidelines is an extract of the complete guidelines that can be found at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsent7/html/vxconcodingtechniques.asp:

  • Since most names are constructed by concatenating several words, use mixed-case formatting to simplify reading them. In addition, to help distinguish between variables and routines, use Pascal casing (CalculateInvoiceTotal), in which the first letter of each word is capitalized, for routine names. For variable names, use camel casing (documentFormatType), in which the first letter of each word except the first is capitalized.

  • Boolean variable names should contain Is, which implies yes/no or true/false values, such as fileIsFound.

  • Avoid using terms such as Flag when naming status variables, which differ from Boolean variables in that they may have more than two possible values. Instead of documentFlag, use a more descriptive name such as documentFormatType.

  • Even for a short-lived variable that may appear in only a few lines of code, you should still use a meaningful name. Use single-letter variable names, such as i, or j, for short-loop indexes only.

  • The Hungarian naming convention prefix all variables with their scope and their data type. An example is: dim g_blnIsDeleted as Boolean where the g stands for global scope of the variable, the bln for Boolean.

Note 

If using Charles Simonyi's Hungarian naming convention, or some derivative thereof, develop a list of standard prefixes for the project to help developers consistently name variables; however, with IntelliSense support, there is no need to use the Hungarian type convention.

Database Conventions

Designing the database is as important as designing the rest of the enterprise application. Without naming conventions for the database and its structure, the database will soon end up in such a messy state that no database administrator will be able to fix it without major changes. The following conventions are what we recommend to streamline the layout of the database to ease future maintenance.

First of all, limit the length of names and fields to 30 characters maximum. Some databases cannot handle more than 30 characters for some types, and a field name like thisIsMyFieldThatIamUsingForCalculatingTheAccountStatus is quite messy.

The name of the object should start with an alphabetic character. The remaining characters should be letters or numbers. Try to avoid the _, @, #, and $ characters since they also may confuse the user or database administrator. One thing you should avoid for certain is having blanks in the name. Also avoid using reserved words for Transact-SQL or PL/SQL, as they can give you trouble.

Because enterprise applications are often used in companies whose employees speak different languages (and you therefore can assume that the administration of the application is multilanguage), use only English-language characters in the design of the database—no local characters—to avoid code-page–related problems, and also to ease the maintenance of the application. Write out all names with whole words so they are easy to comprehend. Names containing more than one word should be written as one word with leading capitals for every word except for the first word it contains—for example, "customer number" should be written as "customerNumber".

Table 5-1 summarizes the naming conventions for databases.

Table 5-1: Database Naming Conventions

Object

Naming Convention

Example

Database

<database name>

Customer

Data device

<database name>Data

customerData

Log device

<database name>Log

customerLog

Table

t_<table name>

t_customer

Column

<column name>

Name

View

v_<view name>

v_Mycustomer

Stored procedure

p_<procedure name>

p_Getcustomer

Trigger

t_<i/u/d>_<table name>

t_i_customer

User-defined type

tp_<type name>

tp_Address

Primary key

pk_< table name>_<key name>

pk_customer_Id

Foreign key

fk_< table name >_ <table name 2>_<keyname>

fk_customer_Order_Id

Unique constraint

u_< table name >_<column name>

u_customer_Name

Check constraint

c_< table name >_<column name>

c_customer_Status

Default constraint

df_< table name >_<column name>

df_Address_City

Index

i_<c/n/u>_<table name>_ <column name>

i_n_customer_Name

The following should help clarify the conventions presented in Table 5-1:

  • A database name can be the same as the name of the application it serves.

  • Table names should always be singular.

  • Stored procedures should not be confused with system stored procedures, and therefore sport the prefix p_, not sp_.

  • Triggers should have a name depending on the type: i = insert, u = update, d = delete.

  • Indexes should be named based on the type: c = clustered, n=nonclustered, u = unique. For example, a unique clustered index for customer number in a table named customer might appear as follows:

    i_cu_customer_ Customernumber
    
Note 

Try to use stored procedures and functions, which are faster to execute than a standard SQL query, as much as possible to achieve good performance on the system. Because an enterprise application seldom will be moved between different platforms and databases, it is possible to use stored procedures. For a standard application that may exist on a different platform, use of stored procedures may require different database access layers for the different types of databases and environments.

Error Handling and Exceptions

The following conventions for error raising are extracted from the complete guidelines that can be found at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconerrorraisinghandlingguidelines.asp.

A multilayered application can detect errors at any level and pass them back through its various layers. For example, an error in the data access layer that occurs in response to a SQL query error may eventually be displayed to a user via an ASP.NET page on the Web layer. To display error messages in an ASP.NET application, you use a custom error-handling ASP.NET page named errorpage.aspx. The errorpage.aspx file is specified in the <customErrors> section of the Web.Config file:

<customErrors defaultRedirect="errorpage.aspx" mode="On" />

Error handling in layered architectures is more complex than in nonlayered counterparts. Adding to this complexity is the necessity of the application to display helpful, nontechnical error messages to users. Following are a few recommended error-handling practices:

  • Return codes: Use return codes to report status conditions.

  • Preconditions: The system should test for preconditions in public methods.

  • Post conditions: The system should check on the exit from a function as appropriate.

Structured Exception Handling

Use the try-catch-finally statement in C# to do the following:

  • Handle situations involving a function that cannot fulfill its contract.

  • Catch expected error conditions—but do not rely on exceptions. What we mean is that you should validate the data with code, not validate it by catching an error that occurs because of invalid data.

  • Ensure cleanup after unexpected exception conditions.

Error Raising and Handling

Following are some useful conventions for error raising and handling:

  • Do end Exception class names with Exception to separate them from normal classes.

    public class FileNotFoundException : Exception {
    }
    
  • Use at least these common constructors for your own exception classes:

    public class XxxException : Exception {
        XxxException() { }
        XxxException(string message) { }
        XxxException(string message, Exception inner) { }
    }
    
  • We recommend using the predefined exception types. Only define new exception types for programmatic scenarios. Introduce a new exception class so a programmer can take a different action in code based on the exception class. Do not define a new exception class unless it is possible a developer might need the exception class.

    For example, it makes sense to define FileNotFoundException because the programmer might decide to create the missing file, whereas FileIOException is not something that would typically be handled specifically in code.

  • In exceptions that do not have an explicit message, make sure the message property traverses down the tree to the next real text.

  • Do not derive new exceptions from the base class Exception. The base class will have multiple subclasses according to the individual namespace.

  • Remember that under the system exception will be another container exception called Core, which will contain the handful of EE exceptions that bring the system down completely. Users cannot throw these exceptions.

  • Group new exceptions off the base class Exception by namespace. For example, subclasses will exist for XML, IO, Collections, and so on; each of these areas subclass their own exceptions as appropriate. Any exceptions that other library or application writers wish to add will extend the Exception base class directly. A single name for all related exceptions should be made, and all exceptions related to that application or library should extend from that bucket.

  • Use a localized description string. An error message displayed to the user will be derived from the description string of the exception that was thrown, and never from the exception class. Include a description string in every exception to clarify for the end user what happened.

  • Use grammatically correct error messages including ending punctuation. Each sentence in a description string of an exception ends in a period. Code that generically displays an exception message to the user handles the case when a developer forgets the final period.

  • Provide Exception properties for programmatic access. Include extra information in an exception (besides the description string) only when there exists a programmatic scenario in which that additional information would be useful. It is rare to need to include additional information in an exception.

  • Throw exceptions only in exceptional cases:

  • Do not use exceptions for normal or expected errors.

  • Do not use exceptions for normal flow of control, as there is an overhead for generating exceptions instead of checking that you have valid values.

  • Return null for extremely common error cases. For example, File.Open returns a null if the file is not found, but throws an exception if the file is locked.

  • Design classes so that in the normal course of use there will never be an exception thrown. For example, a FileStream class exposes another way of determining if the end of file has been reached to avoid the exception that will be thrown if the developer reads past the end of the file. For example:

    class Foo {
        void Bar() {
            FileStream stream = File.Open("mytextfile.txt");
            byte b;
             // ReadByte returns -1 at EOF
            while ((b = stream.ReadByte()) > = 0) {
                // Do something
            }
        }
    }
    
  • Throw an InvalidOperationException if in an inappropriate state. The System.InvalidOperationException exception is supposed to be thrown if the property set or method call is not appropriate given the object's current state.

  • Throw an ArgumentException or subclass thereof if bad parameters are passed. When bad parameters are detected, throw a System.ArgumentException or a subclass thereof.

  • Do realize that the stack trace starts at the throw. The stack trace's origin is located where the exception is thrown, not where it is reviewed. Be aware of this fact when deciding where to throw an exception.

  • Use exception builder methods. It is common for a class to throw the same exception from different places in its implementation. To avoid code bloat, use a helper method that creates a new exception and return it. For example:

    class File {
        string fileName;
    
        public byte[] Read(int bytes) {
            if (!ReadFile(handle, bytes))
                throw NewFileIOException();
        }
          FileException NewFileIOException() {
         string description = // Build localized string,
    //including fileName
             return new FileException(description);
         }
    }
    

    Another alternative is to use the constructor of the exception to build the exception. This is more appropriate for global exception classes like ArgumentException.

  • Throw exceptions in favor of returning an error code (or HResult).

  • Throw the most specific exception possible.

  • Use existing exceptions where possible instead of creating new ones.

  • Set all the fields on the exception you use.

  • Use inner exceptions (chained exceptions).

  • Create meaningful message text targeted at the developer in the exception.

  • Do not include methods that throw NullReferenceException or IndexOutOfRangeException.

  • Do argument checking on protected (family) members. Write clearly in the documentation if the protected method does not do argument checking. Unless otherwise stated, assume argument checking is done. You may get some performance gains in not doing argument checking.

  • Clean up intermediate results when throwing an exception. Callers should be able to assume that there are no side effects when an exception is thrown from a function.

    For example, if Hashtable.Insert throws an exception, then the caller can assume that the item was not added to the hash table (the hash table implementation breaks this rule, but that's another issue).

Miscellaneous

Try to minimize the use of abbreviations, but use those that you have created consistently. An abbreviation should have only one meaning; likewise, each abbreviated word should have only one abbreviation. For example, if you use min to abbreviate minimum, do so everywhere, and do not use min to also abbreviate minute. When naming functions, include a description of the value being returned, such as GetCurrentWindowName().

File and folder names, like procedure names, should accurately describe their purpose.

Avoid reusing names for different elements, such as a routine called ProcessSales() and a variable called iProcessSales.

Avoid homophones, such as write and right, when naming elements to prevent confusion during code reviews.

When naming elements, avoid commonly misspelled words. Also, be aware of differences that exist between American and British English, such as color/colour and check/cheque.

Memory Management

It is good practice to create required objects as late as possible and release them as soon as possible. Hanging onto a resource for as short a time as possible helps you to keep the memory footprint down. Use the Dispose method to tell the garbage collector that you have finished working with component objects. Try to avoid using the Finalize function, because you do not know exactly when it is run. If you are using the Finalize function, also use the Dispose method to trigger the Finalize function.

public void Dispose()
{
    // Clean up unmanaged resources
    GC.SuppressFinalize(this);
}
protected override void Finalize()
{
    // Clean up unmanaged resources
    base.Finalize();
}

Another good practice is to use.NET Enterprise Services for housekeeping of resources on the server. By using .NET Enterprise Services, you will be able to define an object pool in which used objects will reside until clients need them. As soon as a call to an object is invoked, the object from the pool is returned to the client (for example, the layer). When the method finishes, the object is returned to the pool and not destroyed if the AutoComplete attribute is set. A small number of objects are then used between different client requests, thereby reducing the resource usage on the server. Remember to call the Dispose method on the component to tell the garbage collector that the object can be cleaned up.

Data Access Strategy

Data access strategy deals with performance optimization, deployment, and scalability of an application. The strategy focuses on a design in which all data is passed to the client in a method call and all database resources are held for a set period of time. (This is also true for all your objects in all layers—hold them alive as short a time as possible to reduce the resources in use.)

Pass All Data Back to the Client in a Method Call

In general, stateless objects produce highly scalable solutions. The data access classes are stateless; that is, they do not keep state in instance data members. The client passes all data required for a particular action to a method, and the method passes all resulting data back to the client.

This approach simplifies resource management by freeing every data access object following any method call. As a result, the client can use any data access object to make a method call, because all the required inputs are passed to the object with the call.

As a consequence, you need to keep transaction lifetime longer than any of the connections to the database. The standard transactional support in Component Services (COM+) that uses the Distributed Transaction Coordinator (DTC) will be employed to accomplish this (see the section "Transaction Strategy"). By using the JIT and the object pooling support found in COM+, you can reduce the overhead to create new objects for each call.

Hold Database Resources for a Minimum Length of Time

Database resources are limited and expensive. The data access layer postpones database resource allocation as long as possible and frees database resources as soon possible. The data access classes implement Finalize/Dispose semantics to close the active database connection. The data access objects are allocated and released at the point of a database call in the scope of either a using or try-finally block.

Transaction Strategy

Transactions are initiated in the business layer using the transactional support in COM+. Often, nested local transactions yield better performance than distributed transactions executed by serviced components running under control of the Distributed Transaction Coordinator (DTC). However, that performance is marginal compared to the increased complexity of the implementation, so use the support for transactions found in COM+.

Security

The key message regarding security is this: Find security issues in development, not in production. Try to run the development environment without administration privileges on your machine—you will be stunned how many times the application will hang.

You can either use imperative or declarative security. Declarative security is implemented as custom attributes on the class or method level. This kind of security check is often an acceptable solution, since many classes and methods should only be accessed by certain groups of users. The following example shows how a declarative security check is added to the DoWithdrawAlot method to only allow users of the role Clerks to execute it:


[PrincipalPermission(SecurityAction.Demand,Role="Clerks")]
void DoWithdrawAlot()
{
         // Do the transaction...
}

Declarative security can also be added on the class level. If you have declarative security added on the class level and method level, the declarative security on the method level overrides the class level's security. Since these attributes are accessible via the Reflection API, you can read the permission sets for your classes and methods and print them out. An accurate documentation of the existing permission sets on the classes and methods in the enterprise application is very useful to have. Any changes in the permissions can be extracted out to documentation by using the Reflection API and thereby keeping the documentation up to date.

When you have a class with a method that needs fine-grained security check (for example, you need to verify that the user has access to parts of the code inside a method), the declarative way is not flexible enough. Instead you should use the imperative security check.

When using the imperative security check, you need to call the Demand method on the PrincipalPermission object to demand a verification of the current user. When you call the Demand method, the CLR does a verification that the current user belongs to the requested role, or is the requested user. Following is a short example of using the PrincipalPermission object to verify that the current user belongs to the correct role.

public void DoWithdrawAlot ()
{
    PrincipalPermission permission =
        new PrincipalPermission(null, "Clerks");
    permission.Demand();
    // Now do the transaction...
}

If the calling user does not belong to the Clerks role, a SecurityException will be raised from the Demand method.


Team LiB
Previous Section Next Section