08 May, 2008 12:56
Linq dynamic querying
Posted by mimo under [ C# .net development , Workflow ][ (0) Comment ] | [ (0) Trackbacks ]
As part of the workflow service project, I need to build an XML querying framework.
For this to work on the back end, I would like to use Linq - but in order to do so, I need to have it be capable of handling dynamically generated queries derived from the query XML coming into the service.
It appears that Microsoft has produced samples of this - Dynamic Query.
There are a number of good examples around:
ScottGu's dynamic query example:
Blog post that includes a join example:
The issue though with Dynamic Linq is that you appear to trade the strong typing associated with standard Linq for the flexiblity of Dynamic query.
A query executed with Dynamic Linq returns as a type of 'DynamicClass1' etc. which cannot be cast to other types. This means that a generic mapping process of some kind would have to be used to get the data back into some kind of strongly typed form.
In the case of what I am working on, I don't need the strong typing. I am working on setting up a service entry point for generic querying of data associated with the service in question. The query language coming in will be constructed in XML, and defined using a typical XML query pattern.
These queries will be translated to dynamic linq, which is then executed and mapped back to XML from the generic dynamic class generated.
The incoming XML query would look like:
<Query><MatrixEquipmentItem><MatrixEquipmentPackageId op='eq'>P*GWM</MatrixEquipmentPackageId></MatrixEquipmentItem></Query>
This gets translated into Dynamic Linq - since I know the type coming in based on the method being called, reflection can be used on the object to build a query string. If the property attribute on the XML query coming in is not set, it can be assumed that all properties on the object in question are desired for return.
The MatrixEquipmentItem Linq class definition is:
#region (Class) MatrixEquipmentItem/// <summary>
/// Linq class declaring the structure of one row of the Equipment item table
/// </summary>
[Table(Name = "MatrixEquipmentItems")]
public class MatrixEquipmentItem
{
[Column(IsPrimaryKey = true, Name = "Id")]
public Guid Id;
[Column(IsPrimaryKey = false, Name = "Name")]
public string Name;
[Column(IsPrimaryKey = false, Name = "MeasureCode")]
public string MeasureCode;
[Column(IsPrimaryKey = false, Name = "MeasureName")]
public string MeasureName;
[Column(IsPrimaryKey = false, Name = "IsConsumable")]
public bool IsConsumable;
[Column(IsPrimaryKey = false, Name = "MatrixEquipmentPackageId")]
public string MatrixEquipmentPackageId;
}
#endregion
Reflecting this and combining it with the incoming query to build the dynamic linq statement results in:
var equipmentItems = this.EquipmentItemsTable.Where("MatrixEquipmentPackageId = P*GWM").OrderBy("Name").Select("new(Id, Name, MeasureCode, MeasureName, IsConsumable,MatrixEquipmentPackageId)"
To get this translated back to result XML proved a bit tricky. Since the Dyanmic Linq library loses strong types, and instead returns dynamicClass defined 'objects', it is necessary to reverse reflect using the MatrixEquipmentItem class as a base to serialize the result back to XML.
The code I came up with to do this is probably brute force - but works for now until I get a better handle on how to do this...Note that I am cheating at this point by hard-coding the packageId instead of the dynamic query string, as I had to fix one set of the equation to figure out how to get the results back dynamically (rather than having to deal with double-dynamic query build/response)..
public void SelectItems(string packageId, XmlDocument payload)
{
List<MatrixEquipmentItem> result = new List<MatrixEquipmentItem>();
var equipmentItems = this.EquipmentItemsTable.Where("MatrixEquipmentPackageId = @0", packageId).OrderBy("Name").Select("new(Id, Name, MeasureCode, MeasureName, IsConsumable,MatrixEquipmentPackageId)");
MatrixEquipmentItem myItem = new MatrixEquipmentItem();
Type matrixEquipmentType = myItem.GetType();
FieldInfo[] equipmentProperties = matrixEquipmentType.GetFields();
XmlNode equipmentNode = payload.SelectSingleNode("MatrixEquipment");
for (int i = 0; i < equipmentItems.Count(); i++)
{
foreach (FieldInfo property in equipmentProperties)
{
XmlElement equipmentItemElement = payload.CreateElement("MatrixEquipmentItem");
if (property.Name != "Id")
{
var itemValue = item.Select("it." + property.Name).Cast<object>().Single<object>();
Util.CreateXmlElement(payload, equipmentItemElement, property.Name, itemValue.ToString());
}
else
{
var itemValue = item.Select("it." + property.Name).Cast<object>().Single<object>();
equipmentItemElement.SetAttribute("id", itemValue.ToString());
}
equipmentNode.AppendChild(equipmentItemElement);
}
payload.AppendChild(equipmentNode);
}
}
#endregion
This creates the following XML response:
<MatrixEquipment>
<MatrixEquipmentItem id="985a7795-f9cd-4a31-8d2c-dd16ce2e63ff"/>
<MatrixEquipmentItem>
<Name>EM31</Name>
</MatrixEquipmentItem>
<MatrixEquipmentItem>
<MeasureCode>Day</MeasureCode>
</MatrixEquipmentItem>
<MatrixEquipmentItem>
<MeasureName>Day</MeasureName>
</MatrixEquipmentItem>
<MatrixEquipmentItem>
<IsConsumable>False</IsConsumable>
</MatrixEquipmentItem>
<MatrixEquipmentItem>
<MatrixEquipmentPackageId>P*GWM</MatrixEquipmentPackageId>
</MatrixEquipmentItem>
<MatrixEquipmentItem id="985a7795-f9cd-4a31-8d2c-dd16ce2e63ff"/>
<MatrixEquipmentItem>
<Name>EM31</Name>
</MatrixEquipmentItem>
<MatrixEquipmentItem>
<MeasureCode>Day</MeasureCode>
</MatrixEquipmentItem>
<MatrixEquipmentItem>
<MeasureName>Day</MeasureName>
</MatrixEquipmentItem>
<MatrixEquipmentItem>
<IsConsumable>False</IsConsumable>
</MatrixEquipmentItem>
<MatrixEquipmentItem>
<MatrixEquipmentPackageId>P*GWM</MatrixEquipmentPackageId>
</MatrixEquipmentItem>
</MatrixEquipment>
I suppose an easier way of doing this might be to simply pass the XML straight down to the database, but I do like the idea of keeping everything 'Linq' as it (in theory) isolates from database dependency.
This pattern if it works, should allow the system to be very flexible and reletively easy to maintain.




