Creating a Generic Store

  1. #1

    Creating a Generic Store

    After writing (more than) a few pages with Ext.Net I found that mapping datasource RecordFields in Stores was both tedious and error-prone. You always have to remember to update the RecordFields of the store when you add a new property to the datasource.

    To make things a bit better I decided to create a generic store that automatically generates the RecordFields from the properties of the datatype object (DTO).

    To make a generic store we need a generic reader. Since all my datasources generate json I base my generic reader on JsonReader. By using reflection to extract the properties of the DTO it's easy to generate the needed RecordFields.

     
        public class JsonReader<T> : JsonReader
        {
            public JsonReader()
            {
               //Read the properties from the Dto and create a corresponding RecordField
                PropertyInfo[] properties = typeof(T).GetProperties();
                this.Fields.Clear();
                foreach (PropertyInfo p in properties)
                {
                    //Ignore properties with attribute JsonIgnore
                    if (!haveJsonIgnoreAttribute(p))
                        this.Fields.Add(CreateRecordField(p));
                }
            }
     
            private bool haveJsonIgnoreAttribute(PropertyInfo p)
            {
                return p.GetCustomAttributes(typeof(Newtonsoft.Json.JsonIgnoreAttribute), true).Length > 0;
            }
     
            private RecordField CreateRecordField(PropertyInfo p)
            {
                RecordField rf = new RecordField(p.Name);
                Type type = p.PropertyType;
                if (type == typeof(string))
                    rf.SubmitEmptyValue = EmptyValue.Null;
                rf.Type = GetType(type);
                return rf;
            }
     
            private RecordFieldType GetType(Type type)
            {
                if (type == typeof(int))
                {
                    return RecordFieldType.Int;
                }
                else if (type == typeof(double))
                {
                    return RecordFieldType.Float;
                }
                else if (type == typeof(DateTime))
                {
                    return RecordFieldType.Date;
                }
                else if (type == typeof(string))
                {
                    return RecordFieldType.String;
                }
                else if (type == typeof(bool))
                {
                    return RecordFieldType.Boolean;
                }
                else
                {
                    return RecordFieldType.Auto;
                }
            }
        }
    After creating a generic JsonReader, it's very easy to create a generic store.

     
        public class JsonStore<T> : Store
        {
            public JsonStore()
            {
                this.Reader.Add(new JsonReader<T>());
            }
        }
    This is all well as long as you create the store in codebehind. But what if you want to create it directly in the aspx markup? ASP.NET has no direct way of declaring generic objects in the markup.
    The solution is to use a less known feature called ControlBuilder. A ControlBuilder is a class that governs how a server control is parsed when it is used declaratively on an ASP.NET page.

    The first step is to create a new non-generic class JsonStoreGeneric that inherits from our generic store. JsonStoreGeneric is extended with the property ObjectType that will hold the type of the generic argument (our DTO). JsonStoreGeneric is the class that we declare in the markup.

     
        [ControlBuilder(typeof(GenericControlBuilder))]
        public class JsonStoreGeneric : JsonStore<Object>
        {
            private string _objectType;
            public string ObjectType
            {
                get
                {
                    return _objectType ?? String.Empty;
                }
                set
                {
                    _objectType = value;
                }
            }
        }
    With the ControlBuilder attribute we tell ASP.NET to use the class GenericControlBuilder when generating the control. GenericControlBuilder looks at the ObjectType property and transforms JsonStoreGeneric to a JsonStore<ObjectType> behind the scenes. I found the code for GenericControlBuilder online so I can't take credit for it, but here it is in its full glory:

     
        // Custom ControlBuilder that instantiates the generic control when the page
        // is constructed.
        public class GenericControlBuilder : ControlBuilder
        {
            public override void Init(TemplateParser parser, ControlBuilder parentBuilder, Type type,
                string tagName, string id, IDictionary attribs)
            {
                Type newType = type;
                if (attribs.Contains("objecttype"))
                {
                    // If objecttype is specified, create a generic type that is bound to that
                    // argument and then hide the objecttype attribute.
                    Type genericType = type.BaseType.GetGenericTypeDefinition();
                    Type genericArg = Type.GetType((string)attribs["objecttype"], true, true);
                    newType = genericType.MakeGenericType(genericArg);
                    attribs.Remove("objecttype");
                }
                base.Init(parser, parentBuilder, newType, tagName, id, attribs);
            }
        }
    And now it's a one-liner to declare a store in markup no mather how many properties the datasource have.

     
         <ext:gridpanel id="GridPanel1" runat="server" striperows="true" title="GridPanel With Generic Store"
            trackmouseover="true" width="600" height="350" autoexpandcolumn="Name">
            <Store>
                <cc1:JsonStoreGeneric ObjectType="DataAccess.EmployeeDto,DataAccess" ID="Store1" runat="server" />
            </Store>
            <ColumnModel runat="server">
                <Columns>
                    <ext:Column ColumnID="Name" Header="Name" DataIndex="Name" />
                    <ext:Column ColumnID="Age" Header="Age" DataIndex="Age" />
                    <ext:Column ColumnID="Salary" Header="Salary" DataIndex="Salary" />
                    <ext:Column ColumnID="IsOnVacation" Header="IsOnVacation" DataIndex="IsOnVacation" />
                    <ext:DateColumn ColumnID="Birthday" Header="Birthday" DataIndex="Birthday" />
                </Columns>
            </ColumnModel>
            <SelectionModel>
                <ext:RowSelectionModel runat="server" SingleSelect="true" />
            </SelectionModel>
        </ext:gridpanel>
    You can continue to build further on this making the entire gridpanel generic. It all depends on your needs.

    See link in reply below for full solution.
  2. #2

    Link to solution

    You can find the solution here in both .zip and .7z archives:

    http://cid-d19a3a6346c64870.skydrive...5USYAfq*ozQ%24
  3. #3
    Hi Fredrik,

    Very cool. Thanks for sharing.
    Geoffrey McGill
    Founder & CEO

Similar Threads

  1. [CLOSED] Creating Combobox + Store in CodeBehind
    By trezv in forum 1.x Legacy Premium Help
    Replies: 3
    Last Post: Mar 12, 2015, 1:41 PM
  2. Problem creating Store with JsonReader
    By GLD in forum 1.x Help
    Replies: 0
    Last Post: Feb 08, 2011, 1:59 PM
  3. Creating Store from Datatable
    By QualityCode in forum 1.x Help
    Replies: 2
    Last Post: Dec 13, 2010, 5:20 AM
  4. Method to populate any store with any generic list
    By signup in forum Examples and Extras
    Replies: 5
    Last Post: Oct 17, 2009, 12:28 AM
  5. Creating a Store From CodeBehind
    By ahmetmeral in forum 1.x Help
    Replies: 5
    Last Post: Dec 14, 2008, 5:14 PM

Posting Permissions