0

I have the following linq which i want to pass different conditions to the where clause,but i get error:

 var rslt = (from t in cr.faultStatAlt
                   join v in cr.errorCatGroup
                   on t.m_error_id equals v.m_error_id
                   join h in cr.masterAlarm on t.m_inv_error_details equals h.pc_group_pattern into ps
                   from h in ps.DefaultIfEmpty()
                   join s in cr.MasterDataTurbine on t.m_turbine_id equals s.m_turbine_id
                   where (whereClause)
                   group t.error_duration by v.m_error_subgroup_name into k
                   select new faulttest
                   {
                       m_error_subgroup = k.Key,
                       error_duration = Math.Round(k.Sum() / 3600, 2)


                   }).ToString();

the condition i want to make it dynamic is :

 Func<t_fault_stat_alt, bool> whereClause = t => t.m_date >= dt1 && t.m_date <= dt2 && t.m_grid_loss==true;
ArunPratap
  • 4,816
  • 7
  • 25
  • 43

2 Answers2

1

You can't pass a lambda into where in query syntax - you'll need to use method syntax instead. Something like

   var fltr = (from t in cr.faultStatAlt
               join v in cr.errorCatGroup
               on t.m_error_id equals v.m_error_id
               join h in cr.masterAlarm on t.m_inv_error_details equals h.pc_group_pattern into ps
               from h in ps.DefaultIfEmpty()
               join s in cr.MasterDataTurbine on t.m_turbine_id equals s.m_turbine_id)
              .Where (whereClause);

   var rslt = (from t in fltr
               group t.error_duration by v.m_error_subgroup_name into k
               select new faulttest
               {
                   m_error_subgroup = k.Key,
                   error_duration = Math.Round(k.Sum() / 3600, 2)


               }).ToString();

Explanation -

Query syntax is just a shortcut the compiler provides for you. The entire query is rewritten using LINQ method calls when it's compiled. For where clauses, the compiler requires a statement which evaluates to bool - the compiler converts that statement into a lambda.

You gave the compiler a lambda to start with (Func<t_fault_stat_alt, bool>) - it doesn't know how to convert that to a bool.

just.another.programmer
  • 8,579
  • 8
  • 51
  • 90
  • 1
    You certainly can use it in query syntax, you just have to invoke it with the correct parameter: `where whereclause(t)` – pinkfloydx33 Oct 15 '18 at 08:09
  • You may want to note what i added as answer to warn people for using this when not appropriate (eg. with *EntityFramework*). – X39 Oct 15 '18 at 08:41
  • Ohh ... also, you can pass a `Func` into a LINQ where.. but you need to actually call it for that to work `from t in ... where whereClause(t) ...` – X39 Oct 15 '18 at 08:43
  • @pinkfloydx33 There's a critical difference between the two - with `where whereclause(t)`, the compiler will write a MethodCallExpression to call the `whereclause(t)` with the ParameterExpression t. With `.Where(whereclause)`, you get just the predicate. **This is critical** b/c many query providers (like LINQ to SQL and LINQ to EF) do not support MethodCallExpression's. – just.another.programmer Oct 15 '18 at 11:04
  • @X39 You only have that problem when you add a method call to a query syntax. – just.another.programmer Oct 15 '18 at 11:05
  • @just.another.programmer in both cases the filter is a `Func<>` and *not* an `Expression>` and the `Enumerable.Where` method will be called. Query providers cannot translate it regardless. Also the OP is not tagged EF. – pinkfloydx33 Oct 15 '18 at 11:10
  • @pinkfloydx33 That's only half true. If `cr.faultStatAlt` has an EF or LINQ to SQL query provider, it will run the first part of the query in DB and the second part will move to memory (LINQ to Objects). If you call the method directly in the query, it will try to translate to SQL and crash. B/c the difference is so subtle, I think it's best to teach the "full proof" way. – just.another.programmer Oct 15 '18 at 11:26
0

Even though the question already has been marked as correct, i need to note something important that may get lost in the comments.

If you use Entity Framework (or anything else that is accepting Expression rather then Func), you need to use different types.

Given the following code where Tag is a random object containing the int field Id (+ its corresponding column) and cntxt is the Entity Framework DBContext, you would yield the following two SQL queries:

Func<Data.Net.Tag, bool> cond = (tag) => tag.Id == 1;
var query = cntxt.Tags.Where(cond);
query.ToArray();
// SELECT "Extent1"."Id", "Extent1"."Name" FROM "public"."Tags" AS "Extent1"

query = cntxt.Tags.Where((tag) => tag.Id == 1);
query.ToArray();
// SELECT "Extent1"."Id", "Extent1"."Name" FROM "public"."Tags" AS "Extent1" WHERE 1 = "Extent1"."Id"

Reason being that Entity Framework is using IQueryable to actually build those SQL queries.

Due to IQueryable also implementing IEnumerable however, you may fall into the trap of expecting the same behavior for Func and Expression due to the looks. If one actually checks the methods themself, the difference gets clearer:

public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);

To still get the same behavior (Filter-Method), you have to change the method head to something like this:

void MyWhere<T>(..., Expression<Func<T, bool>> cond)

For more on this, check out eg.: Why would you use Expression<Func<T>> rather than Func<T>? or google C# Expression :)

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
X39
  • 789
  • 6
  • 22
  • 1
    I would be surprised, if you can a supply a `Func`, where `Expression` is expected, this shall be a compilation error. There's no reason to replace Expression by Func, just that both have similar syntax using Lambda for simple queries, but they work quite differently – Mrinal Kamboj Oct 15 '18 at 10:10
  • The moment, something implements `IQueryable`, you may experience that situation where both are accepted (eg. `public static IQueryable Where(this IQueryable source, Expression> predicate)` vs `public static IEnumerable Where(this IEnumerable source, Func predicate);`). – X39 Oct 15 '18 at 10:36
  • 1
    though ... you are correct in that my reasoning was pretty much BS ... will fix that (gonna put that one on the lack of morning coffee) – X39 Oct 15 '18 at 10:45