Passing a method into a LINQ query

In the project I'm working on now, we have a lot of static expressions, we need to bring a local area with a variable when we call the Invoke method on them and pass the arguments to the lambda expression.

Today we announced a static method, the parameter of which is exactly the type that the request expects. So, my colleague and I were busy to see if we can use this method to execute the project in the Select statement of our request, and not call it on the whole object without introducing it into the local area.

And it worked! But we do not understand why.

Imagine code like this

// old way
public static class ManyExpressions {
   public static Expression<Func<SomeDataType, bool> UsefulExpression {
      get {
         // TODO implement more believable lies and logic here
         return (sdt) => sdt.someCondition == true && false || true; 
      }
   }
}

public class ARealController : BaseController {

   /* many declarations of important things */

   public ARealClass( /* many ninjected in things */) {
      /* many assignments */
   }

   public JsonNet<ImportantDataResult> getSomeInfo(/* many useful parameter */) {

      var usefulExpression = ManyExpressions.UsefulExpression;

      // the db context is all taken care of in BaseController
      var result = db.SomeDataType
         .Where(sdt => usefulExpression.Invoke(sdt))
         .Select(sdt => new { /* grab important things*/ })
         .ToList();

      return JsonNet(result);
   }
}

And then you do it!

// new way
public class SomeModelClass {

   /* many properties, no constructor, and very few useful methods */
   // TODO come up with better fake names
   public static SomeModelClass FromDbEntity(DbEntity dbEntity) {
      return new SomeModelClass { /* init all properties here*/ };
   }
}

public class ARealController : BaseController {

   /* many declarations of important things */

   public ARealClass( /* many ninjected in things */) {
      /* many assignments */
   }

   public JsonNet<SomeModelClass> getSomeInfo(/* many useful parameter */) {

      // the db context is all taken care of in BaseController
      var result = db.SomeDataType
         .Select(SomeModelClass.FromDbEntity) // TODO; explain this magic
         .ToList();

      return JsonNet(result);
   }
}

, ReSharper ( , , , ), , . , - , # LINQ, ... .

?

+4
4

, - , , , - . , , , , .

Linq, , .

.NET 1.0 . . , . ( C, ++ ) / ( Javascript ). ​​

:

public delegate int MyDelegate(double someValue, double someOtherValue);

, , , .

- .

public int CompareDoubles(double x, double y)
{
  if (x < y) return -1;
  return y < x ? 1 : 0;
}

MyDelegate dele = CompareDoubles;

dele.Invoke(1.0, 2.0) dele(1.0, 2.0).

, .NET, , CompareDoubles. , , , , public int CompareDoubles(double x, double y, double z){…} , CompareDoubles dele, . , CompareDoubles , double int, CompareDoubles .

, , .

, .NET 2.0, generics, , # 2 , . 2.0, :

MyDelegate dele = delegate (double x, double y)
{
  if (x < y) return -1;
  return y < x ? 1 : 0;
};

# 2, , " " (, .NET, #, # ). , , , , .

, .NET 3.5 - ( ) Func Action ( , , , ), # 3, -.

, .

:

var func = (int i) => i * 2;

var , , , , lamdas , , , .

:

Func<int, int> func = i => i * 2;

:

Func<int, int> func = delegate(int i){return i * 2;};

, , - :

int <>SomeNameImpossibleInC# (int i)
{
  return i * 2;
}
Func<int, int> func = <>SomeNameImpossibleInC#;

:

Expression<Func<int, int>> func = i => i * 2;

:

Expression<Func<int, int>> func = Expression.Lambda<Func<int, int>>(
  Expression.Multiply(
    param,
    Expression.Constant(2)
  ),
  param
);

.NET 3.5 Linq, . , Linq System.Linq.Expressions. , , , - , ( , , ) , .

Linq . IQueryable IQueryable<T> IEnumerable IEnumerable<T>. , "" , "" , .

. a IEnumerable<T> IQueryable<T> AsQueryable, , IQueryable<T> IEnumerable<T>, , IQueryable<T> IEnumerable<T>.

. Select ( , , , , ):

public static IEnumerable<TResult> Select(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
{
  foreach(TSource item in source) yield return selector(item);
}

, , , Expression<TSource, TResult>, , Select , , . , , Select , Select!

, , . SQL, Compile() , Select ..

, . ( , Compile() ). - , . , .

, , .

public string IntString(int num) { return num.ToString(); }

, -:

Enumerable.Range(0, 10).Select(i => IntString(i));

lambda, , , , . , :

public string MyAnonymousMethod(int i){return IntString(i);}

MyAnonymousMethod ; , , IntString(i) , IntString :

Enumerable.Range(0, 10).Select(IntString);

( . ) , . , ReSharper "Convert to Method Group" , , ( ReSharper ).

-, . IQueryable<T> , , (, SQL ). IEnumerable<T> , .NET. ( ) Compile(), : , - , " ", -, SQL.

, -, i => i * 2, IQueryable<T> IEnumerable<T> - , ( , , ). , - Func<> , , , , , . , , linq " ", , .

95% , . , 95% , " " , , ", , . ? ?". 5% , " , ". ( , , , ).

, , , , , , , , " , grok".

+10
Select(SomeModelClass.FromDbEntity)

Enumerable.Select, . "queryable-LINQ" LINQ . , .

.Where(sdt => usefulExpression.Invoke(sdt))

, , .Where(usefulExpression). , . LINQ .

, SQL Profiler , SQL . , .

0

. :

  var result = db.SomeDataType
     .Select(SomeModelClass.FromDbEntity) // TODO; explain this magic
     .ToList();  // <<!!!!!!!!!!!!!

, Entity Framework, "ToList()" " ". "ToList()" .

: , EF:

  • , (, context.Orders)
  • :

.

var query = context.Where(o => o.Customer.Name == "John")
                   .Where(o => o.TxNumber > 100000)
                   .OrderBy(o => o.TxDate);
//I've pulled NO data so far! "var query" is just an object I can pass around
//and even add on to!  For example, I can now do this:

query = query.ThenBy(o => o.Items.Description); //and now I've appended that to my query

, . , , :

    /// <summary>
    /// Generates the Lambda "TIn => TIn.memberName [comparison] value"
    /// </summary>
    static Expression<Func<TIn, bool>> MakeSimplePredicate<TIn>(string memberName, ExpressionType comparison, object value)
    {
        var parameter = Expression.Parameter(typeof(TIn), "t");
        Expression left = Expression.PropertyOrField(parameter, memberName);
        return (Expression<Func<TIn, bool>>)Expression.Lambda(Expression.MakeBinary(comparison, left, Expression.Constant(value)), parameter);
    }

:

public GetQuery(string field, string value)
{
    var query = context.Orders;
    var condition = MakeSimplePredicate<Order>(field, ExpressionType.Equal, value);
    return query.Where(condition);
}

, . . , ToList().

!

, , , . Linq

0

, . " ".

VS. , IQueryable<T> "" IEnumerable<T>, Queryable , Enumerable, , Expression<Func<...>>, Func<..>.

, Func method group over IQueryable<T>, Enumerable, , LINQ to Entities LINQ to Objects. - , - .

The key is to stay as long as possible in context IQueryable<T>, so the β€œold way” is preferred. For example. from your examples

.Where(sdt => sdt.someCondition == true && false || true)

or

.Where(ManyExpressions.UsefulExpression)

or

.Where(usefulExpression)

but not

.Where(sdt => usefulExpression.Invoke(sdt))

And never

.Select(SomeModelClass.FromDbEntity)
0
source

Source: https://habr.com/ru/post/1628908/


All Articles