0

Im trying to write a query function. And with expression trees I am trying to get runtime input to query.

var query = context.Customers            
    .Join(context.Orders,
        customer => customer.CustomerId,
        order => order.CustomerId,
    (customer, order) => new
    {
        customer.ContactName,
        order.ShipCountry
    }).Where(customer => customer.ContactName.StartsWith("A"))
    .ToList();

I created a simple query. Then tried to functionalize it using expression trees:

public static IEnumerable<object> GetContactNameandOrderDate(string prop_1, string prop_2, string prop_3, string text)
{

    NorthwindContext context = new();

    var customer = Expression.Parameter(typeof(Customer));    //customer  
    var order = Expression.Parameter(typeof(Order));          //order

    var customerCustomerId = Expression.PropertyOrField(customer, prop_1);        //customer.CustomerId
    var orderCustomerId = Expression.PropertyOrField(order, prop_1);              //order.CustomerId
    var customerContactName = Expression.PropertyOrField(customer, prop_2);       //customer.ContactName
    var orderShipCountry = Expression.PropertyOrField(order, prop_3);             //order.ShipCountry

    var textConstant = Expression.Constant(text);  //"A"



    Expression<Func<Customer, int>> lambda_1 = Expression.Lambda<Func<Customer, int>>(customerCustomerId, customer);  //customer => customer.CustomerId
    Expression<Func<Order, int>> lambda_2 = Expression.Lambda<Func<Order, int>>(orderCustomerId, order);     //  order => order.CustomerId




    Expression startsWith = Expression.Call(                    //customer.ContactName.StartsWith("A"))
                                customerContactName,
                                typeof(String).GetMethod("StartsWith",
                                            new Type[] { typeof(String) }), textConstant );




    Expression<Func<Customer, bool>> lambda_3 = Expression.Lambda<Func<Customer, bool>>(startsWith, customer); //customer => customer.ContactName.StartsWith("A"))

    


    var values = context.Customers.Join(context.Orders, lambda_1, lambda_2, (customer, order) => new { customerContactName, orderShipCountry }).Where(lambda_3).ToList();  //lambda_3 error
    return values;
}

Im getting error at the Where(lambda_3) that says:

Error CS1503 Argument 2: cannot convert from 'System.Linq.Expressions.Expression<System.Func<expression_trees.Entities.Customer, bool>>' to 'System.Func<<anonymous type: System.Linq.Expressions.MemberExpression customerContactName, System.Linq.Expressions.MemberExpression orderShipCountry>, bool>'

Im guessing I did something wrong writing customer.ContactName.StartsWith("A")) as expression. But I could not figure out what is wrong.

I am essentially trying to figure out if I can use expression trees effectively and dynamic with entity framework. Like getting database objects that has "/" at the beggining in runtime. Is my approach wrong? How can i use expression trees effectively with entity framework? I could not find many resource on this topic so any recommendation would be helpful.

Svyatoslav Danyliv
  • 21,911
  • 3
  • 16
  • 32
  • There is problem with anonymous types while building Expression Tree. I can correct your query but what is the reason to build such simple thing? Maybe you are trying to build something more useful? – Svyatoslav Danyliv Mar 22 '23 at 12:47
  • You could say that. I am researching expression trees to see how i can utilize them with entity framework core . I read that they can be used to create dynamic queries. But I am a bit confused about it. I also could not find real world usage examples so I was trying to create something myself to see where it goes. – charthraxis Mar 22 '23 at 12:56
  • 1
    With Expression Trees you can create any query dynamically. Problem with anonymous types. It is very difficult to create them dynamically (but possible). Usually in such extensions used real class. – Svyatoslav Danyliv Mar 22 '23 at 13:48
  • I see. Do you recommend any resource about this topic or anything useful that I can use? – charthraxis Mar 22 '23 at 16:50
  • I'm self-taught, usually it was Stack Overflow. At the start if was decompiling code which generates C# compiler, like`Expression>>> func = q => q.Join(...)...` Then I have found this useful extension [Readable Expressions](https://marketplace.visualstudio.com/items?itemName=vs-publisher-1232914.ReadableExpressionsVisualizers) to visualise what I have built. – Svyatoslav Danyliv Mar 22 '23 at 17:06
  • Also useful was to look into `Queryable` extensions implementation. They have clue how to build such queries. – Svyatoslav Danyliv Mar 22 '23 at 17:08
  • Thank you. To have a better view on Expressions , may I ask in which area you used Expressions? Also this might be a dumb question but I am very new c#: I saw that if I write a complex query with more than one table , the query is anonymous type, but if I write using only one table the query returns objects of that tables type. Is this the kind of problem you mentioned with anonymous types? – charthraxis Mar 22 '23 at 17:15
  • 1
    Well, `Join`, `SelectMany`, GroupJoin` connects two recordsets into new projection - changing result type (here usually. anonymous type is used). `GroupBy` - always changes result type. We can talk whole day about this. What you are trying to achieve? – Svyatoslav Danyliv Mar 22 '23 at 17:28
  • *may I ask in which area you used Expressions?* - Simplifying extension methods used in application - simple syntax but under hood, several joins or something else like in this [my answer](https://stackoverflow.com/a/72787160/10646316) Also I'm developing [linq2db](https://github.com/linq2db/linq2db) which generates a way better SQL than EF for about ten years. – Svyatoslav Danyliv Mar 22 '23 at 17:32
  • *What you are trying to achieve?* : I just wanted to clarify what you meant by *Problem with anonymous types* above. I checked your linq2db work. Thats very impressive. Do you recommend a newbie to try these libraries like yours or should I stick with EF for now? – charthraxis Mar 22 '23 at 17:48
  • EF Core is good for complex model and processes. ChangeTracker simplifies a lot of things, but if you need really complex queries with Window Functions, temporal tables, fast inserting, `linq2db` beats this monster like a child. I do not suggest to drop EF at all, you can use `linq2db` as a helper via [linq2db.EntityFrameworkCore](https://github.com/linq2db/linq2db.EntityFrameworkCore). – Svyatoslav Danyliv Mar 22 '23 at 17:59
  • An example of what you can do is in [my answer here](https://stackoverflow.com/a/49418695/2557128). It also shows how you can use an example anonymous object to get a type when you know the shape of the anonymous class you need. I would also recommend LINQPad which is a good tool for dumping compiler created `Expression` trees so that you can see how they are built by the compiler. – NetMage Mar 23 '23 at 19:50
  • C# is a language of types. What is the return type from the call to `Join`? What is the parameter type to the `lambda_3` lambda `Expression`? – NetMage Mar 23 '23 at 20:38
  • PS See [this code](https://github.com/zzzprojects/System.Linq.Dynamic.Core/blob/master/src/System.Linq.Dynamic.Core/DynamicClassFactory.cs) from the Dynamic LINQ project to get a partial idea of why creating your own anonymous type is difficult. – NetMage Apr 28 '23 at 20:38

0 Answers0