I've been unable to find any examples where you can conditionally exclude fields based off a variable within a select projection in LINQ, see also LINQ: Select an object and change some properties without creating a new object.
Let me give some background on what I'm trying to achieve. I want to limit some fields in the DTO being set from the model based on if a user can edit data (i.e. a comment field). For example, the following select with a delegate named CustomerView.
var qry = _ctx.Customer.Select(CustomerView(User.IsInRole("Editor")));
The Customer model has an Orders navigation property and the following function transforms the data into the CusomerViewModel DTO.
private Expression<Func<Customer, CustomerViewModel>> CustomerView(bool isEditor) {
return c => new CustomerViewModel
{
Id = c.Id,
Name = c.Name,
Comment = isEditor ? c.Comment : null,
OrderCount = c.Orders.Count()
};
}
This will generate SQL like CASE WHEN @__isEditor_0 = TRUE THEN Comment ELSE NULL
which works, but I'd prefer the expression not even be generated, i.e. field left as it's default. That is a simple use case, but if I wanted to do the same with the OrderCount field a SQL subquery would still get included.
Of course I could create a another function for non-editor users that excludes certain fields, but I'd rather not have separate projections to maintain especially when they are more complex.
I see questions where dynamic LINQ is used for where clauses but not that many for select. Is this approach feasible?
Edit: Is there anyway to manually remove fields from an expression tree after a select has been used, maybe through an extension method?
Using LINQKit I was able to achieve the desired result by adding AsExpandable()
to select.
var qry = _ctx.Customer.AsExpandable().Select(CustomerView(User.IsInRole("Editor")));
Then adding an expression for comment field and calling Invoke()
on the field assignment.
private Expression<Func<Customer, CustomerViewModel>> CustomerView(bool isEditor) {
Expression<Func<Customer, string>> exprComment;
if (isEditor)
exprComment = c => c.Comment;
else
exprComment = c => null;
return c => new CustomerViewModel
{
Id = c.Id,
Name = c.Name,
Comment = exprComment.Invoke(c),
OrderCount = c.Orders.Count()
};
}
It does appear to work, but I would still be interested to hear of any alternative approaches.