3

I feel like I am missing something simple, but I have not found the documentation that answers my question.

I have recently been decomposing some of the linq projections into reusable expressions. It works great when operating on a collection, but I can't seem to figure out how to apply an expression to a single object in another expression. Below is an example of what I am trying to accomplish:

public class Person
{
    public string ID { get; set; }
    public string Name { get; set; }
}

public class PersonDto
{
    public string ID { get; set; }
    public string Name { get; set; }
}

public class Department
{
    Person Manager { get; set; }
    List<Person> Employees { get; set; }
}

public class DepartmentDto
{
    PersonDto Manager { get; set; }
    List<PersonDto> Employees { get; set; }
}

public Expression<Func<Person, PersonDto>> CreatePersonDto = p => new PersonDto
{
    ID = p.ID,
    Name = p.Name
};

public Expression<Func<Department, DepartmentDto>> CreateDepartmentDto = d => new DepartmentDto
{
    Manager = d.Manager // How do I transform this `Person` using `CreatePersonDto`
    Employees = d.Employees.Select(CreatePersonDto) //Does not work either
};

EDIT: To be clear, I am using Linq-to-Entities that needs to use this Expression to generate a SQL statement. As a result, I cannot Compile the expression to a Func as I might be able to using Linq-to-Objects.

Gene C
  • 2,010
  • 26
  • 34
  • `CreatePersonDto` transforms a `Person` into a `PersonDto`, but `Manager` type is `Person`, you will not be able to assign a `PersonDto` to a `Person` variable (that is what you want to do in `Manager = // How do I....`) – Matteo Umili Sep 25 '15 at 14:36
  • There was an error in my example. I have modified it. DepartmentDto should be composed of a PersonDto and a List. Thank you for pointing it out @codroipo – Gene C Sep 25 '15 at 14:41
  • 1
    Does the 2nd line, `Employees = d.Employees.Select(CreatePersonDto)`, give you any errors? It does for me. IIRC it is not possible to use expressions inside of expressions that easily. You have to manually create the expression using the static methods on the `Expression` class. – Mayoor Sep 25 '15 at 14:43
  • @Mayoor You are probably correct. Now that I have taken a closer look at my actual source code (and not my hastily created example), I see that I am passing the equivalent of `CreatePersonDto` to a `Select` outside of an expression on a DbSet. Example: `dbSet.Select(CreatePersonDto)`. It does not appear to work within the context of an expression. – Gene C Sep 25 '15 at 14:51
  • It is unfortunate that there does not seem to be an easier way of composing these expressions for reuse. – Gene C Sep 25 '15 at 14:53
  • what's wrong with using `Compile` as some answer suggested? That's a way to convert LambdaExpression to a Func<> and then you can use that Func to Select on a List<>. That can also be used to transform single value for Manager. – Hopeless Sep 25 '15 at 15:09
  • 2
    The problem that I am having with using `Compile` is that while it will work with Linq to Objects, it does not work with Linq to Entities. Linq to Entities needs to evaluate an expression that ultimately translates to a SQL query. It is unable to evaluate a `Func` to sql – Gene C Sep 25 '15 at 15:14
  • @GeneC oh I see, I did not notice the return value of your method. – Hopeless Sep 25 '15 at 15:16

2 Answers2

3

You can use LINQKit to expand the expressions that you have within other expressions:

private static Expression<Func<Department, DepartmentDto>> CreateDepartmentDtoUnexpanded = d => new DepartmentDto
{
    Manager = CreatePersonDto.Invoke(d.Manager),
    Employees = d.Employees.Select(employee => CreatePersonDto.Invoke(employee))
        .ToList(),
};
public static Expression<Func<Department, DepartmentDto>> CreateDepartmentDto = CreateDepartmentDtoUnexpanded.Expand();
Servy
  • 202,030
  • 26
  • 332
  • 449
  • 1
    This link is just what I am looking for. I have struggled to articulate the problem, and this page explains it very clearly. It also provides a solution! – Gene C Sep 28 '15 at 16:04
-1

You can:

public static Expression<Func<Department, DepartmentDto>> CreateDepartmentDto = d =>
     new DepartmentDto
        {
            Manager = CreatePersonDto.Compile()(d.Manager),
            Employees = d.Employees.Select(CreatePersonDto.Compile()).ToList() // Or declare Employees as IEnumerable and avoid ToList conversion
        };

Clearly, instead of calling two times Compile method you can store the compiled Func

Matteo Umili
  • 3,412
  • 1
  • 19
  • 31
  • 1
    this won't work for the OP (you can see his comment above). However he did not even comment anything to complain about that (until I asked him in my comment). That indicates an ***unrespectful*** attitude and I don't like that at all. So I've just deleted my answer (which surely works). You should also delete this answer, no way to edit as well as keep it here. He might think everyone here was ignorant or just nooby. My knowledge about LinqToEntity is good enough so that I did not have to ask him why it would not work, it was just a problem of my eyes. – Hopeless Sep 28 '15 at 01:30
  • @Hopeless I agree with you about deleting for the OP's unrespectful attitude, but I think that if someone come in this page because he has the problem originally stated by the OP he can find an answer if I don't delete the post. – Matteo Umili Sep 28 '15 at 07:08
  • if your answer had worked, he would have accepted it. But because it not working, keeping it has no benefit for others. Also the correct answer is fairly simple, someone could try digging in a little to find the correct answer with no much effort (maybe with some team's help). – Hopeless Sep 28 '15 at 08:16
  • @Hopeless @codroipo How do you recommend I proceed with this question? I agree with you that I did not handle it well. I did add a comment on 9/25 about the problem with `Compile` should I add a similar comment to each answer or update my question? Frankly, I feel like the question is fundamentally flawed. – Gene C Sep 28 '15 at 15:18