In this answer I suggested using a fluent interface "builder" to replace all the hard-coded, repetitive and quite error-prone inline XML string concatenations.
This code might need a bit of tweaking to work perfectly with the OP's code in the question this code is following-up on, but it seems to work as expected. Thoughts?
public enum XmlQueryOperator
{
Equals,
LessThan,
LessThanOrEqual,
GreaterThan,
GreaterThanOrEqual
}
public class XmlQueryBuilder<TEntity>
where TEntity : class
{
private readonly XDocument _xDoc = new XDocument();
private readonly XElement _queryNode = new XElement("query");
public XmlQueryBuilder()
{
var rootNode = new XElement("queryxml");
rootNode.SetAttributeValue("version", "1.0");
var entityNode = new XElement("entity");
entityNode.Value = typeof (TEntity).Name;
rootNode.Add(entityNode);
_xDoc.Add(rootNode);
}
public XmlQueryBuilder<TEntity> Where<TProperty>(Expression<Func<TEntity, TProperty>> property, XmlQueryOperator operation, string value)
{
var xCondition = new XElement("condition");
var xField = new XElement("field");
xField.Value = GetPropertyName(property);
var xExpression = new XElement("expression");
xExpression.SetAttributeValue("op", operation.ToString());
xExpression.Value = value;
xField.Add(xExpression);
xCondition.Add(xField);
_queryNode.Add(xCondition);
return this;
}
public override string ToString()
{
var parent = _xDoc.Element("queryxml");
if (parent == null) throw new InvalidOperationException("root node was not created.");
parent.Add(_queryNode);
return _xDoc.ToString();
}
private string GetPropertyName<TSource, TProperty>(Expression<Func<TSource, TProperty>> propertyLambda)
{
// credits http://stackoverflow.com/questions/671968/retrieving-property-name-from-lambda-expression/672212#672212 (modified to return property name)
var type = typeof(TSource);
var member = propertyLambda.Body as MemberExpression;
if (member == null)
throw new ArgumentException(string.Format("Expression '{0}' refers to a method, not a property.", propertyLambda));
var propInfo = member.Member as PropertyInfo;
if (propInfo == null)
throw new ArgumentException(string.Format("Expression '{0}' refers to a field, not a property.", propertyLambda));
if (type != propInfo.ReflectedType && !type.IsSubclassOf(propInfo.ReflectedType))
throw new ArgumentException(string.Format("Expresion '{0}' refers to a property that is not from type {1}.", propertyLambda, type));
return propInfo.Name;
}
}
Usage - given a TestEntity
:
public class TestEntity
{
public string Test1 { get; set; }
public int Test2 { get; set; }
public DateTime Test3 { get; set; }
}
This program produces the expected output:
class Program
{
static void Main(string[] args)
{
var builder = new XmlQueryBuilder<TestEntity>()
.Where(t => t.Test1, XmlQueryOperator.Equals, "abc")
.Where(t => t.Test2, XmlQueryOperator.GreaterThan, "def")
.Where(t => t.Test3, XmlQueryOperator.LessThanOrEqual, DateTime.Now.ToString());
Console.WriteLine(builder.ToString());
Console.ReadLine();
}
}
Output:
<queryxml version="1.0">
<entity>TestEntity</entity>
<query>
<condition>
<field>Test1<expression op="Equals">abc</expression></field>
</condition>
<condition>
<field>Test2<expression op="GreaterThan">def</expression></field>
</condition>
<condition>
<field>Test3<expression op="LessThanOrEqual">2014-05-05 14:14:57</expression></field>
</condition>
</query>
</queryxml>
Where
. In LINQ,Where
means filtering, here it means something quite different. – svick yesterdaynew Query<TestEntity> { { t => t.Test1, XmlQueryOperator.Equals, "abc" }, … }
. – svick yesterdaycondition
nodes though. But I agree if it's all that's needed the fluent interface is probably overkill (although was cool to implement [first-time-I-do-this]). – Mat's Mug yesterday