Skip to content

Instantly share code, notes, and snippets.

@sgoguen
Created March 5, 2009 17:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sgoguen/74441 to your computer and use it in GitHub Desktop.
Save sgoguen/74441 to your computer and use it in GitHub Desktop.
// Copy and Paste this code into LINQPad and use the Nutshell database
void Main()
{
var text = @"$Purchase.Customer.Name purchased $Detail on $Purchase.Date for $$$Purchase.Price";
var Hello = new ExpressionTemplate<PurchaseItem>(text);
var query = PurchaseItems.Select(Hello.ToExpr());
query.ToString().Dump("SQL");
}
class ExpressionTemplate<T> {
private string _source;
public ExpressionTemplate(string source) {
_source = source;
}
public Expression<Func<T,string>> ToExpr() {
var pattern = new Regex("(?<text>\\$\\$)|\\$(?<field>[\\w.]+)|(?<text>[^$]*)");
var parameter = Expression.Parameter(typeof(T), "p");
var matches = from m in pattern.Matches(_source).Cast<Match>()
let field = m.Groups["field"].Value
let raw_text = m.Groups["text"].Value
let text = (raw_text == "$$") ? "$" : raw_text
where field.Length > 0 || text.Length > 0
let expr = (field.Length > 0) ? GetProperties(parameter, field) : Expression.Constant(text)
select new { field, text, expr };
Expression joined = matches.FirstOrDefault().expr;
foreach(var match in matches.Skip(1)) {
var concat = typeof(string).GetMethod("Concat", new Type[] { typeof(Object), typeof(Object) });
joined = Expression.Add(joined, match.expr, concat);
}
return Expression.Lambda<Func<T, string>>(joined, new ParameterExpression[] { parameter });
}
public Expression GetProperties(Expression Param, string Name)
{
var TypeInfo = Param.Type;
try {
// Create the accessor function
var PropertyNames = Name.Split('.');
Expression Input = Param;
var Accessors = from PropertyName in PropertyNames
select new {
PropertyName,
Accessor = (Input = Expression.PropertyOrField(Input, PropertyName))
};
var RAccessors = Accessors.ToArray().Reverse().ToArray();
var DefaultValue = Expression.Constant("");
Expression Last = null;
foreach (var Item in RAccessors) {
if (Last == null) {
Last = MemberExpToString(Item.Accessor, DefaultValue);
}
else {
Last = Expression.Condition(IsNotNothing(Item.Accessor), Last, DefaultValue);
}
}
return Expression.Condition(IsNotNothing(Param), Last, DefaultValue);
}
catch (Exception ex) {
throw new Exception(string.Format("Error compiling: {0} for type {1}", Name, TypeInfo.Name), ex);
}
}
public Expression MemberExpToString(Expression PropGet, Expression DefaultValue)
{
var TestNull = IsNotNothing(PropGet);
var t = PropGet.Type;
if (object.ReferenceEquals(t, typeof(string))) {
return Expression.Condition(IsNotNothing(PropGet), PropGet, DefaultValue);
}
else {
var ToStringMethod = PropGet.Type.GetMethod("ToString", new Type[] { });
var ConvGet = Expression.Call(PropGet, ToStringMethod, new Expression[] { });
return Expression.Condition(IsNotNothing(PropGet), ConvGet, DefaultValue);
}
}
public Expression GetProperty(Expression e, string field)
{
var DefaultValue = Expression.Constant("");
var PropGet = Expression.PropertyOrField(e, field);
var TestNull = IsNotNothing(PropGet);
var t = PropertyOrFieldType(e, field);
if (object.ReferenceEquals(t, typeof(string))) {
return Expression.Condition(IsNotNothing(PropGet), PropGet, DefaultValue);
}
else {
var ToStringMethod = PropGet.Type.GetMethod("ToString", new Type[] { });
var ConvGet = Expression.Call(PropGet, ToStringMethod, new Expression[] { });
return Expression.Condition(IsNotNothing(PropGet), ConvGet, DefaultValue);
}
}
private BinaryExpression IsNotNothing(Expression e) {
if(e.Type.IsPrimitive) {
return Expression.Equal(Expression.Constant(true), Expression.Constant(true));
} else {
return Expression.NotEqual(Expression.Convert(e, typeof(Object)), Expression.Constant(null, typeof(Object)));
}
}
public Type PropertyOrFieldType(Expression expression, string propertyOrFieldName)
{
var p = expression.Type.GetProperty(propertyOrFieldName, (BindingFlags.FlattenHierarchy | (BindingFlags.Public | (BindingFlags.Instance | BindingFlags.IgnoreCase))));
if (p != null) {
return p.PropertyType;
}
var f = expression.Type.GetField(propertyOrFieldName, (BindingFlags.FlattenHierarchy | (BindingFlags.Public | (BindingFlags.Instance | BindingFlags.IgnoreCase))));
if (f == null) {
p = expression.Type.GetProperty(propertyOrFieldName, (BindingFlags.FlattenHierarchy | (BindingFlags.NonPublic | (BindingFlags.Instance | BindingFlags.IgnoreCase))));
if (p != null) {
return p.PropertyType;
}
f = expression.Type.GetField(propertyOrFieldName, (BindingFlags.FlattenHierarchy | (BindingFlags.NonPublic | (BindingFlags.Instance | BindingFlags.IgnoreCase))));
}
if (f != null) return f.FieldType;
return null;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment