RSS

Category Archives: Web Application

ExtJS and ASP.NET MVC 3: CRUD DataGrid

This short tutorial will walk though the implementation of DataGrid using ExtJS 3.3.1 andASP.NET MVC 3. In this tutorial I focus more on the integration of the front-end, so you will not find any database code. Whenever we deal with data we usually create, read/retrieve, update or delete them. ExtJS provides a great data grid component and the ability to perform CRUD operation with very little coding. I used JSON as data format in the example below.

ExtJS DataGridExtJS DataGrid

Let’s start with the server-side by developing the data model and controller.

Controller:

01 public class ContactController : Controller
02 {
03     //
04     // GET: /Contact/
05
06     public ActionResult Index()
07     {
08         return View();
09     }
10
11     public JsonResult Load()
12     {
13         var contact = new List<Contact> {
14             new Contact("Smith","95746325","smith@me.com"),
15             new Contact("Adam","87291034","adam@me.com"),
16             new Contact("Eve","98271345","eve@me.com"),
17             new Contact("Chun Li","81728312","chun.li@me.com")
18         };
19         return Json(new
20         {
21             total = contact.Count,
22             data = contact,
23         }, JsonRequestBehavior.AllowGet);
24     }
25
26     [HttpPost]
27     public JsonResult Create(List<Contact> data)
28     {
29         //insert Create code
30         return Json(new
31         {
32             data = new Contact(data[0].Name, data[0].Phone, data[0].Email),
33             success = true,
34             message = "Create method called successfully"
35         });
36     }
37
38     [HttpPost]
39     public JsonResult Update(List<Contact> data)
40     {
41         //insert Update code
42         return Json(new
43         {
44             success = true,
45             message = "Update method called successfully"
46         });
47     }
48
49     [HttpPost]
50     public JsonResult Delete(List<string> data)
51     {
52         //insert Delete code
53         return Json(new
54         {
55             success = true,
56             message = "Delete method called successfully"
57         });
58     }
59 }

Data Model:

01 public class Contact
02 {
03     public string Id { getset; }
04     public string Name { getset; }
05     public string Phone { getset; }
06     public string Email { getset; }
07
08     public Contact(string pName, string pPhone, string pEmail)
09     {
10         this.Id = System.Guid.NewGuid().ToString();
11         this.Name = pName;
12         this.Phone = pPhone;
13         this.Email = pEmail;
14     }
15
16     public Contact() { }
17 }

ExtJS:

Now, you move on to the view and work on the ExtJS. First, you define the record type which matches the server-side object.

01 var Contact = Ext.data.Record.create([
02     {
03         name: 'Id',
04         type: 'string'
05     }, {
06         name: 'Name',
07         type: 'string'
08     }, {
09         name: 'Phone',
10         type: 'string'
11     }, {
12         name: 'Email',
13         type: 'string'
14     }
15 ]);

Now you need to setup the Writer and Reader

01 var writer = new Ext.data.JsonWriter({
02     encode: false,
03     listful: true,
04     writeAllFields: true
05 });
06
07 var reader = new Ext.data.JsonReader({
08     totalProperty: 'total',
09     successProperty: 'success',
10     idProperty: 'Id',
11     root: 'data',
12     messageProperty: 'message'  // <-- New "messageProperty" meta-data
13 }, Contact);

Then, setup a proxy to define the connection to the controller.

1 var proxy = new Ext.data.HttpProxy({
2     api: {
3         read: '/Contact/Load',
4         create: '/Contact/Create',
5         update: '/Contact/Update',
6         destroy: '/Contact/Delete'
7     },
8     headers: { 'Content-Type''application/json; charset=UTF-8' }
9 });

Hooks the above components (reader, writer, proxy) to the store.

1 var store = new Ext.data.Store({
2     id: 'user',
3     proxy: proxy,
4     reader: reader,
5     writer: writer,
6     autoSave: false
7 });

Add the data grid declaration

01 var grid = new Ext.grid.GridPanel({
02     store: store,
03     columns: [
04         { header: "Name",
05             width: 170,
06             sortable: true,
07             dataIndex: 'Name',
08             editor: {
09                 xtype: 'textfield',
10                 allowBlank: false
11             }
12         },
13         { header: "Phone No.",
14             width: 160,
15             sortable: true,
16             dataIndex: 'Phone',
17             editor: {
18                 xtype: 'textfield',
19                 allowBlank: false
20             }
21         },
22         { header: "EMail",
23             width: 170,
24             sortable: true,
25             dataIndex: 'Email',
26             editor: {
27                 xtype: 'textfield',
28                 allowBlank: false
29             }
30         }
31     ],
32     plugins: [editor],
33     title: 'Contacts DataGrid',
34     height: 300,
35     width: 510,
36     tbar: [{
37         iconCls: 'icon-user-add',
38         text: 'Add Contact',
39         handler: function () {
40             var e = new Contact({
41                 Name: 'New Friend',
42                 Phone: '(65) 89182736',
43                 Email: 'new@google.com'
44             });
45             editor.stopEditing();
46             store.insert(0, e);
47             grid.getView().refresh();
48             grid.getSelectionModel().selectRow(0);
49             editor.startEditing(0);
50         }
51     }, {
52         ref: '../removeBtn',
53         iconCls: 'icon-user-delete',
54         text: 'Remove Contact',
55         handler: function () {
56             editor.stopEditing();
57             var s = grid.getSelectionModel().getSelections();
58             for (var i = 0, r; r = s[i]; i++) {
59                 store.remove(r);
60             }
61         }
62     }, {
63         iconCls: 'icon-user-save',
64         text: 'Save All Modifications',
65         handler: function () {
66             store.save();
67         }
68     }]
69 });

Some observations:

  • When submitting data as JSON, set the headers “Content-Type” to “application/json” in the proxy object and set the encoding to false in the JsonWriter. If not, it will be treated as form submission.
  • Since I set the auto save to false. DataGrid will submit list of contact when there are 2 or more changes, however it will send a single object when there is 1 change. In order to make it consistent to send as list, set list full to true in the JsonWriter.
  • ASP.NET MVC 3 able to recognize the JSON without additional coding.

If you want to see the complete source code, download it from here. Hope it’s useful. :)

Happy hacking!

Referenced by: http://myxaab.wordpress.com/2011/02/20/extjs-and-asp-net-mvc-3-crud-datagrid/

 
1 Comment

Posted by on October 23, 2011 in Javascript, Web Application

 

Web Service via Data Access Object Pattern

Introduction

Access to the data source varies depending on the source of data. Access to persistent storage, such as to a database, varies greatly depending on the type of storage and vendor implementation. The ODBC API enables applications to use SQL statements. However, even within RDBMS environments, the actual syntax and format of SQL statements vary depending on the particular database product. There is even greater variation with different types of persistent storage. Access mechanism, supported APIs, and features vary between different types of persistent storage, such as RDBMS, object-oriented databases, LDAP, flat file, XML file, and so forth. Such disparate data sources make challenges to applications, and can potentially create a direct dependency between the application and the data source code. When business components need to access a data source, they use the appropriate APIs to create connectivity and manipulate the data source. But including the data access code within business components introduces a tight coupling between the business components and the data source implementation. Such code dependencies make it difficult to migrate from one type of data source to another. When the data source changes, the business components have to be changed to handle the new type of data source.

Solution

Use the Data Access Object to do all manipulations to the data source object. The DAO manages the connection with the data source to get and store data. The DAO implements all access functionality to work with the data source. It doesn’t matter what the data source is. It could be any persistent storage such as RDBMS, a repository like LDAP, or even just XML files. The business components that rely on DAO use a simple interface exposed by DAO to its clients. The DAO doesn’t show the data source implementation and there is no need to do this. Because the DAO interface doesn’t need to change when the underlying data source changes, this ability allows DAO to adapt to different types of storage without having to handle it within the business component. You can also say that the DAO acts as an adapter between the components and the data source object.

Picture 1. Relationships in the DAO pattern
Collapse
       ------------------   uses          -------------------   encapsulates    -----------
      |   BusinessObject | ------------> |  DataAccessObject | --------------> | DataSource | 
       ------------------                 -------------------                   ------------
                         \                 /
                          \               /
                           \             /  
obtains, modifies, and      \           / save the transfer object into  
creates the transfer object  \         /  persistent data storage
                              \       /
                               \     /
                                \   /
                                \/ \/
                           -----------------
                          |  TransferObject |
                           -----------------

Business Object

This object represents the data client. It’s the object that requires access to the data source to obtain and modify the data.

Data Access Object

This object is the underlying object in this pattern. It abstracts the data access implementation for its clients to enable transparent access to the data source. The business object delegates the data creation and stores actions to the data access object.

Data Source

This object represents a data source implementation. It could be any persistent storage, for instance, RDBMS, OODBMS, XML repository, flat file system, and so forth.

Transfer Object

This object represents a data carrier. The data access object uses this object to return the data to the client as well as to receive updates and create the data in the data source.

Sample Project

I provided this sample project with two kinds of DAO: XmlRepository and MSSql. This sample project demonstrates how easy it is to use this pattern. It also shows that any migration from one data source to another is a piece of cake.

Screenshot - Diagram.jpg

As you can see, I use the abstract factory model. DaoFactory is the abstract class for all DAO factories.

Collapse
public abstract class DaoFactory 
{
    public abstract  CustomerDao  GetCustomerDao();
    public abstract  OrderDao   GetOrderDao();
       
    public static DaoFactory GetDaoFactory()
    {
        string strDaoFactoryType = ConfigurationManager.AppSettings["DaoFactory"];
        if (String.IsNullOrEmpty(strDaoFactoryType))
            throw new NullReferenceException("DaoFactory type configuration " + 
                                             "is missing from your web.config.");

        else
        {
            Type DaoFactoryType = Type.GetType(strDaoFactoryType);
            if (DaoFactoryType == null)
                throw new NullReferenceException("DaoFactory type can not be found.");

            Type DaoFactoryBaseType = 
                 Type.GetType("AndrewGolik.DaoTraining.DataAccessLayer.DaoFactory");
            if (!DaoFactoryBaseType.IsAssignableFrom(DaoFactoryType))
                throw new ArgumentException("DaoFactory type does not inherits " + 
                          "from AndrewGolik.DaoTraining.DataAccessLayer.DaoFactory.");

            DaoFactory daoFactory = (DaoFactory)Activator.CreateInstance(DaoFactoryType);
            return daoFactory;
        }
    }
}

The implementation of MSSqlDaoFactory:

Collapse
public class MSSqlDaoFactory:DaoFactory
{
    public static string ConnectionString
    {
        get
        {
            if (ConfigurationManager.ConnectionStrings["MsSqlDao"] == null)
                throw new NullReferenceException("MsSqlDao connectionString " + 
                          "configuration is missing from your web.config.");
            string connectionString = 
                   ConfigurationManager.ConnectionStrings["MsSqlDao"].ConnectionString;
            if (String.IsNullOrEmpty(connectionString))
                throw new NullReferenceException("MsSqlDao connectionString " + 
                          "configuration is missing from your web.config.");
            else
                return connectionString;
        }
    }
    public override CustomerDao GetCustomerDao() 
    {
       return new MSSqlCustomerDao();
    }
    public override OrderDao GetOrderDao() 
    {
        return new MSSqlOrderDao();
    }
}

The implementation for XmlDaoFactory is the same, except specific changes. XmlDaoFactory doesn’t have the connection string, but it has paths to XML files.

Collapse
public class XmlDaoFactory:DaoFactory
{
    public static string PathToCustomersFile
    {
       get{
         string pathToFile = HttpContext.Current.Server.MapPath(
                                "App_Data/XmlRepository/Customers.xml");
         if (!File.Exists(pathToFile))
         {
             XmlDocument xmlDoc = new XmlDocument();
             xmlDoc.LoadXml("<customers lastgeneratedid="\"0\"">");
             xmlDoc.Save(pathToFile);
         }
         return pathToFile;
       }
    }
    public static string PathToOrdersFile 
    {
        get
        {
            string pathToFile = HttpContext.Current.Server.MapPath(
                                  "App_Data/XmlRepository/Orders.xml");
            if (!File.Exists(pathToFile))
            {
                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.LoadXml("<orders lastgeneratedid="\"0\"">");
                xmlDoc.Save(pathToFile);
            }
            return pathToFile;
        }
    }
    public override  CustomerDao  GetCustomerDao()
    {
         return new XmlCustomerDao();
    }
    public override OrderDao GetOrderDao() 
    {
        return new XmlOrderDao();
    }
}

There are only two business objects in this project: Customer and Order. So we need to have two interfaces: CustomerDao and OrderDao.

Collapse
public interface OrderDao
{
    int CreateOrder(Order order);
    bool DeleteOrder(int orderID);
    bool UpdateOrder(Order order);
    List<order> GetOrders();
}

public interface CustomerDao
{
     int CreateCustomer(Customer customer);
     bool DeleteCustomer(int customerID);
     bool UpdateCustomer(Customer customer);
     List<customer> GetCustomers();
     bool DeleteOrders(int customerID);
}

These interfaces describe the base data access functionality for all DAO implementations. Once you add a new interface method, you have to modify all DAO implementations of the interface.

The implementation of OrderDao:

Collapse
public class MSSqlOrderDao:OrderDao
{
    public int CreateOrder(Order order)
    {
        //using the Transact-SQL 
        //return the order id on success  
        ......
    }
    public bool DeleteOrder(int orderID) 
    {
       //using the Transact-SQL 
       //return the true if the order is removed, otherwise false
        .......
    }
        
    public bool UpdateOrder(Order order) 
    {
       //using the Transact-SQL 
       //return the true if the order is removed, otherwise false
        .......
    }

    public List<order> GetOrders() 
    {
       //using the Transact-SQL 
       //return the list of orders
        .......
    }
}

The implementation of XmlOrderDao is specific because it uses XML files to store the data.

The transfer objects are used by DAO to send and receive data from the data client.

Collapse
public class Customer
{
    private const int defaultIDValue = 0; 
    private int _customerID;
     private string _companyName; 
    private string _contactName; 
    private string _contactTitle; 
    private string _address; 
    private string _city;  
    private string _region; 
    private string _postalCode; 
    private string _country;  
    private string _phone; 
    private string _fax;

    //get and set properties.....
}
public class Order
{
    private const int defaultIDValue = 0; 
    private int _orderID;
    private int _customerID;
    private DateTime _orderDate;
    private Decimal  _freight;
    private    string _shipName;
    private string _shipAddress;
    private string _shipCity;
    private string _shipRegion;
    private string _shipPostalCode;
    private string _shipCountry;
    
    //get and set properties.....

It’s pretty simple to use DAO from the data client.

Collapse
[WebMethod]
public bool UpdateCustomer(int customerID ,string companyName, string contactName,
                string contactTitle, string address, string city, string region,
                string postalCode, string country, string phone, string fax)
{

    Customer customer = new Customer(customerID , companyName, contactName,
                contactTitle, address, city, region,
                 postalCode,  country, phone, fax);

    CustomerDao customerDao = DaoFactory.GetDaoFactory().GetCustomerDao();
    return customerDao.UpdateCustomer(customer);
}

The Web Service classes in this project act as business objects or data clients. As you can see, the data client hasn’t any understanding what is the data source. The DAO abstracts all manipulations to the data source.

Notes: Currently, in this code, the default DAO is the XML repository as it can work standalone. It will store the XML files locally in the App_Data folder (be sure that the ASPNET user has write permissions on your machine). If you are going to use it with MS SQL, you have to modify the web.config file. You need to comment the XMLDaoFactory code and uncomment the MSSqlDaoFactory code and also specify the connection string to your database as well to execute the SQL script that is in the SqlInstall folder. That is all what you need to migrate from one data source to another.

Download source code – 29.4 KB

Referenced by: http://www.codeproject.com/KB/webservices/Data_Access_Object.aspx

 
Leave a comment

Posted by on July 30, 2010 in C#, Web Application