Search code examples
c#entity-framework-core.net-6.0

Entity Framework how do I nest an Expression<Func<YDTO, Y>> inside another Expression<Func<XDto, X>>


Okay so I have records to be used as DTOs

public record ProviderRecord(int ProviderId, string Name, DateTimeOffset InsertTimeStamp, IEnumerable<ModelRecord> models);
public record ModelRecord(int ModelId, string Name, string Version, DateTimeOffset InsertTimeStamp);

So I know in EF you can do an expression before the EF evaluates like so:

public async Task<IEnumerable<ProviderRecord>> GetAllProviders() => 
        await _context.Providers
            .Include(x => x.Models)
            .ThenInclude(x => x.ModelScores)
            .Select(SelectProvider)  //Works yay!
            .ToListAsync();

private Expression<Func<Provider, ProviderRecord>> SelectProvider = x => new ProviderRecord(x.ProviderId, x.Name, x.InsertTimeStamp, null);

However I swore I used to be able to chain expressions inside another expression like so:

public static Expression<Func<Provider, ProviderRecord>> SelectProvider = x => new ProviderRecord(x.ProviderId, x.Name, x.InsertTimeStamp,
        x.Models.Select(SelectModels) //Doesn't work but I want to pre evaluate?
        //x.Models.Select(y => new ModelRecord(y.ModelId, y.Name, y.Version, y.InsertTimeStamp))  //This work but it's long form inline
        //GetModelRecords(x.Models.ToList())  //Does work but I don't want this
        );

public static List<ModelRecord> GetModelRecords(List<Model> models) => models.Select(model => new ModelRecord(model.ModelId, model.Name, model.Version, model.InsertTimeStamp)).ToList();
public static Expression<Func<Model, ModelRecord>> SelectModels = y => new ModelRecord(y.ModelId, y.Name, y.Version, y.InsertTimeStamp);

Any EF warriors out there that know something. I can do it inline but that is a pain for resuse and visually if I then introduce a third or fourth level of a tree. But I may have to...


Solution

  • Try using AsQueryable on x.Models:

    // ...
      x.Models.AsQueryable().Select(SelectModels)
    

    If this does not work then another option is to use 3rd party library like LINQKit:

    // ...
      x.Models.Select(x => SelectModels.Invoke(x))
    

    And do not forget to call AsExpandable on source IQueryable.