Convert anonymous object to expression delegate - c#

I have an third party library that has the following API:
Update<TReport>(object updateOnly, Expression<Func<TReport,bool>> where)
What I want to do is call this method but using anonymous objects such as :
Update(new {Name = "test"}, new {Id = id})
Is it possible to take the second anonymous object and convert it to something like :
x => x.Id == id.
So what I want is to convert the new {Id = id} to a function that takes TReport and returns bool?

Even if I agree with Daniel A. White on the fact that it's complicating things, I tried a bit.
But it's not safe, cause you're losing strong typing. (You can put whatever you want in an anonymous object : it's not linked to the object's "real" properties... So no refactoring, no check...)
It's not really tested, so not sure if that's what you want. You can have (if it works) different objects in the "predicate object" :
new {Name="test"}, new{Id=1, Name="test2"})
So, you could have something like that :
public static class MyHelpers
{
public static Expression<Func<TReport, bool>> CreatePredicate<TReport>(this object predicateObject)
{
var parameterExpression = Expression.Parameter(typeof(TReport), "item");
Expression memberExpression = parameterExpression;
var objectDictionary = MakeDictionary(predicateObject);
foreach (var entry in objectDictionary.Where(entry => typeof(TReport).GetProperty(entry.Key) == null))
{
throw new ArgumentException(string.Format("Type {0} has no property {1}", typeof(TReport).Name, entry.Key));
}
var equalityExpressions = GetBinaryExpressions(objectDictionary, memberExpression).ToList();
var body = equalityExpressions.First();
body = equalityExpressions.Skip(1).Aggregate(body, Expression.And);
return Expression.Lambda<Func<TReport, bool>>(body, new[] { parameterExpression });
}
private static IDictionary<string, object> MakeDictionary(object withProperties)
{
var properties = TypeDescriptor.GetProperties(withProperties);
return properties.Cast<PropertyDescriptor>().ToDictionary(property => property.Name, property => property.GetValue(withProperties));
}
private static IEnumerable<BinaryExpression> GetBinaryExpressions(IDictionary<string, object> dic, Expression expression)
{
return dic.Select(m => Expression.Equal(Expression.Property(expression, m.Key), Expression.Constant(m.Value)));
}
}
usage, for example
public void Update<TReport>(object updateOnly, object predicateObject) {
var predicate = predicateObject.CreatePredicate<TReport>();
yourGenericApi.Update(updateOnly, predicate);
}
EDIT :
As you're losing strong typing security, you should add something like
foreach (var entry in objectDictionary.Where(entry => typeof(TReport).GetProperty(entry.Key) == null))
{
throw new ArgumentException(string.Format("Type {0} has no property {1}", typeof(TReport).Name, entry.Key));
}
after
var objectDictionary = MakeDictionary(predicateObject);

If you have a specific value that you want the function to return, I think you can simply do this:
bool desiredResult = true;
Update(new { Name = "test" }, x => desiredResult);

Related

Variable 'x.Sub' of type 'SubType' referenced from scope '' but it is not defined error

Check this fiddle for the error: https://dotnetfiddle.net/tlz4Qg
I have two classes like this:
public class ParentType{
private ParentType(){}
public int Id { get; protected set; }
public SubType Sub { get; protected set; }
}
public class SubType{
private SubType(){}
public int Id { get; protected set; }
}
I am going to transform a multilevel anonymous expression to a multilevel non-anonymous expression. To achieve this I have an expression like the below-mentioned one:
x => new
{
x.Id,
Sub = new
{
x.Sub.Id
}
}
To achieve that goal, I have transformed it to an expression like this:
x => new ParentType()
{
Id = x.Id,
Sub = new SubType()
{
Id = x.Sub.Id
},
}
But when I call Compile() method, I get the following error:
Variable 'x.Sub' of type 'SubType' referenced from scope '' but it is not defined
Here is my visitor class:
public class ReturnTypeVisitor<TIn, TOut> : ExpressionVisitor
{
private readonly Type funcToReplace;
private ParameterExpression currentParameter;
private ParameterExpression defaultParameter;
private Type currentType;
public ReturnTypeVisitor() => funcToReplace = typeof(Func<,>).MakeGenericType(typeof(TIn), typeof(object));
protected override Expression VisitNew(NewExpression node)
{
if (!node.Type.IsAnonymousType())
return base.VisitNew(node);
if (currentType == null)
currentType = typeof(TOut);
var ctor = currentType.GetPrivateConstructor();
if (ctor == null)
return base.VisitNew(node);
NewExpression expr = Expression.New(ctor);
IEnumerable<MemberBinding> bindings = node.Members.Select(x =>
{
var mi = currentType.GetProperty(x.Name);
//if the type is anonymous then I need to transform its body
if (((PropertyInfo)x).PropertyType.IsAnonymousType())
{
//This section is became unnecessary complex!
//
var property = (PropertyInfo)x;
var parentType = currentType;
var parentParameter = currentParameter;
currentType = currentType.GetProperty(property.Name).PropertyType;
currentParameter = Expression.Parameter(currentType, currentParameter.Name + "." + property.Name);
//I pass the inner anonymous expression to VisitNew and make the non-anonymous expression from it
var xOriginal = VisitNew(node.Arguments.FirstOrDefault(a => a.Type == property.PropertyType) as NewExpression);
currentType = parentType;
currentParameter = parentParameter;
return (MemberBinding)Expression.Bind(mi, xOriginal);
}
else//if type is not anonymous then simple find the property and make the memberbinding
{
var xOriginal = Expression.PropertyOrField(currentParameter, x.Name);
return (MemberBinding)Expression.Bind(mi, xOriginal);
}
});
return Expression.MemberInit(expr, bindings);
}
protected override Expression VisitLambda<T>(Expression<T> node)
{
if (typeof(T) != funcToReplace)
return base.VisitLambda(node);
defaultParameter = node.Parameters.First();
currentParameter = defaultParameter;
var body = Visit(node.Body);
return Expression.Lambda<Func<TIn, TOut>>(body, currentParameter);
}
}
And use it like this:
public static Expression<Func<Tin, Tout>> Transform<Tin, Tout>(this Expression<Func<Tin, object>> source)
{
var visitor = new ReturnTypeVisitor<Tin, Tout>();
var result = (Expression<Func<Tin, Tout>>)visitor.Visit(source);
return result;// result.Compile() throw the aforementioned error
}
Here is the extension methods used inside my Visitor class:
public static ConstructorInfo GetPrivateConstructor(this Type type) =>
type.GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
// this hack taken from https://stackoverflow.com/a/2483054/4685428
// and https://stackoverflow.com/a/1650895/4685428
public static bool IsAnonymousType(this Type type)
{
var markedWithAttribute = type.GetCustomAttributes(typeof(CompilerGeneratedAttribute), inherit: false).Any();
var typeName = type.Name;
return markedWithAttribute
&& (typeName.StartsWith("<>") || type.Name.StartsWith("VB$"))
&& typeName.Contains("AnonymousType");
}
Update
Here is the .Net Fiddle link for the problem: https://dotnetfiddle.net/tlz4Qg
Update
I have removed the extra codes that seems to be out of the problem scope.
The cause of the problem in question is the line
currentParameter = Expression.Parameter(currentType, currentParameter.Name + "." + property.Name);
inside VisitNew method.
With your sample, it creates a new parameter called "x.Sub", so if we mark the parameters with {}, the actual result is
Sub = new SubType()
{
Id = {x.Sub}.Id
},
rather than expected
Sub = new SubType()
{
Id = {x}.Sub.Id
},
In general you should not create new ParameterExpressions except when remapping lambda expressions. And all newly created parameters should be passed to Expression.Lambda call, otherwise they will be considered "not defined".
Also please note that the visitor code has some assumptions which doesn't hold in general. For instance
var xOriginal = Expression.PropertyOrField(currentParameter, x.Name);
won't work inside nested new, because there you need access to a member of the x parameter like x.Sub.Id rather than x.Id. Which is basically the corersonding expression from NewExpression.Arguments.
Processing nested lambda expressions or collection type members and LINQ methods with expression visitors requires much more state control. While converting simple nested anonymous new expression like in the sample does not even need a ExpressionVisitor, because it could easily be achieved with simple recursive method like this:
public static Expression<Func<Tin, Tout>> Transform<Tin, Tout>(this Expression<Func<Tin, object>> source)
{
return Expression.Lambda<Func<Tin, Tout>>(
Transform(source.Body, typeof(Tout)),
source.Parameters);
}
static Expression Transform(Expression source, Type type)
{
if (source.Type != type && source is NewExpression newExpr && newExpr.Members.Count > 0)
{
return Expression.MemberInit(Expression.New(type), newExpr.Members
.Select(m => type.GetProperty(m.Name))
.Zip(newExpr.Arguments, (m, e) => Expression.Bind(m, Transform(e, m.PropertyType))));
}
return source;
}

Get custom object from lambda expression

I have this method:
public static SiteSettingEntity
GetSettings<SiteSettingEntity>(string siteId,
Expression<Func<SiteSettingEntity, object>> properties)
{
// This method returns a SiteSettingEntity
// filled with the values selected in my expression
}
If want to invoke:
var _siteSetting = SiteSettingService.GetSettings<SiteSettingEntity>(SiteID,
s => new { s.BillCycleType, s.InvoiceComment,
s.AllowInvoiceDetailApproval, s.JobMinimumHours });
So this returns an object with the properties filled that was selected by my lambda expression.
My question is: how I do to return a custom object dynamically same using my expression?
Taking an example: when if you want to use .Select(s => new{ filed1 = data.FieldX}), you can just use the property .filed1 in return object.
Thanks!
If you are trying to return a dynamic object you can do something like this simple example of using ExpandoObject:
public class DuckFactory
{
public ExpandoObject GetDuck()
{
dynamic duck = new ExpandoObject();
duck.Name = "Fauntleroy";
return duck;
}
}
And then call it like:
dynamic duck = new DuckFactory().GetDuck();
// Check the property exists before using it
if (((IDictionary<string, Object>)duck).ContainsKey("Name"))
{
Console.WriteLine(duck.Name); // Prints Fauntleroy
}
else
{
Console.WriteLine("Poor duck doesn't have a name.");
}
Just remember you won't get the benefits of the object being strongly typed.
This is my original method:
public static T GetSettings<T>(string siteId, Expression<Func<SiteSettingEntity, object>> properties)
{
string query = $"SELECT TOP 1 {DbTool.GetSqlFields(properties)} FROM {SiteSettingEntity.TABLE_NAME} (NOLOCK) WHERE Sites.SiteID = #SiteID";
var parameters = new Dictionary<string, object>
{
{"SiteID", siteId},
};
var _data = DbTool.SqlExec<T>(PowerDetailContext.GetConnectionString(siteId), CommandType.Text, query, parameters);
return _data != null ? _data.FirstOrDefault() : default(T);
}
Ang the when I made the call I have this:
var _siteSetting = SiteSettingService.GetSettings<SiteSettingEntity>(SiteID, s => new { s.BillCycleType, s.InvoiceComment, s.AllowInvoiceDetailApproval, s.JobMinimumHours });
the var _siteSetting I want just have the properties SELECTED between new{} when I called. If I try use _siteSetting.OtherProperty, the "OtherProperty" won't available in the code.
After reading it again I suppose you are just trying to return SiteSettingEntity from GetSettings method.
public static SiteSettingEntity GetSettings<T>(string siteId, Expression<Func<SiteSettingEntity, object>> properties)
{
string query = $"SELECT TOP 1 {DbTool.GetSqlFields(properties)} FROM {SiteSettingEntity.TABLE_NAME} (NOLOCK) WHERE Sites.SiteID = #SiteID";
var parameters = new Dictionary<string, object>
{
{"SiteID", siteId},
};
var _data = DbTool.SqlExec<SiteSettingEntity>(PowerDetailContext.GetConnectionString(siteId), CommandType.Text, query, parameters);
return _data != null ? _data.FirstOrDefault() : default(SiteSettingEntity);
}

How to get arguments from an Expression where TDelegate is a callback

I'm attempting to write a simple generic cache but running into problems with generating unique enough keys with using System.Func as a callback.
What I ideally want is to be able to pass in an invocable delegate of some description so that the cache itself can get the value, and determine a key all from the same expression. Right now I'm getting exceptions because I'm not passing in an argument that implements or inherits from MethodCallExpression. What should I be using instead of a System.Func for this intended behaviour?
public class SimpleCacheKeyGenerator : ICacheKey
{
public string GetCacheKey<T>(Expression<Func<T>> action)
{
var body = (MethodCallExpression) action.Body; //!!! Exception Raised - action.Body is FieldExpression
ICollection<object> parameters = (from MemberExpression expression in body.Arguments
select
((FieldInfo) expression.Member).GetValue(
((ConstantExpression) expression.Expression).Value)).ToList();
var sb = new StringBuilder(100);
sb.Append(body.Type.Namespace);
sb.Append("-");
sb.Append(body.Method.Name);
parameters.ToList().ForEach(x =>
{
sb.Append("-");
sb.Append(x);
});
return sb.ToString();
}
}
public class InMemoryCache : ICacheService
{
private readonly ICachePolicy _cachePolicy;
private readonly ICacheKey _cacheKey;
public InMemoryCache(ICachePolicy cachePolicy, ICacheKey cacheKey)
{
_cachePolicy = cachePolicy;
_cacheKey = cacheKey;
}
public T Get<T>(Func<T> getItemCallback) where T : class
{
var cacheID = _cacheKey.GetCacheKey(() => getItemCallback);
var item = HttpRuntime.Cache.Get(cacheID) as T;
if (item == null)
{
item = getItemCallback();
if (_cachePolicy.RenewLeaseOnAccess)
{
HttpContext.Current.Cache.Insert(cacheID, getItemCallback, null, System.Web.Caching.Cache.NoAbsoluteExpiration, _cachePolicy.ExpiresAfter);
}
else
{
HttpContext.Current.Cache.Insert(cacheID, getItemCallback, null, DateTime.UtcNow + _cachePolicy.ExpiresAfter, System.Web.Caching.Cache.NoSlidingExpiration);
}
}
return item;
}
}
The problem is, you can't easily use both the Expression> and Func representing the same thing without duplicating the code.
You could possibly convert Expression> to a Func with LambdaExpression>.Compile() method, but that could create a performance problem, since Compile actually uses assembly emit, which is quite expensive.
Here is how i would implement the same thing without using Expressions and compilation.
You can find the same pattern everywhere in the standard Linq extensions.
Pass your argument as a separate object.
The type you use as an argument will be used for type inference for the delegate, and the argument itself will provide the arguments for the delegate at the same type.
Note that the cache in this implementation works because of the default ToString implementation of the anonimous objects used as arguments.
void Main()
{
var computeCount = 0;
var item1 = GetCached(new{x = 1, y = 2}, (arg)=>{computeCount++; return arg.x + arg.y;});
Console.WriteLine(item1);
var item2 = GetCached(new{x = 1, y = 2}, (arg)=>{computeCount++; return arg.x + arg.y;});
Console.WriteLine(item2);
var item3 = GetCached(new{x = 1, y = 3}, (arg)=>{computeCount++; return arg.x + arg.y;});
Console.WriteLine(item3);
Console.WriteLine("Compute count:");
Console.WriteLine(computeCount);
}
Dictionary<string, object> _cache = new Dictionary<string, object>();
E GetCached<T, E>(T arg, Func<T,E> getter)
{
// Creating the cache key.
// Assuming T implements ToString correctly for cache to work.
var cacheKey = arg.ToString();
object result;
if (!_cache.TryGetValue(cacheKey, out result))
{
var newItem = getter(arg);
_cache.Add(cacheKey, newItem);
return newItem;
}
else
{
Console.WriteLine("Cache hit: {0}", cacheKey);
}
return (E)result;
}
Console output:
3
Cache hit: { x = 1, y = 2 }
3
4
Compute count:
2
You get this exception because (() => getItemCallback) means (() => { return getItemCallback; })
That's why action.Body is not a method call, it is the return statement. If you change your code to (() => getItemCallback()) you should not have the error. But you won't have any arguments.
To obtain arguments of the base call, you will have to change your code to accept an Expression and Compile your lambda.
public T Get<T>(Expression<Func<T>> getItemCallbackExpression) where T : class
{
var cacheID = _cacheKey.GetCacheKey(getItemCallbackExpression);
var item = HttpRuntime.Cache.Get(cacheID) as T;
if (item == null)
{
item = getItemCallback.Compile()();
if (_cachePolicy.RenewLeaseOnAccess)
{
HttpContext.Current.Cache.Insert(cacheID, getItemCallback, null, System.Web.Caching.Cache.NoAbsoluteExpiration, _cachePolicy.ExpiresAfter);
}
else
{
HttpContext.Current.Cache.Insert(cacheID, getItemCallback, null, DateTime.UtcNow + _cachePolicy.ExpiresAfter, System.Web.Caching.Cache.NoSlidingExpiration);
}
}
return item;
}
I won't recommend this approach because compiling an expression takes time.
It may be easier and more performant to manually generate cache keys. If you really want to automatically manage cache keys. You may have a look to Aspect Oriented Programmation using castle.Core or PostSharp. Theses tools will allow you to automatically add code to some of your methods and automatically add cache logic.
I modified the code as below, I got the expected result this way, so you can try this, I hope this would be helpful.
public class SimpleCacheKeyGenerator
{
public string GetCacheKey<T, TObject>(Expression<Func<T, TObject>> action)
{
var body = (MethodCallExpression) action.Body;
ICollection<object> parameters = body.Arguments.Select(x => ((ConstantExpression) x).Value).ToList();
var sb = new StringBuilder(100);
sb.Append(body.Type.Namespace);
sb.Append("-");
sb.Append(body.Method.Name);
parameters.ToList().ForEach(x =>
{
sb.Append("-");
sb.Append(x);
});
return sb.ToString();
}
}
public class InMemoryCache
{
public void Get<T, TObject>(Expression<Func<T, TObject>> getItemCallback)
{
var generator = new SimpleCacheKeyGenerator();
Console.WriteLine(generator.GetCacheKey(getItemCallback));
}
}
main:
private static void Main(string[] args)
{
var cache = new InMemoryCache();
var tt = new SomeContextImpl();
cache.Get<SomeContextImpl, string>(x => x.Any("hello", "hi"));
Console.ReadKey();
}
somcontextimpl:
public class SomeContextImpl
{
public string Any(string parameter1, string parameter2) { return ""; }
}

Get the parameter value from a Linq Expression

I have the following class
public class MyClass
{
public bool Delete(Product product)
{
// some code.
}
}
Now I have a helper class that looks like this
public class Helper<T, TResult>
{
public Type Type;
public string Method;
public Type[] ArgTypes;
public object[] ArgValues;
public Helper(Expression<Func<T, TResult>> expression)
{
var body = (System.Linq.Expressions.MethodCallExpression)expression.Body;
this.Type = typeof(T);
this.Method = body.Method.Name;
this.ArgTypes = body.Arguments.Select(x => x.Type).ToArray();
this.ArgValues = ???
}
}
The idea ist to use this code from somewhere:
// I am returning a helper somewhere
public Helper<T> GetMethod<T>()
{
var product = GetProduct(1);
return new Helper<MyClass>(x => x.Delete(product));
}
// some other class decides, when to execute the helper
// Invoker already exists and is responsible for executing the method
// that is the main reason I don't just comile and execute my Expression
public bool ExecuteMethod<T>(Helper<T> helper)
{
var instance = new MyClass();
var Invoker = new Invoker(helper.Type, helper.Method, helper.ArgTypes, helper.ArgValues);
return (bool)Invoker.Invoke(instance);
}
The point where I am stuck is how to extract the arguments from the expression itself.
I found this way
((ConstantExpression)((MemberExpression)body.Arguments[0]).Expression).Value
which seems to be an object type with a field "product" but I believe there must be a simpler solution.
Any suggestions.
Update
Just to clarify, I modified my code according to what I want to achive. In my real word application I already have a class that does the same but without an expression tree:
var helper = new Helper(typeof(MyClass), "Delete",
new Type[] { typeof(Product) }, new object[] {product}));
The main reason for my Helper<T> is to have Compile-Time checking if the method signature is valid.
Update 2
This is my current implementation, is there a better way to acces the values, without using reflection?
public Helper(Expression<Func<T, TResult>> expression)
{
var body = (System.Linq.Expressions.MethodCallExpression)expression.Body;
this.Type = typeof(T);
this.Method = body.Method.Name;
this.ArgTypes = body.Arguments.Select(x => x.Type).ToArray();
var values = new List<object>();
foreach(var arg in body.Arguments)
{
values.Add(
(((ConstantExpression)exp.Expression).Value).GetType()
.GetField(exp.Member.Name)
.GetValue(((ConstantExpression)exp.Expression).Value);
);
}
this.ArgValues = values.ToArray();
}
This method works pretty well. It returns the argument types and values for an Expression>
private static KeyValuePair<Type, object>[] ResolveArgs<T>(Expression<Func<T, object>> expression)
{
var body = (System.Linq.Expressions.MethodCallExpression)expression.Body;
var values = new List<KeyValuePair<Type, object>>();
foreach (var argument in body.Arguments)
{
var exp = ResolveMemberExpression(argument);
var type = argument.Type;
var value = GetValue(exp);
values.Add(new KeyValuePair<Type, object>(type, value));
}
return values.ToArray();
}
public static MemberExpression ResolveMemberExpression(Expression expression)
{
if (expression is MemberExpression)
{
return (MemberExpression)expression;
}
else if (expression is UnaryExpression)
{
// if casting is involved, Expression is not x => x.FieldName but x => Convert(x.Fieldname)
return (MemberExpression)((UnaryExpression)expression).Operand;
}
else
{
throw new NotSupportedException(expression.ToString());
}
}
private static object GetValue(MemberExpression exp)
{
// expression is ConstantExpression or FieldExpression
if (exp.Expression is ConstantExpression)
{
return (((ConstantExpression)exp.Expression).Value)
.GetType()
.GetField(exp.Member.Name)
.GetValue(((ConstantExpression)exp.Expression).Value);
}
else if (exp.Expression is MemberExpression)
{
return GetValue((MemberExpression)exp.Expression);
}
else
{
throw new NotImplementedException();
}
}
You can compile the argument expression and then invoke it to calculate the value:
var values = new List<object>();
foreach(var arg in body.Arguments)
{
var value = Expression.Lambda(argument).Compile().DynamicInvoke();
values.Add(value);
}
this.ArgValues = values.ToArray();
Here is an example of creation of a delegate using a lambda. The object instance is encapsulated into the delegate using a C# feature called closure.
MyClass instance = new MyClass();
//This following line cannot be changed to var declaration
//since C# can't infer the type.
Func<Product, bool> deleteDelegate = p => instance.Delete(p);
Product product = new Product();
bool deleted = deleteDelegate(product);
Alternatively you are trying to create a Helper that automagically Currys.
public class Helper<T>
where T : new()
{
public TResult Execute<TResult>(Func<T, TResult> methodLambda)
{
var instance = new T();
return methodLamda(instance);
}
}
public void Main()
{
var helper = new Helper<MyClass>();
var product = new Product();
helper.Execute(x => x.Delete(product));
}
However I have to say this problem looks suspiciously like the creation of a Helper class to handle the lifetime of a WCF proxy....You know...just say...in which case this ISN'T how I would approach this...simply because this approach leaks WCF specific code into your domain.

Can I make the following IQueryable linq statment generic

Is there a way I can make the following db query builder generic?
private IQueryable<Foo> ByName(IQueryable<Foo> dbQuery, Query query)
{
string[] searchTerms = query.Data.Replace(" ","").ToLower().Split(',');
if (query.Exclude)
{
return dbQuery.Where(x => searchTerms.All(
y => y != x.Name.Replace(" ", "").ToLower()));
}
return dbQuery.Where(x => searchTerms.Any(
y => y == x.Name.Replace(" ", "").ToLower()));
}
I've got the same function for lots of different properties of Foo. ByCounty, ByTown, ByStreet etc etc.
I've written some functions that return linq before like the following
public Expression<Func<Foo, bool>> FoosAreWithinDistanceFromGeocode(
double distance, Geocode geocode)
{
double distanceSquare = distance * distance;
return foo => ( SqlFunctions.Square((double)(
foo.Address.Geocode.Easting - geocode.Easting)) +
SqlFunctions.Square((double)(fooAddress.Geocode.Northing -
geocode.Northing)) ) <= distanceSquare;
}
But I can't seem to find if the Linq-to-SQL stuff can't use generics or if it's possible to pass properties as generics and that kind of thing.
EDIT: I have this working generically for a single search term.
Where [query.Data == "Foo1"]
return dbQuery.Where(SearchMatch("Name", query.Data));
public Expression<Func<Foo, bool>> SearchMatch(string propertyName, string searchTerm)
{
var foo = Expression.Parameter(typeof(Foo), "foo");
var prop = Expression.Property(foo, propertyName);
var search = Expression.Constant(searchTerm);
var equal = Expression.Equal(prop, search);
return Expression.Lambda<Func<Foo, bool>>(equal, foo);
}
Anyone have any ideas how to make it work for an array of strings?
You need to define an interface that exposes the properties that you want to access, like so:
public interface IHaveName
{
string Name { get; }
}
Then, on your classes, you would implement the interface:
public class Foo : IHaveName
If you're using the classes generated from a DBML file, these classes are marked with the partial keyword so implementing the interface is as simple as creating a new file, and inserting:
public partial class Foo : IHaveName
Since the property is already declared as public in the other .cs file generated from the .dbml file, the interface is implemented implicitly.
Finally, you would rewrite your ByName method to take a generic type parameter with a constraint that it implement your interface IHaveName:
private IQueryable<T> ByName<T>(IQueryable<T> dbQuery, Query query)
where T : IHaveName
{
// Everything else is the same.
For your other properties (and methods which use them), you could aggregate them together into one interface, or separate them out, depending on your needs.
Based on your edit, if you want to create an expression dynamically, you don't have to give up compile-time safety:
public Expression<Func<Foo, bool>> SearchMatch(
Expression<Func<Foo, string>> property, string searchTerm)
{
var foo = Expression.Parameter(typeof(Foo), "foo");
// Get the property info from the property expression.
var prop = Expression.Property(foo,
(property.Body as MemberExpression).Member as PropertyInfo);
var search = Expression.Constant(searchTerm);
var equal = Expression.Equal(prop, search);
return Expression.Lambda<Func<Foo, bool>>(equal, foo);
}
Which you then call like so:
var expression = SearchMatch(f => f.Name, "searchTerm");
This ensures that the properties that you are passing to SearchMatch actually exist on Foo. Note if you wanted to make this generic for other scalar property types, you would do the following:
public Expression<Func<Foo, bool>> SearchMatch<T>(
Expression<Func<Foo, T>> property, T searchTerm)
{
var foo = Expression.Parameter(typeof(Foo), "foo");
// Get the property info from the property expression.
var prop = Expression.Property(foo,
(property.Body as MemberExpression).Member as PropertyInfo);
var search = Expression.Constant(searchTerm);
var equal = Expression.Equal(prop, search);
return Expression.Lambda<Func<Foo, bool>>(equal, foo);
}
If I understood what you are trying to achieve reflection might help you. At least if you play nice. Here's a simplified but working example
internal class Program
{
private class Data
{
public string Name { get; set; }
public string Address { get; set; }
public override string ToString()
{
return String.Format("My name is {0} and I'm living at {1}", Name, Address);
}
}
static Expression<Func<Data,bool>> BuildExpression(PropertyInfo prop, IQueryable<string> restrict)
{
return (data) => !restrict.Any(elem => elem == prop.GetValue(data, null));
}
static IQueryable<Data> FilterData(IQueryable<Data> input, Expression<Func<Data, bool>> filter)
{
return input.Where(filter);
}
public static void Main (string[] args)
{
List<Data> list = new List<Data>()
{
new Data {Name = "John", Address = "1st Street"},
new Data {Name = "Mary",Address = "2nd Street"},
new Data {Name = "Carl", Address = "3rd Street"}
};
var filterByNameExpression = BuildExpression(typeof (Data).GetProperty("Name"),
(new List<string> {"John", "Carl"}).AsQueryable());
var filterByAddressExpression = BuildExpression(typeof(Data).GetProperty("Address"),
(new List<string> { "2nd Street"}).AsQueryable());
IQueryable<Data> filetedByName = FilterData(list.AsQueryable(), filterByNameExpression);
IQueryable<Data> filetedByAddress = FilterData(list.AsQueryable(), filterByAddressExpression);
Console.WriteLine("Filtered by name");
foreach (var d in filetedByName)
{
Console.WriteLine(d);
}
Console.WriteLine("Filtered by address");
foreach (var d in filetedByAddress)
{
Console.WriteLine(d);
}
Console.ReadLine();
}
Hovewer, I'\m almost sure it won't work with LINQ-to-SQL. One way to workaround it is to materialize the IQueryable before passing it to such filtering methods (e.g. by calling ToList on them).

Categories

Resources