How to pass variables to Expression Func Projection before compile

.net-core c# entity-framework-core

Question

I am trying to write Expression Functions for my projections. I found a good article about that link. But I couldn't figure out how can I pass variables to these functions.

How can I write a projection function for this one

int i = 3;
var query = await _db.Users
                .FilterByName(name)
                .Select(item => new SearchResultItemViewModel
                {
                    Id = item.Id,
                    Article = item.FirstName + i.ToString()
                });
}))

This one is working. In select SQL string has only Id and Firstname but I can't pass any variable.

   var query = await _db.Users
                .FilterByName(name)
                .Select(item => SearchResultItemViewModel.Projection)
public static Expression<Func<ApplicationUser, SearchResultItemViewModel>> Projection
        {
            get
            {
                return item => new SearchResultItemViewModel
                {
                    Id = item.Id,
                    Article = item.FirstName
                };
            }
        }

This one is working only if you call compile and invoke. SQL string has all rows. Leading to bad performance

   var query = await _db.Users
                .FilterByName(name)
                .Select(item => SearchResultItemViewModel.Projection.Compile().Invoke(item,i))
public static Expression<Func<ApplicationUser,int, SearchResultItemViewModel>> Projection
        {
            get
            {
                return( item,i) => new SearchResultItemViewModel
                {
                    Id = item.Id,
                    Article = item.FirstName + i.ToString()
                };
            }
        }
1
1
6/2/2019 10:53:00 PM

Accepted Answer

I don't use EF so this might vary in your specific use case, but this seems to be a question about LINQ Expressions more than anything else.

The first big problem is that you are trying to use an Expression<Func<ApplicationUser, int, SearchResultItemViewModel>> where you really meant Expression<Func<ApplicationUser, SearchResultItemViewModel>> and that's not going to do what you want. Instead of binding to a variable you're invoking the indexed variant of Select. So instead of getting the same value of i for all rows you get the index of the row.

When you create an expression that references a variable, one of two things happens. For local variables (and parameters) the value is copied to an anonymous class instance which is bound to the expression, so you can't change it afterwards. For other variables the expression contains a reference to the variable itself, as well as the containing object for non-static variables.

Which means that you could in principle use a static variable to alter the parameter and never have to recreate the projection expression. There are certainly times when this is useful.

On the other hand, your code above is creating a new instance each time you access the Projection property. So why not just change it to a function and generate the expression you need when you need it?

public static Expression<Func<ApplicationUser, SearchResultItemViewModel>> Projection(int parm)
    => item => new SearchResultItemViewModel
        {
            Id = item.Id,
            Article = item.FirstName + parm.ToString()
        };

Each time you invoke the method you'll get back an expression that uses the specified value.

Or you could use an expression visitor to take a template expression and modify the constants in it to whatever you need at the time. Fun, but a bit beyond scope here I think.

2
6/3/2019 1:05:10 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow