none
LINQ Query From XML Sample RRS feed

  • General discussion

  •  

    I recently had the requirement to build a system which would do the following:

     

    Unpack an entity into a hashtable of values.

    Build a system to query the results of the query.

     

    I wrote a wrapper class that mimics the DataTable, and DataRows around the entity interfaces returned from the database.

     

    That was the easy part.  The hard part was being able to query over the DataRows using LINQ syntax.  I decided to create my queries as an XML representation, and wrote a query parser for the XML doc that builds an Linq Expression to query over the data rows.

     

    Here is a sample XML document representing a query:

    <Filters>

        <Or>

            <And>

                <Filter Column="Number1" Value="10" Operator="1" TypeOfValue="System.Int32">

                </Filter>

                <Filter Column="Title" Value="Write On Track" Operator="1" TypeOfValue="System.String">

                </Filter>

            </And>

            <Or>

                <Filter Column="Title" Value="Enrollment Underway" Operator="1" TypeOfValue="System.String">

                </Filter>

                <Filter Column="Category" Value="Sports" Operator="1" TypeOfValue="System.String">

                </Filter>

            </Or>

        </Or>

    </Filters>


    Here is the code I use to process the request:  The call to GetComparableValue is a helper method that unpacks an item from the hashtable of values and allows for comparisions to strongly typed values. 

    public class ChannelQueryBuilder {

            public ChannelQueryBuilder() {

            }

     

            public Expression<Func<ChannelDataRow, bool>> Build(XElement e) {

                if (e.Name == "And") {

                    var p = PredicateBuilder.True<ChannelDataRow>();

                    var fs = e.Elements("Filter");

                    foreach (var f in fs) {

                        var item = GetFilterItem(f);

                        if (item.Operator == QueryOperator.Equals) {

                            var i = item;

                            p = p.And(x => x.ComparableValueAt(i.Column).AreEqual(i.Value));

                        } else if (item.Operator == QueryOperator.GreaterThan) {

                           var i = item;

                            p = p.And(x => x.ComparableValueAt(i.Column).GreatorThan(i.Value));

                        } else if (item.Operator == QueryOperator.LessThan) {

                            var i = item;

                            p = p.And(x => x.ComparableValueAt(i.Column).LessThan(i.Value));

                        }

                    }

                    var qGroups = from q in e.Elements()

                                  where q.Name == "Or" || q.Name == "And"

                                  select q;

     

                    foreach (var o in qGroups) {

                        p = p.Or(Build(o));

                    }

                    return p;

                } else {

                    var p = PredicateBuilder.False<ChannelDataRow>();

                    var fs = e.Elements("Filter");

                    foreach (var f in fs) {

                        var item = GetFilterItem(f);

                        if (item.Operator == QueryOperator.Equals) {

                             var i = item;

                            p = p.Or(x => x.ComparableValueAt(i.Column).AreEqual(i.Value));

                        } else if (item.Operator == QueryOperator.GreaterThan) {

                             var i = item;

                            p = p.Or(x => x.ComparableValueAt(i.Column).GreatorThan(i.Value));

                        } else if (item.Operator == QueryOperator.LessThan) {

                            var i = item;

                            p = p.Or(x => x.ComparableValueAt(i.Column).LessThan(i.Value));

                        }

                    }

                    var qGroups = from q in e.Elements()

                                  where q.Name == "Or" || q.Name == "And"

                                  select q;

     

                    foreach (var o in qGroups) {

                        p = p.Or(Build(o));

                    }

                    return p;

                }

            }

     

            public IFilterItem GetFilterItem(XElement filter) {

                Type t = Type.GetType(filter.Attribute("TypeOfValue").Value, true);

                object v = null;

                if (t == typeof(int)) {

                    v = Convert.ToInt32(filter.Attribute("Value").Value);

                } else if (t == typeof(float)) {

                    v = Convert.ToSingle(filter.Attribute("Value").Value);

                } else if (t == typeof(DateTime)) {

                    v = Convert.ToDateTime(filter.Attribute("Value").Value);

                } else if (t == typeof(bool)) {

                    v = Convert.ToBoolean(filter.Attribute("Value").Value);

                } else if (t == typeof(string)) {

                    v = filter.Attribute("Value").Value;

                } else if (t == typeof(Guid)) {

                    v = new Guid(filter.Attribute("Value").Value);

                }

     

                return new ChannelQueryFilterItem() {

                    Column = filter.Attribute("Column").Value,

                    Value = v,

                    Operator = (QueryOperator)Convert.ToInt32(filter.Attribute("Operator").Value)

                };

            }

        }

    The expression that gets returned can be used to query the "DataRows" of the "DataTable" that I unpack the results of the orginal query into.   This allows me to have a view of the data where I don't care what type / where the actual data is comming from, just that it get's packed into a hashtable representation of the data, where I can query using the above method. I use this to populate information from all types of sources, xml web serivces, LINQ entities, POCO collections, etc.  I can then use this interface to query that data in a uniformed way.



    What do you think?


    -Lucas Stark

    Sunday, December 21, 2008 12:41 AM