Coding guideline - CatGlobe

From Catglobe Wiki
Revision as of 07:26, 22 December 2008 by Catglobe (talk | contribs)
Jump to: navigation, search

Introduction

This guideline contains CatGlobe-specific coding rules.

Exception

Exception handling

  • Only catch exception if you can do some thing with it.
  • All re-thrown exception must include the original exception in its inner exception.
  • DO: always catch specific exception type. You are allowed to catch generic exception and re-throw a specific exception; for example in CGScript all exceptions are caught and a RuntimeException is re-thrown afterward.
  • DO NOT: empty and undocumented catches; using unspecific catches, unless they are documented well in the design document and are approved by the PMs.

Create a new custom of exception

  • Naming rule: the new exception must be suffixed with “Exception”.
  • All custom exceptions must inherit from another exception class.
  • The exception should be serializable in order to use in CatTask service. Refer to Serializable exceptions for more details. Notice: when the new CatTask service is done, this may be obsolete.
  • Namespace rule:

        - If the being created exception is used in multiple places, it must be placed in the CatGlobe.Framework.Exceptions namespace.

        - Otherwise, if it is used for a specific class only, it can be placed in the namespace where it is used.

Working with threads

  • Usages of threads must be specified in the Technical design and approved by PMs.
  • Use CatGlobe.Framework.Security.AsyncHelper class to start a thread with auto-impersonate, access factory and webconfig manager.

Web pages – web controls

Code behind (ASPX and ASCX)

  • Code behind must be documented with request parameters, usage, purpose.
  • Use Register…(JavascriptConstant…) to include javascript files in the code behind.
  • Web page must inherit from the PageBase class, control must inherit from the ControlBase class and Web Service must inherit from the WebServiceBase class.
  • DO NOT override the OnError event, unless it is approved in the technical design.

ASPX file

  • Replace the auto-generated DOCTYPE by this one: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
  • A page must have a title using text resource.
  • All the validation warnings which the Visual Studio shows must be fixed.
  • Adding references to user controls and using of tagPrefixes: must following this guideline Tag prefixes for user controls and web controls
  • You are allowed to reference to javascript files in the aspx files directly. However, you must use the JavascriptConstant to reference to them.
  • If a page or a control contains more than 3 methods or more than 20 lines, move it to a separate js file.

ASCX file

  • DO NOT reference to javascript files in the ascx files.

Text resources

  • When you need to register a text resource to the client side, use the PageBase.CGClientScript.RegisterTextResource method. The method will encode registered text resources for you.
  • Setting for text resource files of web pages:

        - Need to be set to the [filename].aspx.resx file only.

        - Custom tool namespace: x (yes, only the letter 'x').

        - Build action: None

        - Custom tool: ResXFileCodeGeneratorEx

  • Setting for text resource files of an enumeration file:

        - Custom tool namespace: aenum

        - Build action: Embedded Resource

        - Custom tool: ResXFileCodeGeneratorEx

Events handling

  • When you override an event, always call to base class method.
  • Only handle an event when you have something to do with it. On this example, the OnInit method should be removed because we have nothing to do with it:
protected override void OnInit(EventArgs e)
{ 
 base.OnInit(e);
}

Request

  • All the arguments which can be passed to the url must be documented.
  • Each request parameter must have it own getter property. Handling of the request parameters must always be inside the getter properties.
  • All request parameter getters must be put in a region called “REQUEST PARAMETERS”.

Session

  • Use SessionManager to work with Session.
  • Objects which are put into the Session must be serializable.
  • Examples about good and bad usages of Session, in the share-data-between-pages context:

         - Good if the data is complicated and cannot be render to the clientside or pass via url easily. A good example is the search criterion of the search page.

         - Good if the data is incompleted, so it is not ready to be saved to the database yet, for example the data which is collected through a wizard: we collect some pieces of information in step 1, put them into Session and navigate to the step 2, and so on...

         - Good if the data takes too much resources (IO, CPU, time) to retrieved. For example a fully-initialized Questionnaire, means all of its questions, question properties, sub questions, answer options are initialized.

         - Bad if the data in Session can be changed by multiple pages/forms by multiple ways. For example: the page A shows some information about a report, which is put in the session. The user opens the Report compact dialog in another tab. When the user saves the report in the CRD, it is the database is updated, not the report object in the Session. Now the user backs to the page A and modifies the report. Obviously he will get a concurrency problem.

Javascript

  • Do not use browser-specific script. All the javascript must work in both IE and FireFox.
  • Javascript should be written in OOP style using AJAX .NET. One class should be in one file.
  • Functions must be documented about their usages and their parameters. Types of parameters must also be specified.
  • In order to utilize the intellisense feature of VS2008:

       - Put summary inside function:

Javascriptsummary.jpg

  • To get intellisense work in a javascript file, you have to add a reference to the source JavaScript file. There are two situations need to be considered here:

      - If the source JavaScript file is a embedded resource, use the declaration below:

               /// <reference name="registered name of that embeded JavaScript file" />

      - If the source JavaScript file is an included file, use the declaration below:

               /// <reference path="path file name of that JavaScript file"/>

Javascriptreference.jpg

  • To get intellisense work in a ASPX file:

      - If your page is implementing AJAX, reference to the source JavaScript file in ScriptManager control: <source lang="csharp" line="1"> <asp:ScriptManager runat="server" id="scriptManager"> <Scripts> <asp:ScriptReference path="virtual path file name of the JavaScript file" /> </Scripts> </asp:ScriptManager> </source>       - Otherwise, reference to the source JavaScript file in a script element declaration:

                <script src="virtual path file name of the JavaScript file" type="text/javascript" >

Data access layer

AccessFactory

  • Special uses of AF must be approved in the TD. They can be:

        - Where you need to create an AF by yourself.

        - Where you need to call Commit or Abort.

        - Where you need to call CloseOverwritenConnectionWithNoGlobalTransaction and OverwriteConnectionToNoGlobalTransaction. 

Access class

  • Every access class of a domain class needs two callback methods and two delegates which are used when creating and saving objects.
  • The callback method which is used when creating object is named Create. In this method, data is read from the IDataReader to build an object. Signature of the Create method is:
1 static private T Create(ref CreateOrd co, IDataReader reader)

where T is a domain class, for example User, Attachment or EmailAddress.

  • The callback method which is used when saving object is named Save. In this method, the primary key of the being saved object is read from the IDataReader and update to the object. If the object is a Resource, version is read and update too. Signature of the Save method is:
1 static private void Save(T emailerror, IDataReader reader)
  • Create two delegates for the two methods above. These delegates are passed to the AccessBase.Execute[Action] methods:
1 static readonly CreateDelegateGen<T> create = Create; 
2 
3 static readonly SaveDelegateGen<T> save = Save;

Building query

When building a query:

  • Only make your own sql builder function if you can’t find an appropriate one in the GenericSqlBuilder class.
  • All the methods in the MySqlBuilder layer must begin with “Build” and end with “Sql”.
  • Use StringBuilder to build your queries.
  • Always use provider constant to get table names and column names.
  • MySqlBuilder methods should take the same parameters as calling method from the Access layer, except when multiple methods from the Access layer call the same builder.
  • Make your input parameters as strict as possible. For example, if the input is a provider table of a resource view, its type shouldn’t be ProviderTable, but ProviderResourceViewTable.
  • Always use GetLiteral functions to convert a value to a string which can be appended to the query. Usage of GetLiteral is not required when data type of the value is number.
  • Append every condition of the where clause in a separate line. Operants of an OR operator should be written in one line.
  • Example:

Wrong:

1 StringBuilder sqlBuilder = new StringBuilder(); sqlBuilder.Append("SELECT ID"); sqlBuilder.Append(" FROM "); sqlBuilder.Append(" USER_RESOURCE "); sqlBuilder.Append(" WHERE "); sqlBuilder.Append(" NAME "); sqlBuilder.Append(" = "); sqlBuilder.Append(userName); sqlBuilder.Append(" AND "); sqlBuilder.Append(" (Access_Expiration &lt;= "); sqlBuilder.Append(CGDateTime.Now.ToString()); sqlBuilder.Append(" OR "); sqlBuilder.Append(" Access_Expiration is NULL) ");

Correct:

1 StringBuilder sqlBuilder = new StringBuilder(); sqlBuilder.AppendFormat("SELECT {0} ", PC.UserResource.Id); sqlBuilder.AppendFormat("FROM {0} ", PC.UserResource.TableName); sqlBuilder.Append("WHERE "); sqlBuilder.AppendFormat("{0} = {1} ", PC.UserResource.ShortName.Name, GetLiteral(userName)); sqlBuilder.AppendFormat("AND ({0} &lt;= {1} OR {0} is NULL)", PC.UserResource.AccessExpiration.Name, GetLiteral(CGDateTime.Now));


Performance

  • If using lazy evaluated property on a collection of objects, then the properties should be initialized before usage to prevent the lazy evaluation. Naming for that function is InitAll[PropertyName].
  • The InitAll[PropertyName] functions should be implemented using the CatGlobe.Domain.Common.GenericInitAllHelper class.
  • When you need to check for access of a user to many resources, do it once directly in the query.
  • Similarly for navigation accesses checking: there is a method to check for many navigation accesses in the PageBase class.
  • Use string.Format when you want to concatenate more than 2 string objects in one line.
  • Use StringBuilder when you want to concatenate string objects in many lines, or inside a loop.
  • Use generic types like Dictionary<> and List<> instead of HashTable, ArrayList. ArrayList can be used for return type of Server methods.
  • When you have a list of items, you may want to use Dictionary or List to store them. The rule is:when you want to store values to look up quickly, use the Dictionary class. Otherwise, use List.

Auto-generated files

There are a few auto-generated files in Catglobe:

  • ConfigurationConstants.
  • ProviderConstants.
  • Text resource classes.
  • Resx files.
  • Designer files.
  • SessionManager.
  • JavaScriptFileConstants
  • LINQ to XSD generated files.
  • WSDL generated files.
  • WCF generated files.

Rules:

  • DO NOT: modify it manually.
  • DO: modify the source file and then generate the source code file.


Webconfig

  • A new key must be approved by CORE.
  • You need to add a validation method for the new key in the WebconfigValidation class.
  • DO NOT: read the key directly from webconfig.
  • DO: make a new property in the WebconfigManager class to read it.


Data validation

  • Specify the maximum length of character input fields.
  • Specify the minimum and the maximum value of numeric input fields.
  • Validation, if possible, should be done first on the client side. Data then must always be validated again on the server side.
  • Try to use control which supports data-restriction. For example, the WebNumeric control allows us to specify the minimum and maximum values, allows numbers only.
  • Checking for database constraints, for example if a resource name already exists, should be done before data is actually inserted or updated to the database.
  • Validations may need to be done:
  • If the input value is in the valid range.
  • If length the input value is the valid range.
  • If it contains invalid character.
  • If it meets some specific constraints, for example an email address field must match the email naming rule.


Resource access – navigation access

  • Any page which requires navigation access must be check for navigation access in itself.
  • Any page which shows information of a resource to the user must check for resource access.
  • Given that in page A we have a button called Open which is used to open the page B, which shows information about a User. *Typically, we need to check:
    • In the page A:
      • Check for navigation access to show/hide or to enable/disable the Open button.
      • In some pages you may need to check for resource access to enable/disable the Open button.
    • In the page B: You need to check for both navigation access and resource access again. The reason for this check is that the user may copy the url to open a page and change some of its parameters to open another resource directly to bypass the check in the source page.


Server method

  • DO NOT call server method inside a FOR loop. There is almost always a way to collect all the needed data and call the server method just one time.
  • Important rules
  • Always put the call to a synchronous server method in a try-catch block on the client side. Recall to the empty try catch rule, you may need to show an error message to the user.
  • For asynchronous server methods, refer to ServerMethods' exception handling for more details.
  • Exception handling in a server method should follow the section about exception above.


Localization

  • Data which is shown to the user must be in the culture of the logged in user. In the case of date time, it is also required that the display value must be in the time zone of the logged in user. All exceptions must be specified in the feature design.
  • Use CGDateTime instead of DateTime in the Business logic layer and Domain layer. Usages of the .NET DateTime struct is restricted in GUI only, unless they are approved in TD.
  • If a GUI needs a control to work with date time, use the CGDateTimeControl. However, if the field is an editable-date-only, use the Infragistic.WebDateChooser control.
  • The Turkish culture has a lot of differences from other cultures. Refer to this link for more information: http://www.moserware.com/2008/02/does-your-code-pass-turkey-test.html
  • String representation of data which is saved to the database must be in Invariant culture.
  • String representation of data which is passed to the domain layer should also be in Invariant culture.
  • Date time which is stored in the database must be in UTC. If you need to store a string of date time value in the database, its format must be in Invariant Culture and its time zone must be UTC. This includes all data in XMLs.


Commit rule

For project:

  • Commit all the files in one commit is recommended. Commit log must contains the project id.
  • When committing bug fixes for a project, both task id and project id are required. In addition, there must be comment about what the bug is and what has been done to fix it.

For bug fix:

  • Task id is required.
  • Commit fix for one bug a time (sometimes one fix may kill many bugs).
  • Depend on the complexity and importance of the bug, provide proper comments.


General

Code changing

  • Whenever you modify a domain method, you must use the find all references feature to find all the places which the method is used and investigate if your change also affects them.
  • If you change code of another group, please inform them before you commit.
  • If you modify a public method which doesn’t have summary, add summary for it.
  • If you reformat/re-indent a class, commit it separately without any code change.

Utilizing C# 3.0 syntax

  • Fast declaration of get/set for simple property. Please notice that this syntax cannot be used if you need to initialize a field inline, for example initialize a CGDateTime field to MinValue.
  • Use lambda syntax instead of anonymous method.<span id="fck_dom_range_temp_1229918839047_580" />

ContactFolder missingFolder = contactFolders.Find(cf => cf.ContactFolderType == contactFolderType);
instead of
ContactFolder missingFolder = contactFolders.Find(delegate(ContactFolder cf) { return cf.ContactFolderType == contactFolderType; });

  • Use object initializers:
//Object initializer
User user = new User {Name = "CatGlobe", Password = "cg"};
// List initializer
List<string> people = new List<string>()
{
"Granville",
"John",
"Rachel",
"Betty",
"Chandler",
"Ross",
"Monica"
};
  • When you need to make a JavaScript version of a server side object encoded in JSON, use the CatGlobe.Framework.Serialization.CGJavaScriptSerializer class. Also be noticed that the anonymous type feature of C# 3.0 is a good choice to make an object to serialize to the clientside. Example:
var clientObject = new { Name = "CatGlobe", Value = "Hi there!", Accesible = false };
string json = CGJavaScriptSerializer.Serialize(clientObject);
Page.ClientScript.RegisterStartupScript(typeof(_Default), "Startup", string.Format("var clientObject = {0};", json), true);

The value of json is “{"Name":"CatGlobe","Value":"Hi there!","Accesible":false}”

Code reusing

  • Look for or ask for the needed APIs first. If it is not available, contact the PM to include it.
  • If need to use some piece of code twice or more, then make a function to do it. Even if it is just 2 lines!
  • Whenever you think that what you make may be used by other developer, or it can somehow be promoted to a common library, please contact with RnD to find a good place to put it.

Placement of code

  • Business logic does NOT belong in GUI.
  • What are business logics? Rule of Thumb: any code which doesn’t directly manipulate a GUI component is BL.
  • BL code which is used to make decision should be put in the Framework classes, not Domain classes.
  • A domain class:

       - Is an in-code instance of a database table.

       - Read data from and write data to the corresponding table.

       - Validate data before which may be saved to database.

  • A framework class:

       - Called module class in technical design.

       - Contains business logic code of a web page, calls domain classes to manipulate database's data.

       - Contains helper APIs which are used widely.

  • Helper classes that are used only inside some other class should not be defined it its own file and namespace. Instead it should be an inner class.
  • We alreay had a new project called CatGlobe.Common which contains all the common stuffs which are used across the solution.

Xml

  • It is required that every xml document must have an XSD. The XSD file is put in the same folder with the class which uses it. Set the Build action property to Embedded Resource.
  • Look for an appropriate existing AssemblyIncludedFiles class or create one new iIn the same folder. In this file:

      - Make a string constant for the path to the xsd file.

      - Make a GetStream method like the example below.

public static class AssemblyIncludedFiles
{
    public const string RULES_SCHEMA_PATH = "CatGlobe.Domain.PhoneNumbers.Rules.rules.xsd";
    static public System.IO.Stream GetStream(string filePath)
    {
        return typeof(AssemblyIncludedFiles).Assembly.GetManifestResourceStream(filePath);
    }
}

Working with XSD

  • Naming of the elements becomes naming of classes and properties, so you might want to make your xsd naming same as C# naming.
  • For the targetNamespace attribute: our suggestion is that you can use that of the XSD file, which means http://CatGlobe/<module>/<class>. For example, if the targetNameSpace is http://CatGlobe/Domain/Report/DashBoard/TimeSet, namespace then will become CatGlobe.Domain.Report.DashBoard.TimeSet
  • After the XSD is ready, generate a C# class for it by executing this command:

C:\Program Files\LINQ to XSD Preview\Bin\LinqToXsd.exe <path to your xsd> /fileName:<path to where you want your generated cs file>

  • Read the manual on how to use the generated class. It is located in C:\Program Files\LINQ to XSD Preview\Docs

Utility classes

  • Classes in the namespace CatGlobe.Framework.Text have many APIs which are used to work with strings:

     - Encode strings to use in javascript, in HTML.

     - Validate email addresses.

     - Convert a generic list to a string.

     - Escapse special characters.

     - Convert HTML to plain text.

     - Generate some kinds of strings.

  • CatGlobe.Framework.Security.SecurityManager: provides various APIs to check for resource access, navigation access as well as grant/ungrant resource access, navigation access.
  • CatGlobe.Framework.Network.Dns: have various classes to work with DNS. One of the most useful feature is that it can be used to check for the existence of both ARecord and MXRecord.
  • CatGlobe.Framework.Images.ImageUtilities: provides APIs to work with images:

     - Create thumbnails.

     - Get the link to an Image resource.

     - Get mime type.

  • CatGlobe.Framework.DateTimer.DateTimeHelper and CatGlobe.Framework.DateTimer.CGDateTimeConverter: provide various APIs to work with date times. You also can find standard format strings for date time there.
  • CatGlobe.Framework.DataStructure.SortingHelper: a class which is used to sort unsaved data of a grid control on the server side.
  • CatGlobe.Framework.Communication.Mail: provides various APIs to send mails out.
  • System.Collections.SerializableDictionary: a dictionary type which is xml-serializable.

How to create a CGScript library or function

  • CGScript functions are in the CatGlobe.Framework.Runtime namespace. You need to specify which library the function belongs to.
  • How to register new functions to the library:
  • Register new function to the symbol table: inside Register() method of this library class. Call the symbolTable.AddFunction method with the following syntax:

symbolTable.AddFunction("functionName", new FunctionIdentifier("functionName", new FunctionEventHandler(this.FunctionName), ConstantType.<return_data_type>));

where
FunctionName: name of new functions: such as isRequiredConstant, setBooleanConstant,...
this.FunctionName: a method for handling this new CGS function
ConstantType.<return_data_type>: return type of a new function

  • Put all register commands on the region “register constant functions”. Create a method for handling the CGS function logic. It must have the signature of :

private Constant FunctionName(FunctionIdentifier function)


In this method, we must follow the scenario:

  • Validate the number of arguments, throw runtime exception if it's wrong
  • Validate argument (type and its existence if needed), throw runtime exception if it's wrong
  • Execute its main logic
  • Return result as its <return_data_type>