0

Consider the following Func which takes in the DbSet and returns the result contract:

public static Func<Repository.DataSets.Person, Person> Map(IPersonGetOptions options)
{
    return k => new Person()
    {
        ID = k.ID,
        Department = k.Department == null ? null :  new Department()
        {
           ID = k.Department.ID,
        };
    };
}

Department Func:

public static Func<Repository.DataSets.Department, Department> MapDepartment()
{
    return k => new Department()
    {
        ID = k.ID,
    };
}

Usage:

 public Person GetById(int id, PersonGetOptions options = null)
 {
     options ??= new();

     var result = db.Persons
         .Select(Map(options))
         .FirstOrDefault(k => k.ID == id);

     return result;
 }

 // similar for department: ... db.Departments.Select(Map()) ...

I want to reuse MapDepartment in Map in a way EF can translate (I don't want to use AutoMapper).

What I tried:

public static Func<Repository.DataSets.Person, Person> Map(IPersonGetOptions options)
{
    return k => new Person()
    {
        ID = k.ID,
        Department = k.Department == null ? null : MapDepartment().Invoke(k.Department)
    };
}

This code block won't work as it cannot be translated by efcore (v5). Any idea how to make this work?

With @Svyatoslav Danyliv comment linked to a similar post, What I tried:

With LINQkit, I was able to accomplish a reusable Func without arguments. but it did not seem to be working when adding arguments like options in my example:

This works:

[Expandable(nameof(Map))]
private static Offer MapPerson(Repository.DataSets.Person k)
{
    _map ??= Map().Compile();

    return _map(k);
}

private static Func<Repository.DataSets.Person, Person> _map;

public static Expression<Func<Repository.DataSets.Person, Person>> Map(){
 ...
}

// usage
```cs
.Select(k => MapPerson(k))

But when introducing an argument options, it will throw the following:

No method 'Map' on type 'my-namespace.my-class' is compatible with the supplied arguments

Failed code:

private static Offer MapPerson(IPersonGetOptions options, Repository.DataSets.Person k){
    _map ??= Map(options).Compile();

    return _map(k);
}

private static Func<Repository.DataSets.Person, Person> _map;

public static Expression<Func<Repository.DataSets.Person, Person>> Map(IPersonGetOptions options){ 
 ...
}
Ali Kleit
  • 3,069
  • 2
  • 23
  • 39
  • Does this answer your question? [Can I reuse code for selecting a custom DTO object for a child property with EF Core?](https://stackoverflow.com/questions/66378438/can-i-reuse-code-for-selecting-a-custom-dto-object-for-a-child-property-with-ef) – Svyatoslav Danyliv May 29 '22 at 13:42
  • You have to use `Expression>`. Only via Expression you can generate effective mapping. – Svyatoslav Danyliv May 30 '22 at 04:54

1 Answers1

1

You can use LINQKit for such task:

[Expandable(nameof(MapPersonImpl))]
public static Person MapPerson(Repository.DataSets.Person p)
{
    throw new NotImplementedException();
}

private static Expression<Func<Repository.DataSets.Person, Person>> MapPersonImpl()
{
    return p => new Person()
    {
        ID = p.ID,
        Department = p.Department == null ? null : MapDepartment(p.Department)
    };
}

[Expandable(nameof(MapDepartmentImpl))]
public static Department MapDepartment(Repository.DataSets.Department d)
{
    throw new NotImplementedException();
}

public static Expression<Func<Repository.DataSets.Department, Department>> MapDepartmentImpl()
{
    return d => new Department()
    {
        ID = d.ID,
    };
}

Don't forget to enable LINQKit extension:

builder
    .UseSqlServer(connectionString)
    .WithExpressionExpanding(); // enabling LINQKit extension
Svyatoslav Danyliv
  • 21,911
  • 3
  • 16
  • 32
  • As mentioned, this works, but without the parameter `Options`. (also just tried the code in your answer but the same error) – Ali Kleit May 30 '22 at 18:57
  • Well, it will not work in such case because it may break EF Core LINQ query caching. It needs investigation before implementation. – Svyatoslav Danyliv May 30 '22 at 19:06