I am trying to have a select expression that can be incrementally updated depending on what I receive from the input, something like this:
// Init expression
Expression<Func<Order, object>> selectExpression = x => new
{
x.Id
};
if(selectInput.Title){
// something like this
selectExpression = selectExpression.Add(x => new
{
x.Title
});
}
if(selectInput.ClientFullName){
// something like this
selectExpression = selectExpression.Add(x => new
{
ClientFullname = x.Client.Fullname
});
}
// Use expression in a LINQ query (for an EF Core query)
var result = await queryable.Select(selectExpression).ToListAsync();
Then I would expect to have a result like this
{
"Id": 568,
"Title": "Order 567",
"ClientFullname": "John Smith"
}
Is this something that is possible? The only examples I found online are about .Where(). Am I going into the wrong direction?
Thank you!
If you would do this, then the problem is, that your compiler would not know whether property ClientFullName
is a property in your result or not, thus your compiler can't use it.
Of course you can use the name of the property as an identifier, and put everything in a Dictionary. like @Canton7 suggested.
If you don't expect null values, you can return a class with all properties. The properties that have value null will not have been selected:
class MyItem
{
public int Id {get; set;} // Id always fetched
public string Title {get; set;}
public DateTime? Date {get; set;}
... // etc: all nullable properties
}
As an extension method:
public static IQueryable<MyItem> ToSelectedItems(
this IQueryable<Order> orders,
SelectedInput selectedInput)
{
// TODO: exception if orders or selectedInput null
return orders.Select(order => new MyItem
{
Id = order.Id,
// properties null if not in selectedInput
Title = selectedInput.Title ? order.Title : null,
ClientFullName = selectedInput.ClientFullName ? order.ClientFullName : null,
Date = selectedInput.Date ? order.Date : null,
})
Usage would be:
// We only want the Title and the Date (for example)
SelectedInput propertiesToFetch = new SelectedInput
{
Title = true,
Date = true,
// all other properties are automatically false
}
// the query to fetch the orders that you want (not executed yet!)
IQueryable<Order> myOrders = dbContext.Orders
.Where(order => ...)
...;
// Select only the properties that you want
IQueryable<MyItem> myQuery = myOrders.ToSelectedItems(propertiesToFetch);
The query is still not executed. To execute the query:
List<MyItem> myFetchedData = myQuery.ToList();
Of course you can put the complete query in one big LINQ statement. This does hardly influence efficiency. However it will deteriorate readability:
var myFetchedData = dbContext.Orders
.Where(order => ...)
.ToSelectedData(new SelectedInput
{
Title = true,
Date = true,
})
.ToList();