Latest Posts

03 May 2011

Design Patterns [Simple Definitions]

Abstract factory pattern

The abstract factory pattern is a software design pattern that provides a way to encapsulate a group of individual factories that have a common theme. In normal usage, the client software creates a concrete implementation of the abstract factory and then uses the generic interfaces to create the concrete objects that are part of the theme. The client does not know (or care) which concrete objects it gets from each of these internal factories since it uses only the generic interfaces of their products. This pattern separates the details of implementation of a set of objects from their general usage

Builder

It is used to keep the construction of object separate from its representation. Crystal report is one of the best example of Builder design pattern.
We (Client) construct a report according to our business requirement by using Crystal report's provided interface (Director).
Crystal Report represents that report independently in any available formats.



Adapter or Wrapper

It translates one interface for a class into a compatible interface e.g. Interop classes (Adapter), for using COM and Native DLLs in .Net.


Bridge Pattern

It is used to decouple an abstraction from its implementation so that the two can vary independently.
Suppose we have SQL Server's and Oracle's data connections in our Data Access Layer, and we have number of Business Objects in our Business Layer like Customer, Order etc. These Business Objects can use any data connection from the Data Access layer. So we can define new business objects without any dependency on Data Access Layer, similarly we can define MySQL data connection in our Data Access Layer without any dependency on Business Objects.



Composite Pattern

It is used to compose an object into tree structure.

read more...

22 April 2011

Useful Regular Expressions

Password Policy

^(?=.*\d)(?=.*[A-Za-z])(?=.*\W)(?=.*\d)(.{5,10})$


Regex for replacing data into SQL query by using VS


Search Expression
^{:z},{[0-9A-Z ]+},{:z}$

Replace Expression
INSERT INTO TRAN_ERROR_MAPPING
 (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) \n\t 
VALUES('HOST','\1','\3','Administrator','Administrator')\nGO\n\n

Data
058,Request Timed Out,58
004,Low Balance,04
001,Limit Exceeded,01
003,Account Inactive,03
008,Invalid Card Record,07
010,Duplicate Transaction,10
011,Invalid Transaction Code,11
113,Database Error,13
024,Invalid TIN,62
050,Invalid Host Status,50
059,Host Reject,95
055,Host Comms Down,55
051,Host Not Processing,51
050,Host Not Found,50
075,Unknown Transaction Source,30
094,Permission Denied,94
002,Invalid Account,02
080,Message Format Error,28
060,PIN Retries Exceeded,60
014,Warm Card,14
015,Hot Card,15
098,Original Amount Incorrect,98
090,Customer Record Not Found,90
052,Host Busy,53
057,System Error,31
057,System Error,29
024,Incorrect PIN,24
113,Data Source Not Supported,74
003,Account Inactive,67
080,Message Format Error,80
016,Bad Card Status,16

Converted data to SQL queries
INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','058','58','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','004','04','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','001','01','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','003','03','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','008','07','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','010','10','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','011','11','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','113','13','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','024','62','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','050','50','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','059','95','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','055','55','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','051','51','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','050','50','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','075','30','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','094','94','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','002','02','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','080','28','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','060','60','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','014','14','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','015','15','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','098','98','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','090','90','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','052','53','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','057','31','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','057','29','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','024','24','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','113','74','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','003','67','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','080','80','Administrator','Administrator')
GO


INSERT INTO TRAN_ERROR_MAPPING (CHANNEL_CODE,ERROR_CODE,MAPPED_ERROR,CREATED_BY,UPDATED_BY) 
VALUES('HOST','016','16','Administrator','Administrator')
GO
read more...

26 March 2011

Object Pooling


While writing eXtremecode's business layer, I found some performance related issues when creating a large number of business objects.
Each business object contains some heavy objects which require comparatively larger amount of system memory. Firstly I was creating these objects on each creation of business object, later I realized that these objects should be shared by related business objects, but that was not the solution as business objects were being created in different threads. Finally I decided to make a pool of similar objects so each thread could request pooled objects and could use them independently.

Pooling Source Code

/** 
Copyright (c) 2010, Sheikh Abdul Wahid Ahmed
Details @ http://extremecodeworld.codeplex.com/license
**/

using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Threading;


namespace EXtremecode.Common
{
    public interface IPooledObject
    {
        ObjectPool Pool
        {
            get;
            set;
        }
    }
 
 public class ObjectPool
    {
        private Stack<IPooledObject> pool = new Stack<IPooledObject>();
        private int poolSize;
        private int numberOfPooledObjectsCreated = 0;
        private int timeOut = 15000; // in millisecond
        private Func<IPooledObject> CreateObject;
        private AutoResetEvent autoReset = new AutoResetEvent(false);
        private string poolName = string.Empty;

        public ObjectPool(Func<IPooledObject> CreateObject)
            : this("UnKnown", CreateObject, 10, 15000)
        {}

        public ObjectPool(string poolName, Func<IPooledObject> CreateObject)
            : this(poolName, CreateObject, 10, 15000)
        { }

        public ObjectPool(Func<IPooledObject> CreateObject
                , int timeOut)
            : this("UnKnown", CreateObject, 10 ,timeOut)
        { }

        public ObjectPool(string poolName, Func<IPooledObject> CreateObject
                , int timeOut)
            : this(poolName, CreateObject, 10, timeOut)
        { }

        public ObjectPool(string poolName,Func<IPooledObject> CreateObject
            , int poolSize, int timeOut)
        {
            this.CreateObject = CreateObject;
            this.poolSize = poolSize;
            this.poolName = poolName;
            this.timeOut = timeOut;
        }

        public IPooledObject GetObject()
        {
            while (true)
            {
                lock (pool)
                {
                    if (pool.Count > 0)
                    {
                        //get object from the pool.
                        Console.WriteLine("[{0}] PooledObject Poped  (Opened Objects [{1}])"
                            , poolName
                            ,numberOfPooledObjectsCreated - pool.Count);
                        
                        return pool.Pop();
                        

                    }
                    else
                    {
                        if (numberOfPooledObjectsCreated < poolSize)
                        {
                            //numbers of pooled object has not crossed the limit yet
                            //so create new object and return
                            IPooledObject pooledObject = CreateObject();
                            if (pooledObject != null)
                            {
                                numberOfPooledObjectsCreated++;
                                pooledObject.Pool = this; //add self reference
                                //which will be used to get the object back into pool again. 
                            }
                            Console.WriteLine("[{0}] PooledObject Created  (Opened Objects [{1}])"
                                , poolName
                                , numberOfPooledObjectsCreated - pool.Count);

                            return pooledObject;
                            
                            
                        }
                    }



                    Console.WriteLine("[{0}] Waiting for PooledObject", poolName);
                    bool timeOutOccured = !autoReset.WaitOne(timeOut); 

                    if (timeOutOccured)
                    {
                        throw new Exception(
                            string.Format("object request from the pool '{0}' has been timeout"
                            , poolName));
                    }
                }
               
                
            }
            
            
        }

        [System.Runtime.CompilerServices.MethodImpl
        (System.Runtime.CompilerServices.MethodImplOptions.Synchronized)]
        public void ReleaseObject(IPooledObject pooledObject)
        {
            pool.Push(pooledObject);
            autoReset.Set();
            Console.WriteLine("[{0}] PooledObject Pushed  (Opened Objects [{1}])"
                , poolName
                , numberOfPooledObjectsCreated - pool.Count);
        }
        
    }

    
}

read more...

24 March 2011

Request or Task Scheduling


I worked on the product which was developed for reconciling bank transaction. That reconciliation was performed on the bases of priority. In order to execute priority based requests from different threads, I have written an scheduler for executing them. Following is the source code of that scheduler.

Source Code.

using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Threading;

namespace EXtremecode.Common
{
    //each request must implement this interface. 
    public interface IScheduledProcess
    {
        void Execute();
        
    }
 
 public class PriorityComparer : IComparer<int>
    {
        #region IComparer<int> Members

        public int Compare(int x, int y)
        {
            return x - y;
        }

        #endregion
    }

    public enum PriorityLevel
    {
         High = 10
       , Normal = 5
       , Low = 1

    }
    public class ProcessSchedular
    {
        //for identifying a schedular
        private static Dictionary<string, ProcessSchedular>
            schedularDict = new Dictionary<string, ProcessSchedular>();

        //for priority based execution
        private SortedDictionary<int, Queue<IScheduledProcess>>
            queuesDict = new SortedDictionary<int, Queue<IScheduledProcess>>(
                new PriorityComparer());


        //for controlling termination of main thread.
        private volatile bool isRunning = false;

        //for letting single thread to be executed at a time.
        private AutoResetEvent autoReset = new AutoResetEvent(false);


        //it is used for time based schdular (without priority). All registed process will be executed simultaneously
        public event Action OnExecuteProcess;
        
        
        public static ProcessSchedular CreateInstance(string key)
        {
            ProcessSchedular processSchedular;
            if (schedularDict.ContainsKey(key))
            {
                processSchedular = schedularDict[key];
            }
            else
            {
                processSchedular = new ProcessSchedular();
                schedularDict.Add(key, processSchedular);
            }
            return processSchedular;

        }

        public static ProcessSchedular GetInstance(string key)
        {
            ProcessSchedular processSchedular = null;
            if (schedularDict.ContainsKey(key))
            {
                processSchedular = schedularDict[key];
            }

            return processSchedular;

        }

        
        //Add process to prioriy based schedular with normal priority
        public void AddProcess(IScheduledProcess process)
        {
            AddProcess(process, PriorityLevel.Normal);
        }

        //Add process to prioriy based schedular with provided priority
        public void AddProcess(IScheduledProcess process, PriorityLevel priorityLevel)
        {
            AddProcess(process, (int)priorityLevel);
        }


        [System.Runtime.CompilerServices.MethodImpl
        (System.Runtime.CompilerServices.MethodImplOptions.Synchronized)]
        public void AddProcess(IScheduledProcess process, int priorityLevel)
        {
            GetQueue(priorityLevel).Enqueue(process);
            autoReset.Set(); //every time single thread will me released.
        }


        public void StopScheduling()
        {
            isRunning = false;
        }
        
        public void StartTimeBasedScheduling(object objInterval)
        {
            int interval = (int)objInterval;
            isRunning = true;
            while (isRunning)
            {

                try
                {

                    if (OnExecuteProcess != null)
                    {
                        OnExecuteProcess();
                    }
                    Thread.Sleep(interval);
                }
                catch
                {
                    throw;
                }

            }
          
        }

        public void StartPriorityBasedScheduling()
        {
            isRunning = true;
            while (isRunning)
            {
                while (true)
                {
                    Queue<IScheduledProcess> queue
                        = GetQueueToExecute();

                    if (queue == null)
                    {
                        break;
                        //wait for next processes
                    }
                    else
                    {
                        try
                        {
                            queue.Dequeue().Execute();

                        }
                        catch
                        {
                            //thread should not be terminated
                            //process should handle this exception.
                        }
                    }
                }

                autoReset.WaitOne(15000,false);
            }
        }

        private Queue<IScheduledProcess> GetQueue(int priorityLevel)
        {

            Queue<IScheduledProcess> queue;
            if (queuesDict.ContainsKey(priorityLevel))
            {
                queue = queuesDict[priorityLevel];
            }
            else
            {
                queue = new Queue<IScheduledProcess>();
                queuesDict.Add(priorityLevel, queue);
            }

            return queue;
        }

        private Queue<IScheduledProcess> GetQueueToExecute()
        {
            foreach (int key in queuesDict.Keys)
            {
                if (queuesDict[key].Count > 0)
                {
                    return queuesDict[key];
                }
            }
            return null;
        }
    }
}

read more...

21 March 2011

Parsing a query or statement for Identifying Functions


Overview

I am working in the application which was previously integrated with Microsoft Great Plain system (Back-end SQL Server Database). Now it's going to be integrated with Oracle Financial (Back-end Oracle Database). When i analyzed application's current code, i found number of inline queries everywhere in the code. It would be very difficult to scan whole code and modify all queries for making them Oracle compliance. So rather to do this manual work, i decided to write a routine which would return an object after parsing the query. Once i would get the object, i could make required modification easily before sending it to the sever for execution.

Details

If you want to convert SQL Server's query to Oracle's, beside other modifications, you must need to replace all SQL Server's functions and their parameters with some Oracle compliance functions and parameters. This replacement depends on the name of SQL Server's functions as well as values and positions of their arguments/parameters.
Suppose we have the function from SQL Server query Convert(datetime,'23/02/2010',103). In order to make it Oracle compliance, it should be modified something like To_Date('23/02/2010','DD/MM/yyyy').
First argument of Convert function in above example is datetime which tells that function name should be replaced with To_Date. Third argument of Convert function is 103 which tell that To_Date function's second argument should be 'DD/MM/yyyy'.

IdentifyFunctions (parsing a query or statement)

This method parse the query and return a FunctionParameter object.
/// <summary>
        /// Identifying functions into a parameter.
        /// Whole statement or query is considered as a parameter.
        /// </summary>
        /// <param name="sb">A parameter or a statement. A parameter in case of recursive calling</param>
        /// <param name="startIndex"></param>
        /// <param name="endIndex"></param>
        /// <param name="flatListOfFunction">A flat list of all functions in a statement</param>
        /// <param name="parentFunction"> Function, the parameter belongs to. Null value in case of statement.</param>
        /// <returns></returns>
        public static FunctionParameter IdentifyFunctions(StringBuilder sb, int startIndex, int endIndex
            , SortedList<int, Function> flatListOfFunction
            , Function parentFunction)
        {

            //Parsing will be started for "index" position
            int index = startIndex;

            //This flag will be true where parsing will not be required. e.g "sample text" or 'sample text'
            bool blindMove = false;

            //Initialize flatListOfFunction
            if (flatListOfFunction == null)
            {
                flatListOfFunction = new SortedList<int, Function>();
            }



            //Passed value is considered a parameter. Each param may contain number of functions.
            FunctionParameter parameter
                = new FunctionParameter(sb, startIndex, endIndex, parentFunction);


            //Explicit return condition
            if (parameter.Statement == string.Empty)
            {
                return parameter;
            }

            //Fuction can be constructed with any of them enclosing chars. [{(
            //e.g. Function(),Function[] or Function{}
            Stack<char> enclosingChars = new Stack<char>();
            Stack<int> enclosingCharIndexs = new Stack<int>();

            //Variables for current function
            int currentFuncNameStartIndex = -1;
            int currentFuncNameEndIndex = -1;
            bool currentFuncNameEnded = false;
            char currentFuncEnclosingChar = '\0';
            Function currentFunction = null;


            //current text enclosing char
            char currentTextEnclosing = '\'';

            while (index <= endIndex)
            {
                char chr = sb[index];


                if (blindMove && chr != currentTextEnclosing)
                {
                    //Ignore current char, becuase blind move is true.
                    index++;
                    continue;
                }

                char? chrNext = (index + 1 < sb.Length)
                    ? sb[index + 1]
                    : (char?)null;

                char? chrPrev = (index > 0)
                    ? sb[index - 1]
                    : (char?)null;


                char? topEnclosingChar = (enclosingChars.Count > 0)
                        ? enclosingChars.Peek()
                        : (char?)null;

                int? topEnclosingCharIndex = (enclosingCharIndexs.Count > 0)
                       ? enclosingCharIndexs.Peek()
                       : (int?)null;

                switch (chr)
                {
                    // ' 
                    case '\'':
                    case '"':
                        if (topEnclosingChar == chr)
                        {
                            if (chrNext == chr)
                            {
                                //It is not enclosing (closing) char so skip these two chars
                                index += 2;
                                continue;
                            }
                            else if (chrPrev == '\\')
                            {
                                //Previous char was escaping char. so it is not closing of the text.
                            }

                            else
                            {
                                //It is enclosing (closing) char so pop the char from stacks.
                                enclosingChars.Pop();
                                enclosingCharIndexs.Pop();

                                //Parsing will be required for upcomming data.
                                blindMove = false;

                            }
                        }
                        else
                        {
                            //It is enclosing (opening) char so push it to the stacks.
                            enclosingChars.Push(chr);
                            enclosingCharIndexs.Push(index);

                            //Ignore parising of upcomming data untill we get closing char.
                            blindMove = true;

                        }
                        break;

                    // {([
                    case '{':
                    case '[':
                    case '(':

                        //This char may be for nested function 
                        //"Bracket found in the stack" means that it is opening for nested function.
                        // Nested functions will be handled by related iteration (Recursive Calling)
                        bool isNestedFunctionOpening = (enclosingChars.Count > 0);


                        //If it is not for nested functin then set function name's ending index.
                        if (!isNestedFunctionOpening)
                        {

                            //Set endIndex to previous of this index.
                            currentFuncNameEndIndex = index - 1;

                            //Enclosing char, (Can be used later at the time of statement manipulation).
                            currentFuncEnclosingChar = chr;

                            //Make function
                            currentFunction = new Function(sb
                                , currentFuncNameStartIndex
                                , currentFuncNameEndIndex
                                , currentFuncEnclosingChar
                                , parameter);

                            //Add function to main param
                            parameter.Functions.Add(currentFunction);


                            //Add into flat list
                            flatListOfFunction.Add(currentFuncNameEndIndex, currentFunction);



                        }
                        enclosingChars.Push(chr);
                        enclosingCharIndexs.Push(index);
                        break;

                    // )}]
                    case '}':
                    case ']':
                    case ')':

                        if (enclosingChars.Count == 0)
                        {
                            //There is no openning bracket for this char
                            throw new Exception(string.Format("opening char is missing index={0}    char={1}"
                                , index
                                , chr));
                        }

                        //This char may be for nested function 
                        //"More than one char found in the stack" means, it is closing for nested function.
                        bool isNestedFunctionClosing = (enclosingChars.Count > 1);

                        if (!isNestedFunctionClosing)
                        {
                            //It is closing of current function. 
                            //And previous index is the ending of its last parameter if any.

                            //Get its last param by recursive calling
                            FunctionParameter param = IdentifyFunctions(sb
                                , enclosingCharIndexs.Peek() + 1
                                , index - 1
                                , flatListOfFunction
                                , currentFunction);

                            //Add Param to function params List
                            if (currentFunction != null)
                            {
                                currentFunction.Parameters.Add(param);
                            }

                            //Pop function's opening char.
                            enclosingCharIndexs.Pop();
                            enclosingChars.Pop();


                            //Reset current function variables
                            currentFunction = null;
                            currentFuncNameStartIndex = -1;
                            currentFuncNameEndIndex = -1;

                        }
                        else
                        {
                            //Pop nested fuction opening char
                            enclosingCharIndexs.Pop();
                            enclosingChars.Pop();

                        }
                        break;

                    // ,
                    case ',':

                        //This char may be for nested function 
                        //"More than one char found in the stack" means, it is for nested function.
                        bool isNestedFunctionComma = (enclosingChars.Count > 1);


                        if (!isNestedFunctionComma

                            //commas outside the functions will be ignored. 
                            //If we find stack empty, it means this comma doesnt belog to any function.
                            && enclosingChars.Count > 0)
                        {
                            //Previous index is the ending of the last parameter of current function if any

                            //Get the param by recursive calling
                            FunctionParameter param = IdentifyFunctions(sb
                                , enclosingCharIndexs.Peek() + 1
                                , index - 1
                                , flatListOfFunction
                                , currentFunction);


                            //Add param to list
                            if (currentFunction != null)
                            {
                                currentFunction.Parameters.Add(param);
                            }

                            //Pop function's opening char (it may be a comma).
                            //Because after deciding first param boundary, function's opening char will get poped out the stacks.
                            enclosingCharIndexs.Pop();
                            enclosingChars.Pop();

                            //Push this char, it will be used for identifying starting of next param
                            enclosingCharIndexs.Push(index);
                            enclosingChars.Push(chr);
                        }
                        break;

                    // space or new line
                    case '\t':
                    case '\n':
                    case '\r':
                    case ' ':

                        //As a space has come, system is assuming it is either end of the function name
                        //or not part of the function name.
                        currentFuncNameEnded = true;
                        break;

                    default:
                        if ((chr >= '0' && chr <= '9')
                             || (chr >= 'a' && chr <= 'z')
                             || (chr >= 'A' && chr <= 'Z')
                             || (chr == '_')
                            ) //Possible chars for function name
                        {
                            bool isNestedFunctionText = (enclosingChars.Count > 0);

                            //if Char is from nested functions. system will assume that 
                            //function name has already been identified.
                            if (!isNestedFunctionText)
                            {
                                if (currentFuncNameEnded)
                                {
                                    //As current function name has already been ended.
                                    //and now we have another alpha numeric character.
                                    //it means last identified function name is not correct
                                    //So mark this char as start of the fuction name
                                    currentFuncNameStartIndex = index;
                                    currentFuncNameEnded = false;
                                }
                                else if (currentFuncNameStartIndex < 0)
                                {
                                    //it has no been intialized yet
                                    currentFuncNameStartIndex = index;
                                }
                            }
                        }
                        break;
                }
                index++;

            }

            return parameter;

        }

FunctionParameter object

FunctionParameter object is either a whole query/statement or an argument of a function. FunctionParameter object has a collection of Function objects FunctionParameter object belongs to a parent Function. (In case of whole statemet/query, parent Function object is null)
public class FunctionParameter
    {

        /// <summary>
        /// 
        /// </summary>
        /// <param name="source"></param>
        /// <param name="startIndex"></param>
        /// <param name="endIndex"></param>
        /// <param name="parentFunction">Function, which this param belongs to</param>
        public FunctionParameter(StringBuilder source, int startIndex, int endIndex, Function parentFunction)
        {
            this.startIndex = startIndex;
            this.endIndex = endIndex;
            this.source = source;
            this.functions = new List<Function>();
            this.parentFunction = parentFunction;
        }

        private int startIndex;
        public int StartIndex
        {
            get { return startIndex; }

            set
            {
                startIndex = value;

                //Statement shoud be re assigned.
                statement = null;
            }
        }

        private int endIndex;
        public int EndIndex
        {
            get { return endIndex; }
            set
            {
                endIndex = value;

                //Statement shoud be re assigned.
                statement = null;
            }
        }

        private StringBuilder source;
        public StringBuilder Source
        {
            get { return source; }
            set { source = value; }
        }

        private List<Function> functions;
        public List<Function> Functions
        {
            get { return functions; }
        }


        private Function parentFunction;
        public Function ParentFunction
        {
            get { return parentFunction; }
        }

        private string statement = null;
        public string Statement
        {
            get
            {
                if (statement == null)
                {
                    if (startIndex > endIndex
                        || startIndex < 0
                        || endIndex < 0)
                    {
                        //Parameter with empty text (empty statement)
                        statement = string.Empty;
                    }
                    else
                    {
                        statement = source.ToString(startIndex
                            , endIndex - startIndex + 1).Trim();
                    }
                }
                return statement;
            }
            set
            {
                int statementCurrentLength = 0;
                if (startIndex < 0)
                {
                    //Already empty text so no need to remove old text
                    startIndex = 0;
                }
                else if (endIndex > 0)
                {
                    //Remove old text
                    source.Remove(startIndex
                        , endIndex - startIndex + 1);

                    statementCurrentLength = endIndex - startIndex + 1;
                }

                //Insert new text
                source.Insert(startIndex, value);

                //Update endindex
                endIndex = startIndex + value.Length - 1;

                //Reassign statement.
                statement = value.Trim();

                //Offset: subtrace statementCurrentLength from value's length
                int offset = value.Length - statementCurrentLength;


                //Adjust onward functions and params indexes
                if (parentFunction != null)
                {
                    parentFunction.AdjustIndexes(offset, this);
                }



            }
        }

        public void AdjustIndexes(int offset)
        {
            AdjustIndexes(offset, null);
        }


        public void AdjustIndexes(int offset, Function requestingFunction)
        {

            if (requestingFunction == null)
            {
                endIndex = endIndex + offset;
                startIndex = startIndex + offset;
            }
            else
            {
                //It is requested from its own fuction so only endIndex adjustment is required.
                endIndex = endIndex + offset;
            }

            //Adjustment should be applied only for those statement's functions which come after requesting function.
            int index = (requestingFunction != null)
                ? functions.IndexOf(requestingFunction) + 1
                : 0;

            for (; index < functions.Count; index++)
            {
                functions[index].AdjustIndexes(offset);
            }

            //Notify parent function
            if (parentFunction != null
                && requestingFunction != null)
            {
                //If request from its own function, only then notification is required.
                parentFunction.AdjustIndexes(offset, this);
            }
        }


        public override string ToString()
        {
            return Statement;
        }
    }

Function object

Function object has a collection of FunctionParameter objects. Function object always belongs to a parent FunctionParameter object. New FunctionParameter objects can be added to and removed from Function object.
public class Function
    {

        public Function(StringBuilder source
            , int nameStartIndex, int nameEndIndex
            , char enclosingChar
            , FunctionParameter parentParameter
            )
        {
            this.source = source;
            this.nameStartIndex = nameStartIndex;
            this.nameEndIndex = nameEndIndex;
            this.parameters = new List<FunctionParameter>();
            this.enclosingChar = enclosingChar;
            this.parentParameter = parentParameter;



        }


        private FunctionParameter parentParameter;
        public FunctionParameter ParentParameter
        {
            get { return parentParameter; }
        }

        private int nameStartIndex;
        public int NameStartIndex
        {
            get { return nameStartIndex; }
            set
            {
                nameStartIndex = value;

                //Function name shoud be re assigned.
                functionName = null;
            }
        }

        private int nameEndIndex;
        public int NameEndIndex
        {
            get { return nameEndIndex; }
            set
            {
                nameEndIndex = value;

                //Function name shoud be re assigned.
                functionName = null;
            }
        }


        private List<FunctionParameter> parameters;
        public List<FunctionParameter> Parameters
        {
            get { return parameters; }
        }



        private StringBuilder source;
        public StringBuilder Source
        {
            get { return source; }
            set { source = value; }
        }

        private char enclosingChar;
        public char EnclosingChar
        {
            get { return enclosingChar; }
            set
            {
                enclosingChar = value;

                if (parameters.Count > 0)
                {
                    //opening index
                    FunctionParameter firstParam = parameters[0];
                    int openingIndex = (firstParam.EndIndex > firstParam.StartIndex)
                        ? ((firstParam.StartIndex > 0) ? firstParam.StartIndex - 1 : 0)
                          : firstParam.EndIndex;

                    //Closing index
                    FunctionParameter lastParam = parameters[parameters.Count - 1];
                    int closingIndex = (lastParam.EndIndex > lastParam.StartIndex)
                        ? ((lastParam.EndIndex < source.Length - 1) ? lastParam.EndIndex + 1 : source.Length - 1)
                          : lastParam.StartIndex;

                    source[openingIndex] = value;

                    switch (value)
                    {
                        case '[':
                            source[closingIndex] = ']';
                            break;
                        case '{':
                            source[closingIndex] = '}';
                            break;
                        case '(':
                            source[closingIndex] = ')';
                            break;
                        case '<':
                            source[closingIndex] = '>';
                            break;
                        default:
                            source[closingIndex] = value;
                            break;
                    }


                }
            }
        }

        private string functionName;
        public string FunctionName
        {
            get
            {
                if (functionName == null)
                {
                    if (nameStartIndex > nameEndIndex
                        || nameStartIndex < 0
                        || nameEndIndex < 0)
                    {
                        //Function without name
                        functionName = string.Empty;
                    }
                    else
                    {
                        functionName = source.ToString(nameStartIndex
                            , nameEndIndex - nameStartIndex + 1).Trim();
                    }
                }
                return functionName;
            }
            set
            {
                int nameCurrentLength = 0;
                if (nameStartIndex < 0)
                {
                    //Already empty text so no need to remove old text
                    nameStartIndex = 0;
                }
                else if (nameEndIndex > 0)
                {
                    //Remove old text
                    source.Remove(nameStartIndex
                        , nameEndIndex - nameStartIndex + 1);

                    nameCurrentLength = nameEndIndex - nameStartIndex + 1;
                }

                //Insert new text
                source.Insert(nameStartIndex, value);

                //Update endindex
                nameEndIndex = nameStartIndex + value.Length - 1;

                //Assign function name             
                functionName = value.Trim();

                //Offset: subtrace nameCurrentLength from value's length
                int offset = value.Length - nameCurrentLength;

                //Adjust indexes but not of function name.
                AdjustIndexes(offset, true);

            }
        }

        public void RemoveParameter(FunctionParameter parameter)
        {
            int index = parameters.IndexOf(parameter);
            RemoveParameter(index);
        }
        public FunctionParameter RemoveParameter(int index)
        {
            FunctionParameter parameter = parameters[index];
            bool isFirstParam = index == 0;
            bool isLastParam = index == parameters.Count - 1;

            int sourceStartIndex = (isFirstParam)
                ? parameter.StartIndex
                : (parameters[index - 1].EndIndex + 1); //We also need to remove prior comma.

            int sourceEndIndex = (isLastParam)
                ? parameter.EndIndex
                : (parameters[index + 1].StartIndex - 1); //We also need to remove next comma.


            if (sourceEndIndex >= sourceStartIndex)
            {
                int Paramlength = sourceEndIndex - sourceStartIndex + 1;
                source.Remove(sourceStartIndex
                    , Paramlength);

                AdjustIndexes(Paramlength * -1, parameter);
            }

            parameters.RemoveAt(index);

            return parameter;


        }

        public FunctionParameter AddParameter(string parameterText)
        {
            return InsertParameter(parameterText, parameters.Count);
        }

        public FunctionParameter InsertParameter(string parameterText, int index)
        {
            string comma = " , ";

            //index adjustment
            if (index < 0)
            {
                index = 0;
            }
            else if (index > parameters.Count)
            {
                index = parameters.Count;
            }


            bool isFirstParam = index == 0;
            bool isLastParam = index == parameters.Count;


            //Param text for source, it may be with comma
            string textForSource = parameterText;


            int sourceStartIndexForParam;
            int sourceStartIndex;
            if (isFirstParam)
            {
                sourceStartIndexForParam = sourceStartIndex = nameEndIndex + 2; // indext after openning bracket

                if (!isLastParam)
                {
                    textForSource = textForSource + comma; // Ended by a comma
                }
            }
            else
            {
                sourceStartIndex = parameters[index - 1].EndIndex + 1;
                textForSource = comma + textForSource; // Should be started with comma
                sourceStartIndexForParam = sourceStartIndex + comma.Length;
            }

            //Make param object
            FunctionParameter param = new FunctionParameter(source
                , sourceStartIndexForParam
                , sourceStartIndexForParam + parameterText.Length - 1
                , this);

            //Insert into param list
            parameters.Insert(index, param);

            //Insert text into source
            source.Insert(sourceStartIndex, textForSource);

            AdjustIndexes(textForSource.Length, param);

            return param;

        }



        public void AdjustIndexes(int offset)
        {
            AdjustIndexes(offset, null, false);
        }

        public void AdjustIndexes(int offset, bool ownRequest)
        {
            AdjustIndexes(offset, null, ownRequest);
        }

        public void AdjustIndexes(int offset
            , FunctionParameter requestingParam)
        {
            AdjustIndexes(offset, requestingParam, false);
        }
        public void AdjustIndexes(int offset
            , FunctionParameter requestingParam
            , bool ownRequest)
        {
            if (requestingParam == null && !ownRequest)
            {
                nameEndIndex = nameEndIndex + offset;
                nameStartIndex = nameStartIndex + offset;
            }
            else
            {
                //it is requested from either its own argument or itself. 
                //In both cases, function name adjustment is not required.
            }

            //adjustment should be applied only onward params of requeting param
            int index = (requestingParam != null)
                ? parameters.IndexOf(requestingParam) + 1
                : 0;

            for (; index < parameters.Count; index++)
            {
                parameters[index].AdjustIndexes(offset);
            }

            //Also notify parent
            if (requestingParam != null || ownRequest)
            {
                //if request from either child param or itself, only then notification will be required.
                parentParameter.AdjustIndexes(offset, this);
            }
        }

        public override string ToString()
        {
            return FunctionName;
        }
    }


Example

select ISNULL(Convert(datetime , CreatedOn , 103) , getDate()) from Orders.
After parsing above query, system will give a FunctionParameter object with following details
Seq Property Value
1 Statement select ISNULL(Convert(datetime , CreatedOn , 'DD\MM\YYYY') , getDate()) from Orders.
2 ParentFunction [Null]
3 Functions [List]
1.1    FunctionName ISNULL
1.2    ParentFunctionParameter[ Seq# 1]
1.3    FunctionParameters [List]
1.3.1         Statement Convert(datetime , CreatedOn , 'DD\MM\YYYY')
1.3.2         ParentFunction [Seq# 1.1]
1.3.3         Functions [List]
1.3.3.1            FunctionName Convert
1.3.3.2            ParentFunctionParameter[Seq# 1.3.1]
1.3.3.3            FunctionParameters [List]
1.3.3.3.1                 Statement datetime
1.3.3.3.2                 ParentFunction [Seq#  1.3.3.1]
1.3.3.3.3                 Functions [Empty List]
1.3.3.3.4                 Statement CreatedOn
1.3.3.3.5                 ParentFunction [Seq#  1.3.3.1]
1.3.3.3.6                 Functions [Empty List]
1.3.3.3.7                 Statement 103
1.3.3.3.8                 ParentFunction [Seq#  1.3.3.1]
1.3.3.3.9                 Functions [Empty List]
1.3.4         Statement getDate()
1.3.5         ParentFunction [Seq# 1.1]
1.3.6         Functions [List]
1.3.6.1            FunctionName getDate
1.3.6.2            ParentFunctionParameter[Seq# 1.3.4]
1.3.6.3            FunctionParameters [Empty List]

Query Conversion Routine

After getting Function objects , we can easily write a routine for required conversion.
public static string ConvertQuery(SortedList flatListOfFunction
            , StringBuilder source)
        {
            foreach (int key in flatListOfFunction.Keys)
            {
                Function func = flatListOfFunction[key];
                if (func.FunctionName.ToUpper() == "CONVERT")
                {
                    if (func.Parameters.Count == 3)
                    {
                        if (func.Parameters[2].Statement == "103"
                            && func.Parameters[0].Statement.ToUpper() == "DATETIME")
                        {
                            func.FunctionName = "TO_Date";
                            func.RemoveParameter(0);
                            func.Parameters[1].Statement = "'DD/MM/YYYY'";
                        }

                    }
                }

                if (func.FunctionName.ToUpper() == "ISNULL")
                {
                    func.FunctionName = "NVL";
                }

                if (func.FunctionName.ToUpper() == "SUM")
                {
                    func.FunctionName = "Add";
                }
                if (func.FunctionName.ToUpper() == "GETDATE")
                {
                    func.FunctionName = "SysDate";
                    func.EnclosingChar = ' ';
                }
            }

            return source.ToString();

        }
After run query conversion method, above query will modified as follows.
select NVL(To_Date(CreatedOn , 'DD\MM\YYYY') , sysdate) from Orders
read more...

25 February 2011

eXtremecode, Generated Code Customization Methods.


Thank you for giving good response to my first post, related to the introduction of eXtremecode ASP.Net Generator. Now it's time to dive into further to reveal the mean of using and saving custom code.

Overview

Usually generated code is not the final output for any project. Some customization in generated code is always required. As chances of losing custom work, most of the time developers don't want to take the risk of code regenerating  if some project specific modifications have been done in generated code. But normally it always happens to have to regenerate the code.

Summary

In order to save custom code or code modifications, eXtremecode generator offers to methods of customization. One is by configuring XML and other is by keeping modifications in custom regions.

Code Customization By configuring XML file.

If required customization can be done by configuring XML, eXtremecode recommends to do it by using XML configuration rather to do it by using custom regions.

<entity name="Company" fieldsCountInSingleRow = "2" gridSortedBy= "ImportedOn" typeCode="BOPage">
    <fields>
      <field name="CompanyId" ordinal="1" displayed="false" />
      <field name="Description" ordinal="2" />
      <field name="RegionCode" ordinal="3" />
      <field name="CountryId" listTable="Country" listTableKeyField="CountryId" listTableDisplayFields="Country" caption="Country" />
      <field name="CurrencyId" listTable="Currency" listTableKeyField="CurrencyId" listTableDisplayFields="Currency" caption="Currency" />
      <field name="ImportedOn" ordinal="40" usingTime="true" />
 </fields>
 </entity>

Custom XML Definition

Before knowing about the list of modifications, which can be done by XML configurations, we first need to know about the structure of that XML file.
As this file is nothing but the skeleton of entity defnition so we can call it Custom XML Definition. See my first article (eXtremecode ASP.Net Generator Introduction) for basic details.
Unlike Normal eXtremecode's XML Definition, Custom XML Definition can contains multiple entity elements(<entity>) of the same name. Same named entities are individually identified by different values of typeCode.

typeCode

typeCode identifies the type of out put file which is using particular customization.
Type Codes are totally based on template definitions so according to our needs, new Type Codes can be defined in future. Currently we have following Type Codes.
  • BOPage - Common for all pages of particular entity.
  • BOListSearch - For search panel of particular entity.
  • BOListGrid - For Grid panel of particular entity (result set).
  • BOEditPage - For edit page of particular entity.

Entity Level Modifications

User can do following modifications at entity level
  • gridKey

    In order to sort paginated grid properly, It is mandatory to define the key for Views and for the Tables which don't have any primary and unique key. Multiple columns can also be provided by separating with coma (,).

  • gridSortedBy

    It is used to define the default sorting for the grid. gridSortedBy will be considered same as gridKey if it is not defined.

  • fieldsCountInSingleRow

    It is used to define number of fields which will be rendered in single row. it is used by search and edit panel. In below screen shot of search panel, there is two fields in each row

Entity Field Level Modifications

User can do following modifications at entity field level
  • listTable

    It is used to define the dictionary table of particular filed. system will render this field as dropdown list in web pages. No need to define listTable if relation/FK is already defined in database for this field.

  • listTableKeyField

    If you need to define listTable, you must also need to define the key field of that table.

  • listTableDisplayFields

    It is used to define the display fields for dropdown list. Multiples columns can also be provided by separating with coma (,).

  • formatString

    It is used to define the format of display fields of dropdown list. It must be ensured that format string will be (Format method of .Net String object) compliance.
    Suppose if we define listTableDisplayFields = "UserName,Age" and formatString = "{0} is {1} years old" then, display value in dropdown list will something like "Naveera is 2 years old".

  • usingRange

    It is a flag to define whether the range is taken in search criteria or not. It is only used for all numerics and datetime data types. By default its value is true.

  • displayed

    It is a flag to identify whether to render this field or not. Default value is true

  • usingTime

    It is a flag to identify whether to show time with date or not. Default value is false.

  • confirmationRequired

    It is a flag to identify whether a confirmation field is also required or not. e-g Password Confirmation.

  • ordinal

    It is used to define the order in which field will be rendered in web pages.

  • aspxAttributes

    it is used to define others, aspx webcontrol related attributes. If we define
    aspxAttributes = "TextMode@Password|Visible@True" then it will be rendered into aspx like ( TextMode = "Password" Visible = "True")

Custom Regions

protected void gvCountryBO_RowCreated(object sender, GridViewRowEventArgs e)
{
 switch (e.Row.RowType)
 {
  case DataControlRowType.DataRow:

   /** Custom Region Start [Action Button Server Side Code] **/
            //delete button
   ImageButton btnDelete = e.Row.FindControl("btnDelete") as ImageButton;
   btnDelete.Attributes.Add("onclick",
    string.Format("if (!confirm('{0}')) return false;",
     ResourceProvider.GetGeneralResourceString("Message_WantToDeleteThis")));

   
   /** Custom Region End **/

   break;

  case DataControlRowType.Header:
   //delete selected button
   ImageButton btnDeleteSelected = e.Row.FindControl("btnDeleteSelected") as ImageButton;
   btnDeleteSelected.Attributes.Add("onclick",
    string.Format("if (!confirm('{0}')) return false;",
     ResourceProvider.GetGeneralResourceString("Message_WantToDeleteSelected")));

   break;

 }
}
Customer regions are special code area in generated code, which is supposed to should not be modified by eXtremeCode generator if code is regenerated. In other word, code will remain safe in these regions.

Region Syntax

custome codes can be enclosed by one of the followings.
  • For code behind files

    /** Custom Region Start [Code Generator] **/
    custom code will be here.
    /** Custom Region End **/

  • For HTML files

    <!-- Custom Region Start [Code Generator] -->
    custom code will be here.
    <!-- Custom Region End -->
    

  • For ASPX pages

    <%-- Custom Region Start [Code Generator] --%>
    custom code will be here
    <%-- Custom Region End --%>
The text enclosed by square barackets in above examples "Code Generator" is the name of custom region in particular file. Per file, this name should be unique.

Latest update

As much experience of using eXtremecode generator, it has been identified that most of the time we want to regenerate custom regions as well specially if we want to incorporate new added database's fields in our generated code. For resolving this issue, now it is mandatory to attach a tag with each region. Only those custom regions are supposed to have been modified which are tagged. Tag can be any leading word of the text, "Custom Region Start". In below mentioned example "Modified#" is the tag.
<%-- Modified# Custom Region Start [Code Generator] --%>
custom code will be here
<%-- Custom Region End --%>
By searching the text, "# Custom Region Start", we can easily identify the regions which we have modified.
read more...

23 June 2010

eXtremecode ASP.Net Generator Introduction


Overview

eXtremecode or eX ASP.Net Generator is simply a tool which takes a collection of database connections as an input and produce a well-formed Asp.net Application.

eX Generator consists of two applications. One application is eXtremecode Definition Generator (eXDG), for generating database schema which takes collection of database connections as an input and gives databases schema (definition) in the form of an XML file. And second application is eXtremecode Generator (eXG), for generating final code e-g Asp.Net application which takes generated database definitions and predefined templates as an input and gives final result (currenty an Asp.Net application).


eXtremecode Definition Generator (eXDG)

eXDG takes collection of connections which are defined in an XML file. Path of that XML file can be configured from eXDG's applicatin configuratin file.
<configuration>
  <appSettings>
    <add key="ConnectionsPath" value="Connections.config" />
Currently, eXDG supports SQLServer, Oracle and MySQL databases. Example of connections defining XML is shown below.
<connections>
  <connection name="SQLServerConn" connectionString="Persist Security Info=False;User ID=sa;Initial Catalog=DBName;Data Source=ServerName;Password=password" type="SQLServer" isDefault="false" encrypted="false"/>
  <connection name="SQLServer2005Conn" connectionString="Persist Security Info=False;User ID=sa;Initial Catalog=DBName;Data Source=ServerName;Password=password" type="SQLServer05" isDefault="false" encrypted="false"/>
  <connection name="OracleConn" connectionString="Password=password;User ID=userName;Data Source=ServerName;Persist Security Info=True" type="Oracle" isDefault="false" encrypted="false"/>
  <connection name="MySQLConn" connectionString="server=ServerName;User Id=userId;database=dbName;Persist Security Info=True;Password=password;Port=3306" type="MySQL" isDefault="true" encrypted="false"/>
</connections>

Connection Attributes

  • Name: For unique identification of a connection..
  • Connection String: Simply defining connectivity to database. The format of this string depends on type of the connection.
For using MySql Connection, it is required to install MySQL .Net connector first. 
For complete details about MySQL connectivity with .Net application,
please visit MySQL official site.
  • Type: For telling system about type of the database (SQL server,MySQL,Oracle).
  • IsDefault: If we have more then a connection in our application, we can set one connection as a default. If we don't provide the name of the connection while querying to database. System will pass that query to default connection for execution.
  • Encrypted: For telling system about provided connection string, whether it is encrypted or not .

Running eXDG

   Once we define all required connections now we can run eXDG application. At the time of form loading system will read all defined connections and populate a grid with list of  tables and views of respective databases.


Option, Select Related Entities

Normaly, all database tables are not required in an application. If we have large number of tables in our database, it gets difficult to select required entities. So if we check "related entities" check box and then select a table from grid, system will select all related tables of that table for us.

Option, Refresh Entities

If "Refresh Entities" check box is unchecked, when we regenerate definition/schema of entities, system will generate definition for only new selected entities (tables/views) and already generated entities selection will be ignored.
Some time we create new tables in database which have relations with old tables/entities. In that case we want to regenerate old entities so relations of new created tables can also be incorporated with old entity. In order to regenerate old entities, we will keep "Refresh Entities" check box checked.

Generated Definition/Schema Files

Once we click generate button, system will generate two xml files at defined paths.File paths can be configured from application configuration file.
<add key="EntityDefinitionPath" value="eXDefinition.xml" />
<add key="EntityCustomizedPath" value="eXCustomDefinition.xml" /> 

"EnitiyDefinitionPath" is for Xml file which contains whole schema of selected entities. Here is the sample.
<entity name="Promotion" accessorName="SQLServerConn" type="db" table="Promotion" view="Promotion">
    <fields>
      <field name="IsActive" datatype="bit" type="native" key="false" computed="false" unique="false" nullable="false" autoGenerated="false" />
      <field name="Stock" datatype="int" type="native" key="false" computed="false" unique="false" nullable="false" autoGenerated="false" />
      <field name="Title" datatype="text" type="native" key="false" computed="false" unique="false" nullable="true" autoGenerated="false" />
      <field name="LaunchDate" datatype="datetime" type="native" key="false" computed="false" unique="false" nullable="false" autoGenerated="false" />
      <field name="PromotionId" datatype="int" type="native" key="true" computed="true" unique="true" nullable="false" autoGenerated="false" />
      <field name="PromotionType" datatype="int" type="native" key="false" computed="false" unique="false" nullable="false" autoGenerated="false" />
      ........
      ........
 
    </fields>
    <children>
      <entityReference name="CustomerCategoryPromotion">
        <relationalFields referenceField="PromotionId" nativeField="PromotionId" />
      </entityReference>
      <entityReference name="PromotionItem">
        <relationalFields referenceField="PromotionId" nativeField="PromotionId" />
      </entityReference>
      <entityReference name="OrderItem">
        <relationalFields referenceField="PromotionId" nativeField="PromotionId" />
      </entityReference>
    </children>
    <parents>
      <entityReference name="PromotionType">
        <relationalFields referenceField="PromotionTypeId" nativeField="PromotionType" />
      </entityReference>
    </parents>
</entity> 
"EntityCustomizedPath" is for Xml file which contains only name of selected entities with their fields. It is used for entity level customization of generated code. Here is the sample.
<entity name="Promotion">
    <fields>
      <field name="IsActive" />
      <field name="Stock"/>
      <field name="Title" />
      <field name="LaunchDate" />
      <field name="PromotionId"  />
      <field name="PromotionType" />
      ........
      ........
 
    </fields>
</entity> 
I will explain schema attributes and entity level customization in details, in advanced articals.

eXtremecode Generator (eXG)

Once we generate definition and do required customization, now we can generate final code with the help of predefined templates.
Generation of Asp.Net application is actually based on defined templates.
Any kind of  application can be generated by defining required templates.
Currently, I have written templates for ASP.Net C# application only. 
Generating language (the language in which templates are written) is C#.  

Repository.xml

All defined templates will be controlled or managed by "Repository.xml" file. From Repository.xml file, we can configure followings.
  • Name of the project
  • Name of schema and custom entity files
  • Scurity settings
  • Names of template files (Session Files).
  • Defining generated files.
  • Managing custom code of developer.
Repository management is an advanced topic so I will explain it later in more details. Here is an example of  Repository.xml file.
<?xml version="1.0" encoding="utf-8" ?>
<Repository projectName="MYSQLTest" entityDefinitionFile= "EntityDefinitions.xml" entityCustomFile= "EntityCustomized.xml">
 <Security>
  <UserLogin entity="Duplicate-Users" userIdField="UserId" userNameField="UserName" passwordField="Password" defaultRoleIdField="DefaultGroupId" relatedRolesEntity="Duplicate-user_groups" roleIdField="GroupId" />
  <UserRole entity="Duplicate-Groups" roleIdField="GroupId" roleNameField="GroupName" homeModuleIdField="defaultModuleId" relatedModulesEntity="Duplicate-group_modules" moduleIdField="ModuleId"  />
 </Security>
 
 <SessionFiles>
  <!--<File name="report.cs" />
  <File name="share-script.cs" />-->
  <File name="BusinessObject.cs" />
  <File name="IncludedCode.cs" />
  <File name="BODataSet.cs" />
  <File name="BOCommon.cs" />
  <File name="BOList.aspx.cs" />
  <File name="BOList.aspx" />
  <File name="BO.resx" />
  <File name="BOEdit.aspx" />
  <File name="BOEdit.aspx.cs" />
  <File name="Login.aspx.cs" />
  <File name="Login.aspx" />
  <File name="UserProfile.cs" />
  <File name="Default.aspx" />
  <File name="Default.aspx.cs" />
  <File name="sitemap.xml" />
  <File name="Global.asax" />
  <File name="ChangePassword.aspx.cs" />
  <File name="ChangePassword.aspx" />
 </SessionFiles>
 
 <GeneratedFiles>
  <!--Edit Page-->
  <File name="\aspx\{0}BOEdit.aspx.cs" type="table" copyFile="" cond="" 
   mappCustomCode="true" enabled="true" 
   sessionList="BOEditWholeClass" />
   
   <File name="\aspx\{0}BOEdit.aspx" type="table" copyFile="" cond="" 
   mappCustomCode="false"  enabled="true"
   sessionList="ASPXBOEditWholeClass" />
 
  
  
  <!--Business Objects-->
  <File name="\App_Code\BusinessObjects\{0}BO.cs" type="table" copyFile="" cond="" 
   mappCustomCode="false"  enabled="true"
   sessionList="BUZWholeClass" />
 
  <File name="\App_Code\BusinessObjects\{0}BOCol.cs" type="table" copyFile="" cond=""
   mappCustomCode="true"  enabled="true"
   sessionList="BUZColWholeClass" />
  
  
  <!--List Page-->
  <File name="\aspx\{0}BOList.aspx.cs" type="table" copyFile="" cond="" 
   mappCustomCode="true"  enabled="true"
   sessionList="BOListWholeClass" />
   
   <File name="\aspx\{0}BOList.aspx" type="table" copyFile="" cond="" 
   mappCustomCode="true"  enabled="true"
   sessionList="ASPXBOListWholeClass" />
 
   
   <!--Resource-->
   <File name="\App_GlobalResources\BusinessObjects\{0}BO.resx" type="table" copyFile="" cond="" 
   mappCustomCode="true"  enabled="true"
   sessionList="ResourceBOListWholeClass" />
  
  
   
   <!-- Business Base -->
   <File name="\App_Code\DataTables\{0}DataTable.cs" type="table" copyFile="" cond=""
   mappCustomCode="true" enabled="true"
   sessionList="BUZDataTableWholeClass" />
   
   <File name="\App_Code\BODataSet.cs" type="once" copyFile="" cond="" enabled="true"
   mappCustomCode="true" 
   sessionList="BUZDataSetWholeClass" />
   
   <File name="App_Code\BOCommon.cs" type="once" copyFile="" cond="" enabled="true"
   mappCustomCode="true" 
   sessionList="BOCommonWholeClass" /> 
   
   <File name="Login.aspx.cs" type="once" copyFile="" cond="" enabled="true"
   mappCustomCode="true" 
   sessionList="LoginWholeClass" /> 
   
   <File name="Login.aspx" type="once" copyFile="" cond="" enabled="true"
   mappCustomCode="true" 
   sessionList="ASPXLoginWholeClass" /> 
   
   <File name="ChangePassword.aspx.cs" type="once" copyFile="" cond="" enabled="true"
   mappCustomCode="true" 
   sessionList="ChangePasswordWholeClass" /> 
   
   <File name="ChangePassword.aspx" type="once" copyFile="" cond="" enabled="true"
   mappCustomCode="true" 
   sessionList="ASPXChangePasswordWholeClass" />
   
   
   <File name="Default.aspx" type="once" copyFile="" cond="" enabled="true"
   mappCustomCode="true" 
   sessionList="ASPXDefaultWholeClass" /> 
   
   <File name="Default.aspx.cs" type="once" copyFile="" cond="" enabled="true"
   mappCustomCode="true" 
   sessionList="DefaultWholeClass" /> 
   
   <File name="Web.sitemap" type="once" copyFile="" cond="" enabled="true"
   mappCustomCode="true" 
   sessionList="SitemapWholeClass" /> 
   
   <File name="Global.asax" type="once" copyFile="" cond="" enabled="true"
   mappCustomCode="true" 
   sessionList="GlobalASAXWholeClass" />
   
   
   <File name="App_Code/UserProfile.cs" type="once" copyFile="" cond="" enabled="true"
   mappCustomCode="true" 
   sessionList="UserProfileWholeClass" /> 
   
   <File name="unchangedFiles" type="CopyFolder" copyFile="WebSite" cond="" enabled="true"
   mappCustomCode="true" 
   sessionList="UserProfileWholeClass" /> 
   
   
  <!--MappCustomCode= [false,true]  this option can b used when for avoiding custom code conflict but user code
   will be lost. By default its value is true -->
  <!--type = table,once,copy-->
  <!--cond = not using yet-->
 </GeneratedFiles>
</Repository>

Running eXG

First we need to copy generated definition files into the folder of defined templates so we can configure them in "Repository.xml". Once application gets started, select the location of templates and output dirctory where asp.net application wants to be created.


Now click load button for loading templates, last generated entities (definitions generated by eXDG) will be populatd in a grid. Select required entities and click generate button for generating final code.

Option, Overwrite

While generating code, by default system checks whether generated file already created or not. If file already created, system prompts and asks user to overwrite that file or leave it unchanged. If we check "overwrite" check box, system overwrites all selected files without prompting user.

Software Setup

I have uploaded setup of eXtremecode Asp.Net generator here. it contains two sub folder. one contains eXtrtemecode Definition Generator application and otherone contain eXtremecode Generator application with code of predefined templates. I have written steps to generate ASP.Net application in ReadMe.txt file, it will must help you.



Northwind Example

I have generated Asp.Net application for Northwind database. Please download it from here. I have also enclosed the sample configuration of templates for Northwind database with the setup of software in eXG folder so if you will be intrested to generate Northwid Asp.net application your self, you can use that configurations.



Source Code

You can download complete source code of the generator from here. If you modify the product, don't forget to share updated source code at http://extremecodeworld.codeplex.com. For more details about sharing updates of code, please visit here.


Upcoming Articles

That was all the introduction of eXtremecode Asp.net application generator with basic details. I hope this intro will be pretty enough  to generate simple asp.net application. Still there is lot of things which need to be explained,. here is the summary of topics which will be explained in upcoming articles.
  • Generated Definitions/Schema - Explanation of all elements and their attributes.
  • Entity Level Customization -  How to order fields of specific entity in ASPX pages. How to control visibility and nature of web controls.
  • Templates - How to modify templates. what are conventions have to be followed while writing a template.
  • Templates Management and Controlling - How templates will be executed to generate code. Explanation of "Repository.xml" contents, 
  • Security Implementation and Membership Provider - How to manage rights of user in ASP.Net application. explanation of custom Membership provider. Explanation of custom attributes of "Web.sitemap" xml file. Explanation of controling visibility of items in navigation controls (Menu, TreeView and  SiteMapPath) 
  • Embedding of developer's custom code into generated code - how to embed custom code in the way that code will be safe after regenerating of code.
  • Decorators or Application Appearance - How to use decorators. How to define new decorator for designing new layout and appearance of ASP.Net pages.
  • Business Layer - How to query database through Bussiness Objects. Code level details. 
  • Data Access Layer - How to implement new Accessor of database. Code level details.
Priority of posting of advanced articles will be based on your interest.
So please share your interest and provide your feedback that how much you have found  
extremecode Generator helpfull in web development.  
If you are interested to follow up these topics, please subscribe right now.

Awards


Windows 7 Download

Top 4 Download

read more...