I am trying to write a custom Linq extension, which can also be performed within the DB.
Basically, what I want to do is collectionOfStrings.Where(x => !x.IsNullOrWhiteSpace) Unfortunately it's not supported.
=============== What I've tried so far =============
This part is only interesting to those, who might come up with another idea apart from the one below.
There is a workaround by going this way collection.Where(x => x != null && x.Trim() != string.Empty), but since I use it frequently, it's not the best solution.
The prettiest solution would be, to find a way to write a string extension IsNullOrWhiteSpaceDB, which works or to kind of add the IsNullOrWhiteSpace method to the database programmatically to ensure support.
That's been my attempt to create a working IsNullOrWhiteSpace method, but it's not supported too:
public static bool IsNullOrWhiteSpaceDB(this string? str) =>
str == null || str.Trim() == String.Empty;
So I've started writing a predicate, which is working fine:
public IQueryable<string> GetAll() =>
GetAll().Select(x => x.property).Where(StringIsNotNullOrWhiteSpace).Distinct();
private static Expression<Func<string?, bool>> StringIsNotNullOrWhiteSpace =>
x => x != null && x.Trim() != string.Empty;
=============== The current problem =============
Neverthless I'd actually like to be able to run it on another collection than on a collection of strings. So I tried to build a custom linq extension (inspired by this solution (https://stackoverflow.com/a/40924558/9487478)):
public class QueryVisitor : ExpressionVisitor
{
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method.IsStatic && node.Method.Name == "IsNullOrWhiteSpace")
{
//!(b.Diameter == null || b.Diameter.Trim() == string.Empty)
var arg = node.Arguments[0];
var argTrim = Expression.Call(arg, typeof(string).GetMethod("Trim", Type.EmptyTypes));
var exp = Expression.MakeBinary(ExpressionType.Or,
Expression.MakeBinary(ExpressionType.Equal, arg, Expression.Constant(null, arg.Type)),
Expression.MakeBinary(ExpressionType.Equal, argTrim, Expression.Constant(string.Empty, arg.Type))
);
return exp;
}
return base.VisitMethodCall(node);
}
}
public static class EfQueryableExtensions
{
public static IQueryable<T> WhereEx<T>(this IQueryable<T> queryable, Expression<Func<T, bool>> where)
{
var visitor = new QueryVisitor();
return queryable.Where(visitor.VisitAndConvert(where, "WhereEx"));
}
}
And that's what my custom extension actually looks like:
public static class QueryHelper
{
public static IQueryable<T> WhereIsNotNullOrWhiteSpace<T>(this IQueryable<T> query, Expression<Func<T, string?>> expression)
{
var arg = expression.Body;
var argTrim = Expression.Call(arg, typeof(string).GetMethod("Trim", Type.EmptyTypes));
var exp = Expression.MakeBinary(ExpressionType.And,
Expression.MakeBinary(ExpressionType.NotEqual, arg, Expression.Constant(null, arg.Type)),
Expression.MakeBinary(ExpressionType.NotEqual, argTrim, Expression.Constant(string.Empty, arg.Type))
);
var lambda = Expression.Lambda<Func<T, bool>>(exp, expression.Parameters);
var result = query.Where(lambda);
return result;
}
}
After the query.Where(lambda) is executed there is an inner exception within the result:
NHibernate.Hql.Ast.ANTLR.QuerySyntaxException: A recognition error occurred.
The "original version" throws the same error too, so I thought it might be the created expression ((x == null) Or (x.Trim() == "")) (copied from the debugger). For me it looks actually quite good and I don't understand the cause of the error.
Any ideas? I would be pleased!
You don't need to build an expression for this. You just need an extension method that takes and returns IQueryable<string>.
public static class Extensions
{
public static IQueryable<string> IsNullOrWhiteSpaceDB(this IQueryable<string> input)
{
return input.Where(x => x != null && x.Trim() != string.Empty);
}
}
I have created PR for LINQKit which should simplify your life https://github.com/scottksmith95/LINQKit/pull/127
Idea is to add ExpandableAttribute to such methods which points to static function with expression for substitution.
public static class Extensions
{
[Expandable(nameof(IsNotNullOrWhiteSpaceDBImpl))]
public static bool IsNotNullOrWhiteSpaceDB(string str)
=> throw new NotImplementedException();
public static Expression<Func<string, bool>> IsNotNullOrWhiteSpaceDBImpl()
=> x => x != null && x.Trim() != string.Empty;
}
So query should use AsExpandable() at least once. Put this call somewhere in repository.
db.Users.AsExpandable()
.Where(u => u.FirstName.IsNotNullOrWhiteSpaceDB() || u.MiddleName.IsNotNullOrWhiteSpaceDB())
Related
I have the following problem in my lambda expression: I need to rename the property because it will be passed from entity to entity. In other words: I need to use the same expression in more than one query in different entities.
For example:
var expr = x => x.Id == converterId
To be
var expr = x => x.ConverterId == converterId
I have tried to do the following
var oldParam = expr.Parameters[0];
var newParam = Expression.Parameter(oldParam.Type, "ConverterId");
This code replaces x not Id`
This isn't trivial, but can be done my writing (subclassing) an ExpressionVisitor, and overriding VisitMember, making the substitution for a different Expression.Property, but using the original target expression (from the expression in the original lambda)
However, for simple cases, it is probably easier to forget that, and just build the expression-tree manually from first principles, rather than using a lambda.
The following shows both approaches:
using System;
using System.Linq.Expressions;
static class P
{
static void Main()
{
// the compiler-generated expression-tree from the question
Console.WriteLine(Baseline(42));
// build our own epression trees manually
Console.WriteLine(ByName(42, nameof(Foo.Id)));
Console.WriteLine(ByName(42, nameof(Foo.ConverterId)));
// take the compiler-generated expression tree, and rewrite it with a visitor
Console.WriteLine(Convert(Baseline(42), nameof(Foo.Id), nameof(Foo.ConverterId)));
}
static Expression<Func<Foo, bool>> Baseline(int converterId)
{
// note this uses a "captured variable", so the output
// looks uglier than you might expect
return x => x.Id == converterId;
}
static Expression<Func<Foo, bool>> ByName(int converterId, string propertyOrFieldName)
{
var p = Expression.Parameter(typeof(Foo), "x");
var body = Expression.Equal(
Expression.PropertyOrField(p, propertyOrFieldName),
Expression.Constant(converterId, typeof(int))
);
return Expression.Lambda<Func<Foo, bool>>(body, p);
}
static Expression<Func<Foo, bool>> Convert(
Expression<Func<Foo, bool>> lambda, string from, string to)
{
var visitor = new ConversionVisitor(from, to);
return (Expression<Func<Foo, bool>>)visitor.Visit(lambda);
}
class ConversionVisitor : ExpressionVisitor
{
private readonly string _from, _to;
public ConversionVisitor(string from, string to)
{
_from = from;
_to = to;
}
protected override Expression VisitMember(MemberExpression node)
{
if(node.Member.Name == _from)
{
return Expression.PropertyOrField(
node.Expression, _to);
}
return base.VisitMember(node);
}
}
}
class Foo
{
public int Id { get; set; }
public int ConverterId { get; set; }
}
public IEnumerable<Table1> GetMatchingTable1(string param, double[] Thicknesses)
{
return DBContext.Table1.Where(c => c.Field1 == param
&& Thicknesses.Any(Thickness => Thickness >= c.MinThickness && Thickness <= c.MaxThickness))
.ToList();
}
Above query return the following exception. "Some part of your SQL statement is nested too deeply. Rewrite the query or break it up into smaller queries."
So far, all my research on the web for this error pointed toward replacing "ANY" with "CONTAINS". Here is one site where they fix the problem using this solution : http://blog.hompus.nl/2010/08/26/joining-an-iqueryable-with-an-ienumerable/
But in my case, "CONTAINS" doesn't seem usable since I check a RANGE with Min and Max.
How should this query be written to have a proper SQL Statement generated by LinqToEntity?
Thanks
You could try to build the query dynamically:
public IEnumerable<Table1> GetAllCoilLengthSettingsWithChilds(string param, double[] Thicknesses)
{
// Base query
var query = LinqKit.Extensions.AsExpandable(DBContext.Table1.Where(c => c.Field1 == param));
// All the various || between the Thickness ranges
var predicate = LinqKit.PredicateBuilder.False<Table1>();
foreach (double th in Thicknesses)
{
// Don't want a closure around th
double th2 = th;
predicate = predicate.Or(c => th2 >= c.MinThickness && th2 <= c.MaxThickness);
}
// This is implicitly in && with the other Where
query = query.Where(predicate);
return query.ToList();
}
The PredicateBuilder helps you build an || query. Take it from the LinqKit (source available)
I've tested it with 1000 parameters (but they where DateTime, and I didn't have other query pieces), and it seems to work. Note that the program uses another extension of LinqPad, AsExpandable, used to make the PredicateBuilder "trick" work. Note that I'm using EF 6.1.3, so your mileage may vary.
If you don't want to use LinqKit, I'm appending my version of PredicateBuilder. It doesn't require the use of AsExpandable(), but its syntax is slightly different:
public class PredicateBuilder<T>
{
// We share a single parameter for all the PredicatBuilder<T>
// istances. This isn't a proble, because Expressions are immutable
protected static readonly ParameterExpression Parameter = Expression.Parameter(typeof(T), "x");
protected Expression Current { get; set; }
// Returns an empty PredicateBuilder that, if used, is true
public PredicateBuilder()
{
}
// Use it like this: .Where(predicate) or .Any(predicate) or
// .First(predicate) or...
public static implicit operator Expression<Func<T, bool>>(PredicateBuilder<T> predicate)
{
if (object.ReferenceEquals(predicate, null))
{
return null;
}
// Handling of empty PredicateBuilder
Expression current = predicate.Current ?? Expression.Constant(true);
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(current, Parameter);
return lambda;
}
public static implicit operator PredicateBuilder<T>(Expression<Func<T, bool>> expression)
{
var predicate = new PredicateBuilder<T>();
if (expression != null)
{
// Equivalent to predicate.Or(expression)
predicate.And(expression);
}
return predicate;
}
public void And(Expression<Func<T, bool>> expression)
{
if (expression == null)
{
throw new ArgumentNullException("expression");
}
var expression2 = new ParameterConverter(expression.Parameters[0], Parameter).Visit(expression.Body);
this.Current = this.Current != null ? Expression.AndAlso(this.Current, expression2) : expression2;
}
public void Or(Expression<Func<T, bool>> expression)
{
if (expression == null)
{
throw new ArgumentNullException("expression");
}
var expression2 = new ParameterConverter(expression.Parameters[0], Parameter).Visit(expression.Body);
this.Current = this.Current != null ? Expression.OrElse(this.Current, expression2) : expression2;
}
public override string ToString()
{
// We reuse the .ToString() of Expression<Func<T, bool>>
// Implicit cast here :-)
Expression<Func<T, bool>> expression = this;
return expression.ToString();
}
// Small ExpressionVisitor that replaces the ParameterExpression of
// an Expression with another ParameterExpression (to make two
// Expressions "compatible")
protected class ParameterConverter : ExpressionVisitor
{
public readonly ParameterExpression From;
public readonly ParameterExpression To;
public ParameterConverter(ParameterExpression from, ParameterExpression to)
{
this.From = from;
this.To = to;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (node == this.From)
{
node = this.To;
}
return base.VisitParameter(node);
}
}
}
public static class PredicateBuilder
{
// The value of source isn't really necessary/interesting. Its type
// is :-) By passing a query you are building to Create, the compiler
// will give to Create the the of the object returned from the query
// Use it like:
// var predicate = PredicateBuilder.Create<MyType>();
// or
// var predicate = PredicateBuilder.Create(query);
public static PredicateBuilder<T> Create<T>(IEnumerable<T> source = null)
{
return new PredicateBuilder<T>();
}
// Useful if you want to start with a query:
// var predicate = PredicateBuilder.Create<MyType>(x => x.ID != 0);
// Note that if expression == null, then a new PredicateBuilder<T>()
// will be returned (that by default is "true")
public static PredicateBuilder<T> Create<T>(Expression<Func<T, bool>> expression)
{
// Implicit cast to PredicateBuilder<T>
return expression;
}
}
Use it like:
var predicate = PredicateBuilder.Create(query);
and then everything is the same (but remove the LinqKit.Extensions.AsExpandable part)
We have implemented a security layer around our NHibernate persistence layer in a way that hopes to prevent a user from even receiving an object back from the database if he shouldn't have access to it. That security layer looks like this:
public static IQueryable<T> Secure<T>(this Queryable<T> query){
//if T does not implement ISecurable, then return query
//else
return query.Where(expressionFactory.GetExpression(securityKey));
}
We essentially restrict access to our ISession by wrapping it with a decorator that calls ISession.Query().Secure().
So we have numerous types that return an Expression<Func<T, bool>>, such that we can pass it to Where():
public class DocumentSecurityExpressionFactory : ISecurityExpressionFactory<Document> {
public Expression<Func<Document, bool>> GetExpression(SecurityKey key) {
return doc => doc.MasterDocument.Compartments.Where(c => c.AssociatedCompartment.Type != ProgramTypes.AccessGroup) //Look at non-access group compartments for access
.All(c => key.Compartments.Contains(c.AssociatedCompartment.ID))
&& (
//person has to be either NTK
doc.MasterDocument.NeedToKnowAccessList.Count() == 0
|| doc.MasterDocument.NeedToKnowAccessList.Any(p => p.PersonID == key.PersonID)
|| doc.MasterDocument.NeedToKnowAccessList.Any(p => key.AccessGroups.Contains(p.CompartmentID))
);
}
}
public class DocumentSummarySecurityExpressionFactory : ISecurityExpressionFactory<DocumentSummary> {
public Expression<Func<DocumentSummary, bool>> GetExpression(SecurityKey key) {
return doc => doc.MasterDocument.Compartments.Where(c => c.AssociatedCompartment.Type != ProgramTypes.AccessGroup)
.All(c => key.Compartments.Contains(c.AssociatedCompartment.ID))
&& (
doc.MasterDocument.NeedToKnowAccessList.Count() == 0
|| doc.MasterDocument.NeedToKnowAccessList.Any(p => p.PersonID == key.PersonID)
|| doc.MasterDocument.NeedToKnowAccessList.Any(p => key.AccessGroups.Contains(p.CompartmentID))
);
}
}
public class LatestDocumentVersionSecurityExpressionFactory : ISecurityExpressionFactory<LatestDocumentVersion> {
public Expression<Func<LatestDocumentVersion, bool>> GetExpression(SecurityKey key) {
return version => version.BaseDocument.MasterDocument.Compartments.Where(c => c.AssociatedCompartment.Type != ProgramTypes.AccessGroup)
.All(c => key.Compartments.Contains(c.AssociatedCompartment.ID))
&& (
version.BaseDocument.MasterDocument.NeedToKnowAccessList.Count() == 0
|| version.BaseDocument.MasterDocument.NeedToKnowAccessList.Any(p => p.PersonID == key.PersonID)
|| version.BaseDocument.MasterDocument.NeedToKnowAccessList.Any(p => key.AccessGroups.Contains(p.CompartmentID))
);
}
}
And there are actually several more for different types that look just like this.
The problem here should be clear: each of our entities that does this is essentially the same. They each have a reference to a MasterDocument object, on which all the logic is done. Repeating this code totally sucks (and it all sits in one file so they can all change together if they ever do).
I feel like I should be able to just tell a method how to get the MasterDocument from type T, and then have a generalized method that builds the expression. Something like this:
public static class ExpressionFactory {
public static Expression<Func<T, bool>> Get<T>(Expression<Func<T, MasterDocument>> mdSource, SecurityKey key) {
return t => {
var md = mdSource.Compile()(t);
return md.Compartments.Where(c => c.AssociatedCompartment)...
};
}
}
And call it like so:
public class DocumentSecurityExpressionFactory : ISecurityExpressionFactory<Document> {
public Expression<Func<Document, bool>> GetExpression(SecurityKey key) {
return ExpressionFactory.Get<Document>(doc => doc.MasterDocument, key);
}
}
Now, I understand why this code doesn't work. What I can't figure out is how to build up this expression tree correctly in order to vastly simplify our code. I imagine I could pass in the Expression<Func<T, MasterDocument>> mdSource like that and then use the Expression API to build it out with MemberAccessExpressions and such, but I'm anticipating the mess that would look like, and I'm not sure what would be the lesser evil.
Any help is greatly appreciated.
What you can do is use a Compose method that can compose one expression with another:
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);
}
Which uses 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);
}
}
Now you can write:
public static class ExpressionFactory
{
public static Expression<Func<T, bool>> Get<T>(
Expression<Func<T, MasterDocument>> mdSource, SecurityKey key)
{
return mdSource.Compose(document =>
document.Compartments.Where(c => c.AssociatedCompartment.Type != ProgramTypes.AccessGroup)
.All(c => key.Compartments.Contains(c.AssociatedCompartment.ID))
&& (
doc.MasterDocument.NeedToKnowAccessList.Count() == 0
|| doc.MasterDocument.NeedToKnowAccessList.Any(p => p.PersonID == key.PersonID)
|| doc.MasterDocument.NeedToKnowAccessList.Any(p => key.AccessGroups.Contains(p.CompartmentID))
);
}
}
I want a method that operates similarly to following LINQ-to-SQL Where method.
db.Where(r => r.Age == 42 && r.Name = "Joe");
I would like to take an implementation of:
Something something = Get<Something>()
.FilterBy(x => x.PropOne == 123 && x.PropTwo == 456);
and using a method like below; have the ability to access the Left.Name, Operator and Right.Value of each expression argument to perform work.
public static T FilterBy<T>(this T something, Expression<Func<T, object>> e)
{
// do work here
}
So far I can parse single condition like x => x.PropOne == 123 without the use of && or || using the below.
public static T FilterBy<T>(this T something, Expression<Func<T, object>> e)
{
BinaryExpression binaryExpression =
(BinaryExpression)((UnaryExpression)e.Body).Operand;
string left = ((MemberExpression)binaryExpression.Left).Member.Name;
string right = binaryExpression
.Right.GetType().GetProperty("Value")
.GetValue(binaryExpression.Right).ToString();
ExpressionType #operator = binaryExpression.NodeType;
return (T)Convert.ChangeType(something, typeof(T));
}
How can I access the each of the expression arguments when parts of expression combined with && or ||?
Example Code
Here is an example of how to get the &&:
public static class FilterExtension
{
public class Condition
{
public string Name { get; set; }
public string Value { get; set; }
public string Operator { get; set; }
}
public static T FilterBy<T>(this T something, Expression<Func<T, object>> e)
{
var conditions = new List<Condition>();
BinaryExpression binaryExpression =
(BinaryExpression)((UnaryExpression)e.Body).Operand;
CheckConditions(conditions, binaryExpression);
return (T)Convert.ChangeType(test, typeof(T));
}
private static void CheckConditions(List<Condition> conditions, BinaryExpression binaryExpression)
{
if (binaryExpression.NodeType == ExpressionType.AndAlso)
{
CheckConditions(conditions, binaryExpression.Left as BinaryExpression);
CheckConditions(conditions, binaryExpression.Right as BinaryExpression);
}
else
{
conditions.Add(GetCondition(binaryExpression));
}
}
private static Condition GetCondition(BinaryExpression binaryExpression)
{
var condition = new Condition();
condition.Name = ((MemberExpression)binaryExpression.Left).Member.Name;
condition.Value = binaryExpression
.Right.GetType().GetProperty("Value")
.GetValue(binaryExpression.Right, null).ToString();
condition.Operator = binaryExpression.NodeType.ToString();
return condition;
}
}
Obviously the code for && is not complete; you should get an idea.
Also, I noticed that you pass in an object of type T. If this were to work like Linq-To-SQL where, it probably should be some kind of list or enumerable.
Example:
public static T FilterBy<T>(this List<T> something, Expression<Func<T, object>> e)
Considerations
Just as a note of caution, a lot of different expressions can be passed into this method that will compile but will break your method.
Something something = Get<Something>().FilterBy(x => 123 == x.PropOne);
Something something = Get<Something>().FilterBy(x => x.Method() == 123);
Something something = Get<Something>().FilterBy(x => (x.PropOne == 123 || x.PropOne == 34) && x.PropTwo == 456 );
Make sure to verify that the expression is the type that you are expecting it to be.
I frequently have code like this:
var stRecs = db.<someTable>
.Where(a => a.DepID == depID)
to select a single record, however if depID == 0 I would like to get back all records.
I was thinking about creating an extension method "WhereDepID_OrAll", like
public static IQueryable<T> WhereDepID_OrAll<T>(this IQueryable<T> source)
where T: // is what?
{
if(depID > 0) { return source.Where(a => a.depID == depID); }
else return source.Where(a => a);
}
Now my basic question is:
I have several tables with depID - how do I set the Where T: ?
How would the method determine, whether the table has depID?
A better approach to the underlying problem?
At first glance, the reaction would be : create an interface
public interface ObjectWithDepartmentInterface {
int depID;
}
make all the entities using this depId implementing this interface, and use
where T : ObjectWithDepartmentInterface
but linq to entities doesn't accept properties from interfaces in query... See for example : Expression generated based on interface
So the only way would to make your entities with a depId inheriting from a common entity (probably abstract) with a depId property.
And use this abstract entity as the
where T:
An easier (but uglier way) could be to not add a constraint on T, build the predicate in the method, and throw an exception in bad cases.
if (typeof(T).GetProperty("depId") == null)
throw InvalidOperationException (string.Format("{0}" doesn't have a depId property, typeof(T).Name))
EDIT
But maybe it's not a problem of depId as a common property
Then
public static IQueryable<T> WhereExistsOrAll<T>(this IQueryable<T> source, string propertyName, int value)
where T: // is what?
{
if (value == 0)
return source;
var parameter = Expression.Parameter(typeof(T), "m");
Expression member = parameter;
member = Expression.Property(member, propertyName);
member = Expression.Equals(member, Expression.Constant(value));
var lambda = Expression.Lambda<Func<T, bool>>(member, new[]{parameter});
return source.Where(lambda);
}
usage
var stRecs = db.<someTable>.WhereExistsOrAll("depId", depId);
EDIT 2
Another way would be to parse the Predicate to get the "constant" value
something like that
public static IQueryable<T> GetAllOrRestrict<T>(this IQueryable<T> queryable, Expression<Func<T, bool>> predicate)
{
var expression = predicate.Body as BinaryExpression;
var rightPart = expression.Right as MemberExpression;
var value = GetValue(rightPart);
var test = value.ToString();
int val;
if (Int32.TryParse(value.ToString(), out val))
{
if (val != 0)
return queryable.Where(predicate);
}
return queryable;
}
private static object GetValue(MemberExpression member)
{
var objectMember = Expression.Convert(member, typeof(object));
var getterLambda = Expression.Lambda<Func<object>>(objectMember);
var getter = getterLambda.Compile();
return getter();
}
usage
var stRecs = db.<someTable>.GetAllOrRestrict(m => m.depID == depId);
I know it's not particularly fashionable, but isn't this exactly what the Query Builder methods in Entity Framework are for?
var stRecs = db.<someTable>
.Where("it.DepID == #depID OR #depID = 0",
new ObjectParameter("depID", depID));
This works on any someTable such that it has a column named DepID. It can of course be made an extension method:
public static ObjectQuery<T> WhereIdEqualOrAll<T>(this ObjectQuery<T> q, int depID)
where T : class
{
return q.Where("it.DepID = #id OR #id = 0", new ObjectParameter("id", id));
}
to be invoked thus:
var stRecs = db.<someTable>.WhereIdEqualOrAll(depID);
Use an interface:
public interface IInterface
{
int depId;
}
Which will force T to inherit from IInterface and implement depId.
Then you can add it to the extension:
public static IQueryable<T> WhereDepID_OrAll<T>(this IQueryable<T> source) where T: IInterface
{
}
Use an interface, then build an expression tree manually, referencing the actual class.
Raphaels Edit 2 did the job!
small edition:
How could I include NULL-Values if a DepID is present?
I like to return all Departments with ID == x OR ID == NULL
Maybe with an additional bool includeNullValues)
public static IQueryable<T> GetAllOrRestrict<T>(this IQueryable<T> queryable,
Expression<Func<T, bool>> predicate,
bool includeNullValues)
{
var expression = predicate.Body as BinaryExpression;
var rightPart = expression.Right as MemberExpression;
var value = GetValue(rightPart);
var test = value.ToString();
int val;
if (Int32.TryParse(value.ToString(), out val))
{
if (val != 0)
{
if (includeNullValues)
{
var parameter = Expression.Parameter(typeof(T),"m");
return queryable.Where(predicate) <====HOW to " || depID == null) ???
}
else
{
return queryable.Where(predicate);
}
}
}
return queryable;
}