C# Code Generation Overview

Author: Iqbal Khan

TierDeveloper generates .NET components in three layers. These are:

  • Business Layer: Contains two .NET components for each data object defined in a TierDeveloper project. They are:
    • Domain Object: This .NET component contains only the data members with "get" and "set" methods.
    • Business Interface: This is a "stateless" component; meaning that it does not have any state information (data members) in it.
    • Collection Object: This .Net component is used to return a collection of Domain Objects from a query, or passing same to an operation.
  • Data Layer: Contains implementation of data persistence (in factory classes) and definition of hooks. 
    • Standard Methods: These are "Load", "Insert", "Delete", and "Update".
    • Operations: These have custom names and are customized versions of "Insert", "Load", or "Update" methods. The difference here is that these methods may not access all the attributes of the data object. The reason for these methods is to allow custom "Load" or "Update" functions for performance optimization purposes.
    • Query Methods: These usually return a Collection Object for the corresponding component.
    • Related Query Methods: These end up calling a Query method in a related data object and return a collection of DomainObject2 components where DomainObject2 is the related data object.
    • Stored Procedures: These methods make stored procedure calls and return output parameters of the stored procedure and/or a collection of objects if the stored procedure returns a rowset.
    • Bulk Operation Methods: These are either "Bulk Update" or "Bulk Delete" methods defined in the TierDeveloper project. These methods do not return any data.
    • Custom Hooks: If the user has specified that certain methods in a data object should call custom hooks that the user will implement, the corresponding code for it is also generated. Hooks allow you to modify the behavior of data objects without changing the actual data object code.
  • Integration Layer: The Integration layer works as a Service Provider to the client. It contains a Service Provider class, which contains static methods to instantiate the factory interface objects.

Domain Object Component

In our example, the namespace is "Northwind" and the Domain Object is "Customers". Below is the code:

public class Customers : PersistentObject, IComparable {
    private OrdersCollection _Orders = null;
    public Customers () {
        SetupFields ();
    }
    /// <summary>
    /// Gets or sets the <c>CustomerID<</c> attribute value.
    /// </summary>
    /// <value>The <c>CustomerID</c> attribute value.</value>
    public String CustomerID {
        get {
            if (!this ["CustomerID"].Null)
                return (String) this ["CustomerID"].Value;
            else
                return null;
        }
        set {
            this ["CustomerID"].Value = value;
        }
    }
    /// <summary>
    /// Gets or sets the <c>CompanyName</c> attribute value.
    /// </summary>
    /// <value>The <c>CompanyName</c> attribute value.</value>
    public String CompanyName {
        get {
            if (!this ["CompanyName"].Null)
                return (String) this ["CompanyName"].Value;
            else
                return null;
        }
        set {
            this ["CompanyName"].Value = value;
        }
    }
    /// <summary>
    /// Gets or sets the <c>ContactName</c> attribute value.
    /// </summary>
    /// <value>The <c>ContactName</c> attribute value.</value>
    public String ContactName {
        get {
            if (!this ["ContactName"].Null)
                return (String) this ["ContactName"].Value;
            else
                return null;
        }
        set {
            this ["ContactName"].Value = value;
        }
    }
    /// <summary>
    /// Gets or sets the <c>ContactTitle</c> attribute value.
    /// </summary>
    /// <value>The <c>ContactTitle</c> attribute value.</value>
    public String ContactTitle {
        get {
            if (!this ["ContactTitle"].Null)
                return (String) this ["ContactTitle"].Value;
            else
                return null;
        }
        set {
            this ["ContactTitle"].Value = value;
        }
    }
    /// <summary>
    /// Gets or sets the <c>Address</c> attribute value.
    /// </summary>
    /// <value>The <c>Address</c> attribute value.</value>
    public String Address {
        get {
            if (!this ["Address"].Null)
                return (String) this ["Address"].Value;
            else
                return null;
        }
        set {
            this ["Address"].Value = value;
        }
    }
    /// <summary>
    /// Gets or sets the collection of <c>Orders</c> objects.
    /// </summary>
    /// <value>The <c>OrdersCollection</c> object.</value>
    public OrdersCollection Orders {
        get {
            if (_Orders != null) {
                return _Orders;
            } else {
                _Orders = new OrdersCollection ();
                return _Orders;
            }
        }
        set {
            _Orders = value;
        }
    }
    public  void SetNull (string szFieldName, bool bNull) {
        this [szFieldName].Null = bNull;
    }
    public  bool IsNull (string szFieldName) {
        return this [szFieldName].Null;
    }
    public  int CompareTo (object obj) {
        if (obj is Customers) {
            Customers cmpCustomers = (Customers) obj;
            int compareToValue = 0;
            if (this ["CustomerID"].Null) return -1;
            compareToValue = CustomerID.CompareTo (cmpCustomers.CustomerID);
            if (compareToValue != 0)
                return compareToValue;
            return 0;
        }
        throw new ArgumentException ("object is not a Customers");
    }
    public override string ToString () {
        return "[Customers: " +
            " CustomerID = " + CustomerID +
            " CompanyName = " + CompanyName +
            " ContactName = " + ContactName +
            " ContactTitle = " + ContactTitle +
            " Address = " + Address + "]";
    }
    private void SetupFields () {
        AddField (new PersistentField ("CustomerID", true, false,
            TDevFramework.EDataType.eVarWChar, null));
        AddField (new PersistentField ("CompanyName", true, false,
            TDevFramework.EDataType.eVarWChar, null));
        AddField (new PersistentField ("ContactName", false, false,
            TDevFramework.EDataType.eVarWChar, null));
        AddField (new PersistentField ("ContactTitle", false, false,
            TDevFramework.EDataType.eVarWChar, null));
        AddField (new PersistentField ("Address", false, false,
            TDevFramework.EDataType.eVarWChar, null));
    }
}

Data Persistance Interface Definition

The interface definition below defines all the methods that the user has specified for this data object in TierDeveloper. The implementation of some of these methods is shown on this page.

public interface ICustomersFactory {
    void Load (Customers objInfo, int nDepth);
    void Insert (Customers objInfo, int nDepth);
    void Update (Customers objInfo, int nDepth);
    void Delete (Customers objInfo, int nDepth);
    void Save (CustomersCollection objList, int nDepth);
    void DeleteColl (CustomersCollection objList, int nDepth);
    CustomersCollection AllCustomers ();
    Int32 AllCustomersCount ();
    CustomersCollection AllCustomersPR (Int32 nSP, Int32 nRecords);
    DataSet AllCustomersDS ();
    CustomersCollection CustomersByCity (String strprmCity);
    DataSet CustomersByCityDS (String strprmCity);
    CustomersCollection CustomersByCriteria (String where, String orderBy);
    int UpdatePostalCodeByCity (String strPostalCode, String strprmCity);
    int DeleteCustomersByTitle (String strprmContactTitle);
    void InsertShortProfile (Customers objInfo);
    void UpdateAddress (Customers objInfo);
    void LoadShortProfile (Customers objInfo);
    void CustOrderHist (String strCustomerID, ref ArrayList returnValue);
    void LoadChildren (Customers objInfo, int nDepth);
    void DeleteChildren (Customers objInfo, int nDepth);
    void LoadOrders (Customers objInfo, int nDepth);
    void DeleteOrders (Customers objInfo, int nDepth);
}

Standard Load Method for Customers

The Load method loads a single-row from the Customers table and returns a Customers object containing data from it.

/// <summary>
/// Load (Standard Load Method)
/// </summary>
/// <remarks>
/// Loads a record from the <c>Customers</c> table given by
/// the specified primary key.
/// </remarks>
/// <param name="objInfo">The <c>Customers</c> info object
containing the primary key. < / param >
    /// <param name="nDepth">Specify the depth of children should be loaded.</param>
    public void Load (Customers objInfo, int nDepth) {
        try {
            bool bRecordsFound = false;
            CheckPrimaryKeyValues (objInfo);
            Prepare (STD_LOAD_SQL, USE_STORE_PROC);
            BeginTransaction ();
            int status = 0; 
            ICustomersHooks hooks = new CustomersHooks ();
            status = hooks.PreLoad ((TDevFramework.Connection) getConnection (), objInfo);
            if (status != CustomersHooks.SUCCESS_CONTINUE) {
                SetStatus (status == CustomersHooks.FAIL_NONCONTINUE ?
                    EStatus.eFail : EStatus.eSuccess);
                ReleaseCommand ();
                return;
            }
            AddCmdParameter ("@CustomerID", TDevFramework.EDataType.eVarWChar, objInfo.CustomerID,
                TDevFramework.EParamDirection.eInput, objInfo.IsNull ("CustomerID"));
            ExecuteReader ();
            while (Read ()) {
                FillPersistentObject (objInfo);
                objInfo.IsNew = false;
                objInfo.Dirty = false;
                bRecordsFound = true;
            }
            status = hooks.PostLoad ((TDevFramework.Connection) getConnection (), objInfo);
            if (status != CustomersHooks.SUCCESS_CONTINUE) {
                SetStatus (status == CustomersHooks.FAIL_NONCONTINUE ?
                    EStatus.eFail : EStatus.eSuccess);
                ReleaseCommand ();
                return;
            }
            ReleaseReader ();
            if (!bRecordsFound) {
                throw new RecordNotFoundException ("No record found against given search criteria.");
            }
            if (nDepth != 0) {
                LoadChildren (objInfo, nDepth - 1);
            }
            SetStatus (EStatus.eSuccess);
            ReleaseCommand ();
        } catch (RecordNotFoundException e) {
            SetStatus (EStatus.eSuccess);
            ReleaseCommand ();
            throw e;
        } catch (Exception e) {
            SetStatus (EStatus.eFail);
            ReleaseCommand ();
            throw e;
        }
    }

Query Method for Customers

This Query Method loads multiple rows from the Customers table and returns a collection of Customers objects with each object containing data from one row.

/// <summary>
/// AllCustomers (Query Method)
/// </summary>
/// <remarks>
/// </remarks>
/// <returns><c>CustomersCollection</c> object
containing all the records
/// returned in the resultset.</returns> 

public CustomersCollection AllCustomers () {
    Customers objInfo;
    CustomersCollection objList = new CustomersCollection ();
    try { 
        Prepare (AllCustomers_SQL, USE_STORE_PROC);
        BeginTransaction ();
        ExecuteReader ();

        while (Read ()) {
            objInfo = new Customers ();

            if (!IsNull ("CustomerID"))
                objInfo.CustomerID = Convert.ToString (getValue ("CustomerID"));

            if (!IsNull ("CompanyName"))
                objInfo.CompanyName = Convert.ToString (getValue ("CompanyName"));

            if (!IsNull ("ContactName"))
                objInfo.ContactName = Convert.ToString (getValue ("ContactName"));

            if (!IsNull ("ContactTitle"))
                objInfo.ContactTitle = Convert.ToString (getValue ("ContactTitle"));

            if (!IsNull ("Address"))
                objInfo.Address = Convert.ToString (getValue ("Address"));

            objInfo.IsNew = false;
            objInfo.Dirty = false;
            objList.Add (objInfo);
        }
        ReleaseReader ();
        SetStatus (EStatus.eSuccess);
        ReleaseCommand ();
    } catch (Exception e) {
        SetStatus (EStatus.eFail);
        ReleaseCommand ();
        throw e;
    }
    return objList;
}

Bulk Update Method for Customers

Following is the code generated for "UpdatePostalCodeByCity", a bulk method that update the postal code of particular city.

/// <summary>
/// UpdatePostalCodeByCity (Bulk Update Method)
/// </summary>
/// <remarks>
/// <para>Updates all the record in <c>Customers</c>
table matching the given criteria. < / para >
    /// <para>
    /// </para>
    /// </remarks>
    /// <returns>The number of rows affected.</returns> 
    public int UpdatePostalCodeByCity (String strPostalCode, String strprmCity)
{
    int result = 0;
    try
    {
        Prepare (UpdatePostalCodeByCity_SQL, USE_STORE_PROC);
        BeginTransaction ();
        AddCmdParameter ("@PostalCode", TDevFramework.EDataType.eVarWChar, strPostalCode,
            EParamDirection.eInput);
        AddCmdParameter ("@prmCity", TDevFramework.EDataType.eVarChar, strprmCity,
            EParamDirection.eInput);
        result = ExecuteNonQuery ();
        SetStatus (EStatus.eSuccess);
        ReleaseCommand ();
    } catch (Exception e)

    {
        SetStatus (EStatus.eFail);
        ReleaseCommand ();
        throw e;
    }
    return result;
}

Related Query Method for Customers

The Related Query Method actually returns a collection of related "Orders" object. It does this by calling a query method in the related data object.

/// <summary>
/// OrdersQuery (Query Method)
/// </summary>
/// <remarks> /// 
/// </remarks>
/// <returns><c>OrdersCollection</c> object
containing all the records
/// returned in the resultset.</returns> 

public OrdersCollection OrdersQuery (String strprmCustomerID)
{
    Orders objInfo;
    OrdersCollection objList = new OrdersCollection ();
    try
    { 
        Prepare (OrdersQuery_SQL, USE_STORE_PROC);
        BeginTransaction ();
        AddCmdParameter ("@prmCustomerID",
            TDevFramework.EDataType.eVarChar, strprmCustomerID,
            EParamDirection.eInput);
        ExecuteReader ();
        while (Read ())
        {
            objInfo = new Orders ();
            if (!IsNull ("OrderID"))
                objInfo.OrderID = Convert.ToInt32 (getValue ("OrderID"));

            if (!IsNull ("CustomerID"))
                objInfo.CustomerID = Convert.ToString (getValue ("CustomerID"));

            if (!IsNull ("EmployeeID"))
                objInfo.EmployeeID = Convert.ToInt32 (getValue ("EmployeeID"));

            if (!IsNull ("OrderDate"))
                objInfo.OrderDate = Convert.ToDateTime (getValue ("OrderDate"));

            if (!IsNull ("RequiredDate"))
                objInfo.RequiredDate = Convert.ToDateTime (getValue ("RequiredDate"));

            objInfo.IsNew = false;

            objInfo.Dirty = false;

            objList.Add (objInfo);
        }
        ReleaseReader ();
        SetStatus (EStatus.eSuccess);
        ReleaseCommand ();
    } catch (Exception e)
    {
        SetStatus (EStatus.eFail);
        ReleaseCommand ();
        throw e;
    }
    return objList;
}

Stored Procedure Method for Customers

The Stored Procedure Method calls a stored procedure in the database. It passes any IN, IN/OUT, and OUT parameters to this stored procedure. If the stored procedure is returning a rowset then this method returns a collection containing the data.

/// <summary>
/// CustOrderHist (Stored Procedure Method)
/// </summary>
/// <remarks>
/// <para>Executes the stored procedure <c>CustOrderHist;1</c>.</para> 
/// <para> 
/// </para> 
/// </remarks>
public void CustOrderHist (String strCustomerID, ref ArrayList returnValue) {
    string sqlCmd = "CustOrderHist;1";
    ArrayList objList = new ArrayList ();
    try {
        Prepare (sqlCmd, true);
        AddCmdParameter ("@CustomerID", TDevFramework.EDataType.eVarWChar, strCustomerID,
            TDevFramework.EParamDirection.eInput);
        ExecuteReader ();
        while (Read ()) {
            ArrayList objInfo = new ArrayList ();
            int fieldCount = getFieldCount ();
            if (objList.Count == 0) {
                for (int index = 0; index < fieldCount; index++) {
                    objInfo.Add (getFieldName (index));
                }
                objList.Add (objInfo);
                objInfo = new ArrayList ();
            }
            for (int index = 0; index < fieldCount; index++) {
                objInfo.Add (getValue (index));
            }
            objList.Add (objInfo);
        }
        returnValue = objList;
        returnValue = objList;
        SetStatus (EStatus.eSuccess);
        ReleaseCommand ();
    } catch (Exception ex) {
        SetStatus (EStatus.eFail);
        ReleaseCommand ();
        throw ex;
    }
}

Custom Hooks Inside Generated Code

TierDeveloper optionally generates "ObjectName" Hooks classes if the user has selected/specified "Pre" and "Post" hooks for certain methods of data object through data object settings in TierDeveloper. For example in this case TierDeveloper generates CustomersHooks.cs file. The "Pre" and "Post" Hook Methods for "InsertShortProfile" custom operation are shown below:

public int PreInsertShortProfile (TDevFramework.Connection Conn, Customers objInfo) {
    return SUCCESS_CONTINUE;
}
public int PostInsertShortProfile (TDevFramework.Connection Conn, Customers objInfo) {
    return SUCCESS_CONTINUE;
}

Custom Operation Method with Hooks Being Called

This is the generated code for a Custom Insert Operation "InsertShortProfile". The "Pre" and "Post" hook methods (optional) are called before and after the database operation is performed.

/// <summary>
/// InsertShortProfile (Custom Insert Method)
/// </summary>
/// <remarks>
/// <para>Inserts partial record into <c>Customers</c> table.</para>
/// <para>
/// </para>
/// </remarks>
/// <param name="objInfo">The <c>Customers</c> info object to be inserted.</param>
public void InsertShortProfile (Customers objInfo) {
    try {
        //CheckPrimaryKeyValues(objInfo); 
        objInfo.ValidateRequiredFields ();
        Prepare (InsertShortProfile_SQL, USE_STORE_PROC);
        BeginTransaction ();
        int status = 0;
        ICustomersHooks hooks = new CustomersHooks ();
        status = hooks.PreInsertShortProfile ((TDevFramework.Connection) getConnection (),
            objInfo);
        if (status != CustomersHooks.SUCCESS_CONTINUE) {
            SetStatus (status == CustomersHooks.FAIL_NONCONTINUE ?
                EStatus.eFail : EStatus.eSuccess);
            ReleaseCommand ();
            return;
        }
        AddCmdParameter ("@CustomerID", TDevFramework.EDataType.eVarWChar,
            objInfo.CustomerID,
            TDevFramework.EParamDirection.eInput, objInfo.IsNull ("CustomerID"));

        AddCmdParameter ("@CompanyName", TDevFramework.EDataType.eVarWChar,
            objInfo.CompanyName,
            TDevFramework.EParamDirection.eInput, objInfo.IsNull ("CompanyName"));

        AddCmdParameter ("@ContactName", TDevFramework.EDataType.eVarWChar,
            objInfo.ContactName,
            TDevFramework.EParamDirection.eInput, objInfo.IsNull ("ContactName"));

        AddCmdParameter ("@ContactTitle", TDevFramework.EDataType.eVarWChar,
            objInfo.ContactTitle,
            TDevFramework.EParamDirection.eInput, objInfo.IsNull ("ContactTitle"));
        ExecuteNonQuery ();
        status = hooks.PostInsertShortProfile ((TDevFramework.Connection) getConnection (),
            objInfo);
        if (status != CustomersHooks.SUCCESS_CONTINUE) {
            SetStatus (status == CustomersHooks.FAIL_NONCONTINUE ?
                EStatus.eFail : EStatus.eSuccess);
            ReleaseCommand ();
            return;
        }
        SetStatus (EStatus.eSuccess);
        ReleaseCommand ();
    } catch (Exception e) {
        SetStatus (EStatus.eFail);
        ReleaseCommand ();
        throw e;
    }
}

ObjectCollection Component

The ObjectCollection is mostly used to return a collection of Domain Objects as a result of Queries.

namespace Northwind.Business.Domain {
    [Serializable]
    public class CustomersCollection:  Collection {
        public CustomersCollection () { }
        /// <summary>
        /// Gets the <c>Customers</c> object at the specified index.
        /// </summary>
        /// <value>The <c>Customers</c> object.</value>
        public Customers this [int index]  {
            get  {
                return ((Customers) List[index]);
            }
            set {
                List[index] = value;
            }
        }
        public void Add (Customers objValue)  {
            base.Add (objValue);
        }
        public void Remove (Customers objValue)  {
            int nIndex = List.Count - 1;
            base.Remove (objValue);
            OnListChanged (new ListChangedEventArgs (ListChangedType.ItemDeleted, nIndex));
        } 
        public new void  RemoveAt ( int nIndex)  { 
            base.RemoveAt (nIndex); 
        }
        public CustomersCollection GetChanges () {
            if (List == null) return null;
            CustomersCollection changes = new CustomersCollection ();
            Customers objInfo = null;
            for (int count = 0; count < this.MarkedDeletedItems.Count; count++) {
                changes.MarkedDeletedItems[count] = (Customers) this.MarkedDeletedItems[count];
            }
            for (int count = 0; count < this.Count; count++) {
                objInfo = this [count];
                if (objInfo.IsNew) {
                    changes.Add (objInfo);
                } else if (objInfo.Dirty) {
                    changes.Add (objInfo);
                } 
            }
            return changes;
        }
        /// <summary>
        /// If value is not of type object, this will return false.
        /// </summary>
        public bool Contains (Customers objValue)  {
            return (base.Contains (objValue));
        }
        public int GetIndexOf (Customers objValue) {
            int index = 0;
            foreach (Customers _obj in List) {
                if (_obj.CompareTo (objValue) == 0)
                    return index;
                index = index + 1;
            }
            return -1;
        }
        public  DataSet ToDataSet () {
            DataSet dSet = new DataSet ("Customers");
            dSet.Tables.Add (CreateTable ());
            int i = 0;
            for (i = 0; i < this.Count; i++) {
                DataRow dRow = dSet.Tables["Customers"].NewRow ();
                dRow["CustomerID"] = this [i].CustomerID;
                dRow["CompanyName"] = this [i].CompanyName;
                dRow["ContactName"] = this [i].ContactName;
                dRow["ContactTitle"] = this [i].ContactTitle;
                dRow["Address"] = this [i].Address;
            }
            return dSet;
        }
        public  DataTable CreateTable () {
            DataTable oDataTable = new DataTable ("Customers"); 
            oDataTable.Columns.Add (new DataColumn ("CustomerID")); 
            oDataTable.Columns.Add (new DataColumn ("CompanyName")); 
            oDataTable.Columns.Add (new DataColumn ("ContactName")); 
            oDataTable.Columns.Add (new DataColumn ("ContactTitle")); 
            oDataTable.Columns.Add (new DataColumn ("Address"));
            return oDataTable;
        }
        #region "Sort Methods"
        /// <summary>
        /// Sorts the collection according to primary attributes.
        /// </summary>
        public new void Sort () {
            AttributeComparer comparer = new AttributeComparer ();
            comparer.AddAttribute ("CustomerID");
            base.Sort (comparer);
        }
        /// <summary>
        /// Sorts the collection according to AttributeComparer.
        /// </summary>
        public void Sort (AttributeComparer comparer) {
            base.Sort (comparer);
        }
        /// <summary>
        /// Sorts the collection according to AttributeComparer in specified range.
        /// </summary>
        public void Sort (int index, int count, AttributeComparer comparer) {
            base.Sort (index, count, comparer);
        }
        #endregion
    }
}

ServiceProvider Component

The ServiceProvider contains static methods to instantiate the factory interface objects.

namespace Northwind.Data.Persistence {
    public class ServiceProvider {
        public static ICustomersFactory getCustomersFactory () {
            return new CustomersFactory ();
        }
        public static ICustomersFactory getCustomersFactory (string strConn) {
            return new CustomersFactory (strConn);
        }
        public static ICustomersFactory getCustomersFactory (TDevFramework.Connection tdevConn) {
            return new CustomersFactory (tdevConn);
        }
        public static ICustomersFactory getCustomersFactory (Object objConn) {
            return new CustomersFactory (objConn);
        }
    }
}

Author: Iqbal Khan works for Alachisoft, a leading software company providing .NET and Java distributed caching, O/R Mapping and SharePoint Storage Optimization solutions. You can reach him iqbal@alachisoft.com or visit Alachisoft at www.alachisoft.com.

Signup for monthly email newsletter to get latest updates.

© Copyright Alachisoft 2002 - . All rights reserved. NCache is a registered trademark of Diyatech Corp.