configure dynamic linq express - linq

static class QueryableExtensions
{
private static MethodInfo StringContainsMethod;
private static MethodInfo StringStartsWithMethod;
static QueryableExtensions()
{
Type[] singleStringParam = new[] { typeof(string) };
StringContainsMethod = typeof(string).GetMethod("Contains", singleStringParam);
StringStartsWithMethod = typeof(string).GetMethod("StartsWith", singleStringParam);
}
public static IQueryable<T> AppendTextFilter<T>(this IQueryable<T> queryable, Expression<Func<T, string>> memberSelector, string condition, string value)
{
Expression expression = null;
switch (condition)
{
case "StartsWith":
expression = Expression.Call(memberSelector.Body, StringStartsWithMethod, Expression.Constant(value));
break;
case "Equals":
expression = Expression.Equal(memberSelector.Body, Expression.Constant(value));
break;
case "Contains":
expression = Expression.Call(memberSelector.Body, StringContainsMethod, Expression.Constant(value));
break;
default:
throw new NotSupportedException(string.Format("'{0}' is not a supported condition", condition));
}
var lambda = Expression.Lambda<Func<T, bool>>(expression, memberSelector.Parameters);
return queryable.Where(lambda);
}
}
When i search on google,I get above class.Well,it really help me a lot,But it still can't meet my need.
The problem is that it can only process field of the type "string". As you seen in the above block code ,the method can only process with T,string .
How I can process any type i want within a single method?

well, the idea would be to replace string by a generic type, this way.
public static IQueryable<T> AppendTextFilter<T, TValue>(
this IQueryable<T> queryable,
Expression<Func<T, TValue>> memberSelector,
string condition,
TValue value)
But with your sample, this doesn't make much sense, as StartsWith, for example, can just be applied if TValue type is string...

Related

Implement the Select for a custom LINQ provider

I understand pretty the way implementing Linq to something however there is a little problem that I encounter here is the situation, I'm implementing a Linq to Tree object the tree it self is simple Tree implementation so I started by creating the Queryable object
public class TreeContext<T> : IOrderedQueryable where T :IComparable<T>
{
private Tree<T> _root;
private Expression _expression;
private IQueryProvider _provider;
public TreeContext(Tree<T> root)
{
_root = root;
Provider = new TreeProvider<T>(_root);
}
public TreeContext(IQueryProvider provider, Expression expression)
{
Provider = provider;
Expression = expression;
}
#region Model
public IEnumerable<TResult> Select<TResult>(Func<T,TResult> predicate)
{
var param = predicate.Method.GetParameters()[0];
return null;
}
public IEnumerable<T> Where(Func<T, bool> predicate)
{
Provider.Execute<IEnumerable>(Expression);
return null;
}
#endregion
public Expression Expression
{
private set { _expression = value; }
get { return _expression; }
}
public Type ElementType => typeof(T);
public IQueryProvider Provider
{
private set { _provider = value; }
get { return _provider; }
}
public IEnumerator<T> GetEnumerator()
=> GetEnumerator();
IEnumerator IEnumerable.GetEnumerator()
=>Provider.Execute<IEnumerable>(Expression).GetEnumerator();
}
Then I implemented the QueryProvider and here resides the problem, exactly at the select method implementation
public class TreeProvider<T> : IQueryProvider where T:IComparable<T>
{
private Tree<T> _root;
public TreeProvider(Tree<T> root)
=> _root = root;
public IQueryable CreateQuery(Expression expression)
=> new TreeContext<T>(this, expression);
public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
=>throw new NotImplementedException();
public object Execute(Expression expression)
=> new TreeQueryContext<T>(_root).Execute(expression);
public TResult Execute<TResult>(Expression expression)
{
throw new NotImplementedException();
}
}
The select implementation is not provided for this custom provider so that I can write a request like
from el in context select el
I added a method select
IEnumerable Select<T,TResult>(Func<T,TResult> selector)
The problem is that I didn't find a way the transform this selector to an expression so that I pass it to the execute method of the QueryProvider
Maybe I didn't understand well how to develop a custom LINQ to custom provider and I didn't find any good and complete tutorial on that in C#
If you want to create LINQ Provider - you have to deal always with Expression Tree to create and parse query.
Select and Where in your TreeContext are not needed at all. Standard LINQ Extensions has these methods and will automatically prepare Expression Tree for you. Focus on TreeProvider implementation.
Since you are new in this area, I would suggest to base your LINQ Provider on Relinq. It will simplify your life from start and will show possible ways for implementation. It has base classes for Provider and Query implementation, but it is not required to derive from them.
Just try
var queryParser = new QueryParser(new ExpressionTreeParser(
ExpressionTreeParser.CreateDefaultNodeTypeProvider(),
ExpressionTreeParser.CreateDefaultProcessor(
ExpressionTransformerRegistry.CreateDefault(),
null)));
var queryModel = parser.GetParsedQuery(expression);
And Relinq will give you QueryModel, which is much easier to parse.

Lambda expression check if null helper

I want to be able to write
MyObject.IsNull(p => p.MyObjectProperty)
I think it is achievable with expression.
I thied to implement it this way:
public static void IsNull<T>(this T root, Expression<Func<T, object>> expression)
{
if (CheckIfNull<T>(expression))
{
throw new ArgumentNullException(GetName(expression));
}
}
private static string GetName<T>(Expression<Func<T, object>> expression)
{
return ((MemberExpression)expression.Body).Member.Name;
}
public static bool CheckIfNull<T>(Expression<Func<T, object>> expression)
{
Expression<Func<T, object>> obj = o => expression.Compile().Invoke(o);
return obj == null;
}
But it is seams to be not working.
How can I fix that?
You have a bug:
The comparison obj == null should be obj(root) == null - of course you have to pass root as an argument to CheckIfNull.
The former comparison will always evaluate to false, since you are effectively comparing o => expression.Compile().Invoke(o) with null - they are never equal. You rather want to compare the result of the call to Invoke with null.
All my suggestions combined:
public static bool CheckIfNull<T>(this T root, Expression<Func<T, object>> expression)
{
return expression.Compile()(root) == null;
}
public static void IsNull<T>(this T root, Expression<Func<T, object>> expression)
{
if (root.CheckIfNull<T>(expression))
{
throw new ArgumentNullException(GetName(expression));
}
}
private static string GetName<T>(Expression<Func<T, object>> expression)
{
return ((MemberExpression)expression.Body).Member.Name;
}
Further comments:
I'm not sure ArgumentNullException is the right exception for this situation. But without knowledge about your scenario, it is hard to suggest something better. Actually:
it seems weird to write an extension method that merely throws an exception if a member is null, especially for a method named IsNull, which is why
I would rename IsNull to ThrowIfNull and rename CheckIfNull to IsNull

EF4 CTP5 - LINQ Dynamic Query Library throws InvalidCastException

With the upgrade to EF4 CTP5, the previously working (with CTP4) LINQ Dynamic Query Library throws the following exception
Unable to cast object of type 'System.Data.Entity.Infrastructure.DbQuery' to type 'System.Linq.IQueryable`1[KIT.TAM.Core.Entities.TravelAgent]'.
on the return statement below:
namespace System.Linq.Dynamic
{
public static class DynamicQueryable
{
public static IQueryable<T> Where<T>(this IQueryable<T> source, string predicate, params object[] values)
{
return (IQueryable<T>)Where((IQueryable)source, predicate, values);
}
}
}
Is there an updated version of the library that works with EF4 CTP5?
Thanks folks.
Solved this one. In DynamicLibrary.cs:
I replaced
public static IQueryable<T> Where<T>(this IQueryable<T> source, string predicate, params object[] values)
{
return (IQueryable<T>)Where((IQueryable)source, predicate, values);
}
with
public static IQueryable<T> Where<T>(this IQueryable<T> source, string predicate, params object[] values)
{
if (source == null) throw new ArgumentNullException("source");
if (predicate == null) throw new ArgumentNullException("predicate");
LambdaExpression lambda = DynamicExpression.ParseLambda(source.ElementType, typeof(bool), predicate, values);
return source.Provider.CreateQuery<T>(
Expression.Call(
typeof(Queryable), "Where",
new Type[] { source.ElementType },
source.Expression, Expression.Quote(lambda)));
}
This is the basically the same code in
public static IQueryable Where(this IQueryable source, string predicate, params object[] values)
but changed source.Provider.CreateQuery() to source.Provider.CreateQuery<T>.
You will also have to do this for the static method OrderBy<T>.

Why does hard coding a function expression speed up query by four minutes?

I am using Dane Morgridge's repository code as a parent to my repository class. In the parent class--EFRepository--there is a method that calls an ObjectSet's Where clause passing in a Func method. Upon calling this code and then assigning it to my grid, the process takes 4 minutes. However, if I hard code the call to the ObjectSet's Where it only takes three seconds. Any ideas why? It seems like the compiler is messing it up somehow.
private void button1_Click(object sender, RoutedEventArgs e)
{
IQueryable<PRODDATA> testP = test.Repository.Find(w => w.PCUST == 49 && w.PDTTK == 20101030);
DateTime firstDate = System.DateTime.Now;
//This is where it takes the most time when passing in the expression above. When the espression is hardcoded (see below) it speeds it up considerably.
radGridView1.ItemsSource = testP;
DateTime secondDate = System.DateTime.Now;
}
public class EFRepository<T> : IRepository<T> where T : PRODDATA
{
public IUnitOfWork UnitOfWork { get; set; }
private IObjectSet<T> _objectset;
private IObjectSet<T> ObjectSet
{
get
{
if (_objectset == null)
{
_objectset = UnitOfWork.Context.CreateObjectSet<T>();
}
return _objectset;
}
}
public virtual IQueryable<T> All()
{
return ObjectSet.AsQueryable();
}
public IQueryable<T> Find(Func<T, bool> expression)
{
//Hardcoding this only takes 2 seconds.
//return ObjectSet.Where(w => w.PCUST == 49 && w.PDTTK == 20101030).AsQueryable();
//passing expression takes 4 minutes.
return ObjectSet.Where(expression).AsQueryable();
}
public void Add(T entity)
{
ObjectSet.AddObject(entity);
}
public void Delete(T entity)
{
ObjectSet.DeleteObject(entity);
}
public void Save()
{
UnitOfWork.Save();
}
}
Because Find takes a Func<T,bool> instead of an Expression<Func<T,bool>>. Presumably, this query is being sent to a DB engine, since it is operating on an IQueryable<T>. But if the expression is passed as a delegate and not a true expression, the LINQ-to-whatever-DB layer is not able to inspect the expression and turn it into SQL. This is resulting in the entire data set being sent from the db server to the C# code, where the "where" expression is then applied in the CLR instead of in the DB.
Change the signature of Find from
public IQueryable<T> Find(Func<T, bool> expression)
to
public IQueryable<T> Find(Expression<Func<T, bool>> expression)
Then see how it performs.

EF4 LINQ Include(string) alternative to hard-coded string?

Is there any alternative to this:
Organizations.Include("Assets").Where(o => o.Id == id).Single()
I would like to see something like:
Organizations.Include(o => o.Assets).Where(o => o.Id == id).Single()
to avoid the hard-coded string "Assets".
In EF 4.1, there is a built-in extension method for this.
You must have a reference to "EntityFramework" assembly (where EF 4.1 lives) in your project and use System.Data.Entity.
using System.Data.Entity;
If you want to include nested entities, you do it like this:
db.Customers.Include(c => c.Orders.Select(o => o.LineItems))
Not sure if this works for EF4.0 entities (ObjectContext based).
For Entity Framework 1.0, I created some extensions methods for doing this.
public static class EntityFrameworkIncludeExtension
{
public static ObjectQuery<T> Include<T>(this ObjectQuery<T> src, Expression<Func<T, StructuralObject>> fetch)
{
return src.Include(CreateFetchingStrategyDescription(fetch));
}
public static ObjectQuery<T> Include<T>(this ObjectQuery<T> src, Expression<Func<T, RelatedEnd>> fetch)
{
return src.Include(CreateFetchingStrategyDescription(fetch));
}
public static ObjectQuery<T> Include<T, TFectchedCollection>(this ObjectQuery<T> src, Expression<Func<T, IEnumerable<TFectchedCollection>>> fetch)
{
return src.Include(CreateFetchingStrategyDescription(fetch));
}
public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, RelatedEnd>> fetch, Expression<Func<FetchedChild, Object>> secondFetch)
where FetchedChild : StructuralObject
{
return src.Include(CombineFetchingStrategies(fetch, secondFetch));
}
public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, RelatedEnd>> fetch, Expression<Func<FetchedChild, RelatedEnd>> secondFetch)
where FetchedChild : StructuralObject
{
return src.Include(CombineFetchingStrategies(fetch, secondFetch));
}
public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, RelatedEnd>> fetch, Expression<Func<FetchedChild, StructuralObject>> secondFetch)
where FetchedChild : StructuralObject
{
return src.Include(CombineFetchingStrategies(fetch, secondFetch));
}
public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, IEnumerable<FetchedChild>>> fetch, Expression<Func<FetchedChild, Object>> secondFetch)
where FetchedChild : StructuralObject
{
return src.Include(CombineFetchingStrategies(fetch, secondFetch));
}
public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, IEnumerable<FetchedChild>>> fetch, Expression<Func<FetchedChild, RelatedEnd>> secondFetch)
where FetchedChild : StructuralObject
{
return src.Include(CombineFetchingStrategies(fetch, secondFetch));
}
public static ObjectQuery<T> Include<T, FetchedChild>(this ObjectQuery<T> src, Expression<Func<T, IEnumerable<FetchedChild>>> fetch, Expression<Func<FetchedChild, StructuralObject>> secondFetch)
where FetchedChild : StructuralObject
{
return src.Include(CombineFetchingStrategies(fetch, secondFetch));
}
private static String CreateFetchingStrategyDescription<TFetchEntity, TFetchResult>(
Expression<Func<TFetchEntity, TFetchResult>> fetch)
{
fetch = (Expression<Func<TFetchEntity, TFetchResult>>)FixedWrappedMemberAcces.ForExpression(fetch);
if (fetch.Parameters.Count > 1)
throw new ArgumentException("CreateFetchingStrategyDescription support only " +
"one parameter in a dynamic expression!");
int dot = fetch.Body.ToString().IndexOf(".") + 1;
return fetch.Body.ToString().Remove(0, dot);
}
private static String CreateFetchingStrategyDescription<T>(Expression<Func<T, Object>> fetch)
{
return CreateFetchingStrategyDescription<T, Object>(fetch);
}
private static String CombineFetchingStrategies<T, TFetchedEntity>(
Expression<Func<T, Object>> fetch, Expression<Func<TFetchedEntity, Object>> secondFetch)
{
return CombineFetchingStrategies<T, Object, TFetchedEntity, Object>(fetch, secondFetch);
}
private static String CombineFetchingStrategies<TFetchEntity, TFetchResult, TFetchedEntity, TSecondFetchResult>(
Expression<Func<TFetchEntity, TFetchResult>> fetch, Expression<Func<TFetchedEntity, TSecondFetchResult>> secondFetch)
{
return CreateFetchingStrategyDescription<TFetchEntity, TFetchResult>(fetch) + "." +
CreateFetchingStrategyDescription<TFetchedEntity, TSecondFetchResult>(secondFetch);
}
}
Usage:
Orders.Include(o => o.Product); // generates .Include("Product")
Orders.Include(o => o.Product.Category); // generates .Include("Product.Category")
Orders.Include(o => o.History); // a 1-* reference => .Include("History")
// fetch all the orders, and in the orders collection.
// also include the user reference so: .Include("History.User")
// but because history is an collection you cant write o => o.History.User,
// there is an overload which accepts a second parameter to describe the fetching
// inside the collection.
Orders.Include(o => o.History, h => h.User);
I haven't tested this on EF4.0, but I expect it to work.
That's pretty easy to do, using Expressions :
public static class ObjectQueryExtensions
{
public static ObjectQuery<T> Include<T, TProperty>(this ObjectQuery<T> objectQuery, Expression<Func<T, TProperty>> selector)
{
MemberExpression memberExpr = selector.Body as MemberExpression;
if (memberExpr != null)
{
return objectQuery.Include(memberExpr.Member.Name);
}
throw new ArgumentException("The expression must be a MemberExpression", "selector");
}
}
You can use it exactly as in the example in your question
UPDATE
Improved version, which supports multiple chained properties :
public static class ObjectQueryExtensions
{
public static ObjectQuery<T> Include<T, TProperty>(this ObjectQuery<T> objectQuery, Expression<Func<T, TProperty>> selector)
{
string propertyPath = GetPropertyPath(selector);
return objectQuery.Include(propertyPath);
}
public static string GetPropertyPath<T, TProperty>(Expression<Func<T, TProperty>> selector)
{
StringBuilder sb = new StringBuilder();
MemberExpression memberExpr = selector.Body as MemberExpression;
while (memberExpr != null)
{
string name = memberExpr.Member.Name;
if (sb.Length > 0)
name = name + ".";
sb.Insert(0, name);
if (memberExpr.Expression is ParameterExpression)
return sb.ToString();
memberExpr = memberExpr.Expression as MemberExpression;
}
throw new ArgumentException("The expression must be a MemberExpression", "selector");
}
}
Example :
var query = X.Include(x => x.Foo.Bar.Baz) // equivalent to X.Include("Foo.Bar.Baz")
See 'Say goodbye to the hardcoded ObjectQuery(T).Include calls'.
Another solution is to retrieve entity name using EntitySet.Name.
You code will be:
var context = new DBContext();
context.Organizations.Include(context.Assets.EntitySet.Name).Where(o => o.Id == id).Single()
Good news that EF4 CTP4 currently support this feature.
Another option is to include an internal partial class inside your class using the TT template.
T4 code:
<#
region.Begin("Member Constants");
#>
public partial class <#=code.Escape(entity)#>Members
{
<#
foreach (EdmProperty edmProperty in entity.Properties.Where(p => p.TypeUsage.EdmType is PrimitiveType && p.DeclaringType == entity))
{
bool isForeignKey = entity.NavigationProperties.Any(np=>np.GetDependentProperties().Contains(edmProperty));
bool isDefaultValueDefinedInModel = (edmProperty.DefaultValue != null);
bool generateAutomaticProperty = false;
#>
public const string <#=code.Escape(edmProperty)#> = "<#=code.Escape(edmProperty)#>";
<#
}
#>
}
<#
region.End();
#>
Which will produce something like:
#region Member Constants
public partial class ContactMembers
{
public const string ID = "ID";
public const string OriginalSourceID = "OriginalSourceID";
public const string EnabledInd = "EnabledInd";
public const string EffectiveDTM = "EffectiveDTM";
public const string EndDTM = "EndDTM";
public const string EnterDTM = "EnterDTM";
public const string EnterUserID = "EnterUserID";
public const string LastChgDTM = "LastChgDTM";
public const string LastChgUserID = "LastChgUserID";
}
#endregion

Resources