Showing posts with label eX Generator. Show all posts
Showing posts with label eX Generator. Show all posts

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...

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...

02 June 2010

eXtremecode Comparison With ASP.Net Maker

eX Generator Tool has been developed as a result of inspiration with well Known .Net generating application called ASP.Net Maker. Asp.Net Maker is much mature and available with lot of customization support. I also have used it professionally. Many other generating applications are available on internet but they are not looking that effective.





read more...