Extension method in where clause in linq to Entities - c#

In linq to Entities we needed a method that works like "sql like". We have implemented our own extension method to IQueryable, because contains method not work for us because doesn't accept patterns like '%a%b%'
The created code is:
private const char WildcardCharacter = '%';
public static IQueryable<TSource> WhereLike<TSource>(this IQueryable<TSource> _source, Expression<Func<TSource, string>> _valueSelector, string _textSearch)
{
if (_valueSelector == null)
{
throw new ArgumentNullException("valueSelector");
}
return _source.Where(BuildLikeExpressionWithWildcards(_valueSelector, _textSearch));
}
private static Expression<Func<TSource, bool>> BuildLikeExpressionWithWildcards<TSource>(Expression<Func<TSource, string>> _valueSelector, string _textToSearch)
{
var method = GetPatIndexMethod();
var body = Expression.Call(method, Expression.Constant(WildcardCharacter + _textToSearch + WildcardCharacter), _valueSelector.Body);
var parameter = _valueSelector.Parameters.Single();
UnaryExpression expressionConvert = Expression.Convert(Expression.Constant(0), typeof(int?));
return Expression.Lambda<Func<TSource, bool>> (Expression.GreaterThan(body, expressionConvert), parameter);
}
private static MethodInfo GetPatIndexMethod()
{
var methodName = "PatIndex";
Type stringType = typeof(SqlFunctions);
return stringType.GetMethod(methodName);
}
This works correctly and the code is executed entirely in SqlServer, but now we would use this extension method inside where clause as:
myDBContext.MyObject.Where(o => o.Description.Like(patternToSearch) || o.Title.Like(patterToSerch));
The problem is that the methods used in the where clause have to return a bool result if is it used with operators like '||' , and I don't know how to make that the code I have created returns a bool and keep the code executed in sqlserver. I suppose that I have to convert the returned Expression by BuildLinqExpression method to bool, but I don't know how to do it
To sum up! First of all it's possible to create our own extensiond methods in Linq to Entities that execute the code in SqlServer? if this is possible how I have to do it?
Thanks for your help.

No, you cannot educate EF to process your custom extension methods, even though you have code that builds expressions that would be usable to EF.
Either:
use the SqlFunctions methods directly in your EF Where expression
use an ExpressionVisitor to combine multiple expressions into a composite (OrElse / AndAlso) expression (note this won't help you have code like you want, but it will let you use your two methods and perform a || on them - it will look complex, though)
The first is simpler and clearer.

Answered here: https://stackoverflow.com/a/10726256/84206
His code: http://pastebin.com/4fMjaCMV
Allows you to tag an extension method as [Expandable] and so long as the expression it returns works with linq2entities, it will replace your function call with the inner expression. Note that inline lambdas give errors, so I had to declare them as local variables or static variable such as the IsCurrent variable here:
static Expression<Func<PropertyHistory, bool>> IsCurrent = (p) => p.Starts <= DateTime.Now;
[ExpandableMethod]
public static IQueryable<PropertyHistory> AsOf(this IQueryable<PropertyHistory> source)
{
return source.Where(IsCurrent);
}

Related

Creating Linq Expression to convert int to enum name in AutoMapper projection

I'd like to streamline my current view model mapping. I currently use Automapper ProjectTo to create my view model, then call a post mapping method to populate a StatusDescription string property from a Status enum whose value is in another int property in the view model class, StatusId.
I found this other answer, but I am not able to get it to work, and am having a hard time understanding what exactly it is doing due to my general lack of experience with expressions.
For reference, here is the expression method (note I had to comment out the defaultValue parameter as I was getting an error An expression tree may not contain a call or invocation that uses optional arguments:
public static Expression<Func<TSource, String>> CreateEnumToStringExpression<TSource, TMember>(
Expression<Func<TSource, TMember>> memberAccess)//,string defaultValue = ""
{
string defaultValue = "";
var type = typeof(TMember);
if (!type.IsEnum)
{
throw new InvalidOperationException("TMember must be an Enum type");
}
var enumNames = Enum.GetNames(type);
var enumValues = (TMember[])Enum.GetValues(type);
var inner = (Expression)Expression.Constant(defaultValue);
var parameter = memberAccess.Parameters[0];
for (int i = 0; i < enumValues.Length; i++)
{
inner = Expression.Condition(
Expression.Equal(memberAccess.Body, Expression.Constant(enumValues[i])),
Expression.Constant(enumNames[i]),
inner);
}
var expression = Expression.Lambda<Func<TSource, string>>(inner, parameter);
return expression;
}
I am calling it like this:
.ForMember(dest => dest.Status, option =>
option.MapFrom(src =>
EnumHelper.ExpressionHelper
.CreateEnumToStringExpression((ShippingContainerHeader s) => s.StatusId)
))
The error I am getting when I run this is:
LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expression..
A.) I think I am calling this wrong maybe? Shouldn't I be using the reference to src in the MapFrom method call in order to pass the actual value of StatusId to the expression method?
B.) Seems like there is an issue with the expression generation method as it is producing the ToString() error. There is not an explicit .ToString() call in that method, but it must be implied somehow.
C.) Understanding the code: I can follow all of the code within the CreateEnumToStringExpression method except what the parameter variable is and the final expression variable expression statement. Also the generic types I'm confused on...there are a lot of generic types and the call to the method from the linked example doesn't explicitly define any of them.
In Expression<Func<TSource, String>> I'm pretty sure TSource is the input, which is an int. String is the output, which would be the enum name.
In CreateEnumToStringExpression<TSource, TMember> and Expression<Func<TSource, TMember>> memberAccess: TSource again is int, TMember is the enum class type.
But do you not have to explicitly call these out when calling the method? Something like: CreateEnumToStringExpression<int, StatusesEnum>(new Expression<Func<int, ShippingContainerHeader>> { s => s.StatusId })

How to add extra parameter to Expression<Func<T, bool>> predicate in Generic Repository

I'm using generic repository and I need to add extra parameter in one of the repository methods. For instance I have isDeleted column on database, I want to add this column as false to predicate.
How can I add extra parameter to predicate from repository method? This extra parameter is fixed for all tables. (isDeleted = false)
This is my original method which is getting single record from db.
public T GetSingle(Expression<Func<T, bool>> expression)
{
return _dbSet.Where(expression).SingleOrDefault();
}
This is updated version to add extra parameter I came so far.
public T GetSingle(Expression<Func<T, bool>> expression)
{
Expression<Func<T, bool>> extra = d => d.GetType().GetProperty("isDeleted").Equals(false);
var exp = Expression.And(expression.Body, extra.Body);
var body = Expression.And(expression.Body, extra.Body);
var lambda = Expression.Lambda<Func<T, bool>>(body, extra.Parameters);
return _dbSet.Where(lambda).SingleOrDefault();
}
But this updated version gives lambda body like this and ofcouse it's not working.
((d.ID == value(ProjectName.Namespace.Controllers.ControllerName).User.CompanyId) And d.GetType().GetProperty("isDeleted").Equals(Convert(False)))
public T GetSingle<T>(Expression<Func<T, bool>> expression)
{
var #params = expression.Parameters;
var checkNotDeleted = Expression.Equal(Expression.PropertyOrField(#params[0], "isDeleted"), Expression.Constant(false));
var originalBody = expression.Body;
var fullExpr = Expression.And(originalBody, checkNotDeleted);
var lambda = Expression.Lambda<Func<T, bool>>(fullExpr, #params);
return _dbSet.Where(lambda).SingleOrDefault();
}
Once you start using Expression's methods, you need to always use them (except for Expression.Constant, of course). All logic and code you write must be represented with Expression nodes.
There's a method PropertyOrField which reads the value of a property from a particular expression. In this case, we're reading it from the parameter (that is, d => d.isDeleted - we're writing the d.isDeleted part). Then we need to compare the value to false.
Finally, we simply And the original expression to ours, and create a lambda with the original parameters.
Take a look to PredicateBuilder, in particular the "Generic Predicates" section that explains how to create generic constraints to filter your items
You can have a BaseEntity which contains IsDeleted property.
All the other entities should be child of this BaseEntity.
Now in the Repository,
public class Repository<T> where T : BaseEntity
{
....
}
And in the query, you can add expression for IsDeleted also.
Please have a look here: Generic Repository Pattern - Entity Framework, ASP.NET MVC and Unit Testing Triangle
You can change method to:
public T GetSingle(param Expression<Func<T, bool>>[] expression)
Use array, and key word param.
Than you can send two, three, four or what ever number of parameters you want.

Creating an IQueryable to Equal int32?

I am trying to create an query extension which would compare a nullable int sql column value with a value. But i am struggling already over 8 hours to find any working solution.
I have already found a lot of help on this side. But all the remarks did not helped me.
I have altered the code so many times, but nothing seems to work. I want to create something similar as WHERE ManagerID IN (10,20,30)
The main code
IQueryable<Users> query = _context.CreateObjectSet<Users>();
query = query.IsMember(a => a.ManagerID, new Int32?[] { 10,20,30 });
return query.ToList();
Currently while executing the query.ToList(); it returns me a
Unable to create a constant value of type 'System.Object'. Only primitive types or enumeration types are supported in this context.
public static IQueryable<T> IsMember<T>(this IQueryable<T> source, Expression<Func<T, Int32?>> stringProperty, params Int32?[] searchTerms)
{
if (searchTerms == null || !searchTerms.Any())
{
return source;
}
Expression orExpression = null;
foreach (var searchTerm in searchTerms)
{
var searchTermExpression = Expression.Constant(searchTerm, typeof(object)); // <<--- This cast would make it no longer a primitive type
var containsExpression = Expression.Call(stringProperty.Body, typeof(Int32?).GetMethod("Equals"), searchTermExpression);
orExpression = BuildOrExpression(orExpression, containsExpression);
}
var completeExpression = Expression.Lambda<Func<T, bool>>(orExpression, stringProperty.Parameters);
return source.Where(completeExpression);
}
private static Expression BuildOrExpression(Expression existingExpression, Expression expressionToAdd)
{
return existingExpression == null ? expressionToAdd : Expression.OrElse(existingExpression, expressionToAdd);
}
The line marked to give the constant another datatype, is indeed causing the issue, but if I dont make it of type object, the Expression will not work, as it could not match the Int32? datatype.
Can anybody help me?
thanks
=============================================
Additional info
It is indeed to had a a larger picture.
I just want to create something more dynamic which could be used on other projects also.
I would like to use some functions which will look appealing than all those multilines
query = query.Like(a => a.UserName, filter.UserName, true);
query = query.Equals(a => a.UserTown, filter.UserTown, true);
query = query.IsMember(a => a.Division, filter.Division); // is an array of possible divisions
it worked fine for Like and Equals which are string based. But want to have a similar product for (nullable) integers
I was inspired by the following post. Which created a search function (which i renamed for my project to Like)
link
I wanted to create others similar. the last boolean is to verify if nullable in the column is allowed or not.
The reason also to use an extension, is that i also have alot of filters in my filter page.
With this extension, i would easily check in the beginning of my Like and Equal function if a filter was given without checking if my filter has a value 20x.
public static IQueryable<T> Like<T>(this IQueryable<T> source, Expression<Func<T, string>> stringProperty, string searchTerm, bool isnullValueAllowed)
if (String.IsNullOrEmpty(searchTerm))
{
return query;
}
It's not clear why you want to create this extension as you could simply write something like:
query.Where(user => (new[]{10,20,30}).Contains(user.ManagerId)).ToList();
However, assuming the real use case is somewhat more complicated than the example you've given, could you not construct your expression as either a comparison against a constant Int32 or a comparison against null, depending on whether searchTerm.HasValue() was true?
You need to use the equality operator == rather than the Equals method here. It actually makes your expression code a tad simpler:
foreach (var searchTerm in searchTerms)
{
var comparison = Expression.Equals(stringProperty.Body,
Expression.Constant(searchTerm));
orExpression = BuildOrExpression(orExpression, comparison);
}
Of course, as is mentioned by others, you don't need to build up an expression representing an OR of each of these comparisons, you can simply use Conatains on the set in a lambda and the query provider will do all of this for you.

Utilizing Funcs within expressions?

Background
I have an example of a test that passes but an error that happens down the pipeline and I'm not sure why. I'd like to figure out what's going on but I'm new to Expression construction and don't want to make any assumptions.
This is for a search filtering mechanism. It uses ServiceStack's PredicateBuilder implementation. I essentially have a list of values that I pass in, and I want it to construct an expression tree. I had previously done this just with Func<T<bool>> but realized that I needed to wind up with Expression<Func<T<bool>>>. Bummer.
The Goal
Search filters built from Re-usable search filter types, which built out of Funcs and Expressions that allows me to pass in a field name from an object along with values I should match on and wind up with something that we can run a Where() statement against.
The Code / Issue
The generic "nullable bool" filter I'm trying -- sets up the acceptable items and returns a func that is meant to help filter:
public class NullableBoolFilter : IGenericSearchFilter<bool?>
{
public Func<bool?, bool> GetFilterFunc(string valuesToProcess)
{
var acceptableValues = new List<bool?>();
if (string.IsNullOrWhiteSpace(valuesToProcess))
{
// all values acceptable
acceptableValues = new List<bool?>{true, false, null};
}
else
{
if (!valuesToProcess.Contains("0") && !valuesToProcess.Contains("1"))
{
throw new ArgumentException("Invalid Nullable boolean filter attribute specified");
}
if (valuesToProcess.Contains("0"))
{
acceptableValues.Add(false);
}
if (valuesToProcess.Contains("1"))
{
acceptableValues.Add(true);
}
}
Func<bool?, bool> returnFunc = delegate(bool? item) { return acceptableValues.Any(x=>x == item); };
return returnFunc;
}
}
Then I have another filter, which inherits from the NullableBoolFilter and attempts to use the Func:
public class ClaimsReportIsMDLFilter : NullableBoolFilter, ISearchFilter<vSEARCH_ClaimsReport>
{
public Expression<Func<vSEARCH_ClaimsReport, bool>> GetExpression(string valuesToProcess)
{
var theFunc = base.GetFilterFunc(valuesToProcess);
Expression<Func<vSEARCH_ClaimsReport, bool>> mdlMatches = item => theFunc(item.IsMDL);
var predicate = PredicateBuilder.False<vSEARCH_ClaimsReport>();
predicate = predicate.Or(mdlMatches);
return predicate;
}
}
The following test passes:
public class ClaimsReportIsMDLFilterTests
{
// ReSharper disable InconsistentNaming
private readonly vSEARCH_ClaimsReport ItemWithMDL = new vSEARCH_ClaimsReport { IsMDL = true };
private readonly vSEARCH_ClaimsReport ItemWithoutMDL = new vSEARCH_ClaimsReport { IsMDL = false };
private readonly vSEARCH_ClaimsReport ItemWithNullMDL = new vSEARCH_ClaimsReport { IsMDL = null };
// ReSharper restore InconsistentNaming
[Fact]
public void WithSearchValueOf1_HidesNonMDLAndNull()
{
var sut = this.GetCompiledExpressionForValues("1");
sut.Invoke(ItemWithMDL).Should().BeTrue();
sut.Invoke(ItemWithoutMDL).Should().BeFalse();
sut.Invoke(ItemWithNullMDL).Should().BeFalse();
}
private Func<vSEARCH_ClaimsReport, bool> GetCompiledExpressionForValues(string searchValue)
{
return new ClaimsReportIsMDLFilter().GetExpression(searchValue).Compile();
}
}
The Problem
When I actually attempt to run this, I receive the error:
variable 'param' of type 'vSEARCH_ClaimsReport' referenced from scope '', but it is not defined
It makes sense to me why this might occur -- at the time it's evaluated, I don't have a real object to pass into the Func. However, I'm confused as to why my tests might pass but this doesn't in actual usage.
Questions
Why might my tests pass but I still receive this error?
How the heck should I begin trying to fix this?
Is there a remotely easy way to take that Func and turn it into an Expression that I can pass a field into?
Do I need to abandon the generic filter idea and have each class manually add expressions to the PredicateBuilder based on input passed in? That's doable, but it seems like the work could be reduced more.
Why might my tests pass [...]
Because your test is simply compiling the expression down into the code that it represents and invoking it. It doesn't need to actually parse the expression tree and look at what the code it represents is doing, it just runs it and ensures that the output is right.
Why might [...] I still receive this error?
Because when you're actually using it, it's not just executing the code; rather it is looking through the expression tree to try to determine what the code is doing so that it can be translated into something else, not so that it can be run as C# code.
Your expression is doing nothing but calling a delegate. There is no way for someone traversing the expression tree to see inside the delegate and know what it's doing. Knowing that you're calling another method isn't something that can be translated into another language.
How the heck should I begin trying to fix this?
You need to generate an Expression from the start, rather than generating a Func and then just creating an Expression that calls it.
Is there a remotely easy way to take that Func and turn it into an Expression that I can pass a field into?
No. You'd need to pull out the IL code of the function, decompile that into C# code, then build up Expression objects to represent that code. That's pretty much just not going to happen.
You're pretty much going to need to have GetFilterFunc return an Expression, to get this to work. Fortunately, this is quite easy to do, given what you have. You simply need to change the method signature and to replace the last two lines with the following:
return item => acceptableValues.Any(x => x == item);
And voila. The lambda can be compiled into an Expression object, rather than a delegate, based on context, so if the return type of the method is an Expression<Func<bool?,bool>> that's what you'll get.
Now, to use this in GetExpression. First off, the PredicateBuilder isn't really doing anything. Adding an OR FALSE to your expression changes nothing meaningful about it. All of that can go. All that leaves us with is using an Expression<Func<bool?,bool>> and changing it into an Expression<Func<vSEARCH_ClaimsReport, bool>> by pulling out a boolean property. To do this is a bit more work for expressions than for delegates. Rather than just invoking the expression, we need to do a tad more work to compose them. We'll want to write a method to do this operation:
public static Expression<Func<TFirstParam, TResult>>
Compose<TFirstParam, TIntermediate, TResult>(
this Expression<Func<TFirstParam, TIntermediate>> first,
Expression<Func<TIntermediate, TResult>> second)
{
var param = Expression.Parameter(typeof(TFirstParam), "param");
var newFirst = first.Body.Replace(first.Parameters[0], param);
var newSecond = second.Body.Replace(second.Parameters[0], newFirst);
return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}
And this relies on the use of the following method to replace all instances of one expression with another:
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
internal class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
What this is doing is replacing all instances of the second expression's parameter with the body of the first expression, effectively inlining that expression into the second. The rest is simply replacing all of the parameters with a new single parameter and wrapping it back up into a lambda.
Now that we have that, our method is quite easy:
public Expression<Func<vSEARCH_ClaimsReport, bool>> GetExpression(
string valuesToProcess)
{
Expression<Func<vSEARCH_ClaimsReport, bool?>> selector =
item => item.IsMDL;
return selector.Compose(base.GetFilterFunc(valuesToProcess));
}

extending SqlMethods.Like to support property name

I'm trying to extend SqlMethods.Like method to support property name rather than property value, i wrote the following extension method :
public static bool Like(this object obj, string propertyName, string pattern)
{
var properties = obj.GetType().GetProperties().Select(p => p.Name);
if(!properties.Contains(propertyName))
throw new Exception(string.Format("Object does not contain property:{0}", propertyName));
return SqlMethods.Like(obj.GetType().GetProperty(propertyName).GetValue(obj, null).ToString(), pattern);
}
however the method throws the following exception :
Method 'Boolean Like(System.Object, System.String, System.String)' has no supported translation to SQL.
how can i write an extension method with transaction to SQL support ?
I found this answer from RichardD that is exactly the correct answer. Reposting for clarity, but original is linked below.
using System;
using System.Linq;
using System.Linq.Expressions;
public static class Extensions
{
public static IQueryable<T> WhereLike<T>(this IQueryable<T> source, string propertyName, string pattern)
{
if (null == source) throw new ArgumentNullException("source");
if (string.IsNullOrEmpty(propertyName)) throw new ArgumentNullException("propertyName");
var a = Expression.Parameter(typeof(T), "a");
var prop = Expression.Property(a, propertyName);
var body = Expression.Call(typeof(SqlMethods), "Like", null, prop, Expression.Constant(pattern));
var fn = Expression.Lambda<Func<T, bool>>(body, a);
return source.Where(fn);
}
}
...
.WhereLike("Description", "%a%b%c%"));
The solution uses expression trees, but all advanced LinqToSql operations will require familiarity with that.
From: http://forums.asp.net/p/1488418/3503874.aspx
What you want to do does not seem to make sense in the contxt of what SqlMethods.Like actually does. When you pass in a property of a class you are essentially telling it to translate that into the equivelent field in the SQL query. e.g.
var result = from names in db.Names
where SqlMethods.Like(names.FullName, '%Smith%')
select names;
would translate to something like:
SELECT *
FROM Names
WHERE Fullname LIKE '%Smith%'
(in practice it would be different using parameters and sp_executeSQL but coneptually that is what it would do).
If you want to pass in the name of a property what does that mean in terms of SQL, conceptually it makes no sense e.g.
SELECT *
FROM Names
WHERE --what would go here-- LIKE '%Smith%'
As such you are not going to be able to create a Linq To SQL method that creates nonsense SQL.
What are you actually trying to do, the chance is that you are going about it completely the wrong way.
Edit:hmm from your comment i think i understand what you want to do, in essense you want to be able to specify the column you are doing a LIKE comparison with at run time. You cannot do it exactly. You could use a stored procedure that used dynamic SQL and took a string parameter for the column. You could then expose this as a method on your data context class.

Categories