0

I have the following two classes:

public Part {
    public string PartNumber {get; set;}
    public string Description {get; set;}
    public List<Warehouse> Warehouses {get; set;}
}

public Warehouse {
    public string PartNumber {get; set;}
    public string WarehouseName {get; set;}
    public int Quantity {get; set;}
    public int ReorderPoint {get; set;}
}

Using Entity Framework Core 2.0 I have associated these using a one to many relationship. Using Dynamic Linq Core I'm trying to create a query that returns the PartNumber, Description, and the list of all associated Warehouses for a particular part where the only property in the Warehouses list is WarehouseName ideally like this:

List<string> fields = new List<string> {"PartNumber", "Description", "Warehouses.WarehouseName"};
var _dataSet = dbContext.Parts.Include(x => x.Warehouses);
var data = _dataSet.Where("PartNumber = \"Part1234\"").Select("new (" + String.Join(",", fields) + ")").ToDynamicArray();

But I receive this error: "No property or field 'Warehouse' exists in type 'List`1'". If I do something like this it works fine:

var data = _dataSet.Where("PartNumber = \"Part1234\"").Select(x => new Part
{
    PartNumber = x.PartNumber,
    Description = x.Description,
    Warehouses = x.Warehouses.Select(y => new Warehouse { Warehouse = y.Warehouse }).ToList()
}).Single();

The problem is that I would like it to be dynamic so that the user can just pass in a list of fields from the Part and Warehouse class that they want to get without having to modify the select to build it for those specific fields.

jpico
  • 41
  • 1
  • 3
  • Try this as a test in your `fields`: `Warehouses[0].WarehouseName`. If that works then, you need to add the index for the other ones too. – CodingYoshi Apr 13 '18 at 19:39
  • @CodingYoshi This returns a valid data object, but the WarehouseName is only a single field it isn't a list. How would I add the index to return all warehouses (there could be 0 to 100)? – jpico Apr 13 '18 at 19:44

1 Answers1

0

You'd need to support a subquery on Warehouses. I'll copy the relevant steps listed in this answer:

  1. add the following in ParseAggregate:

    Expression ParseAggregate(Expression instance, Type elementType, string methodName, int errorPos)
    {
       // Change starts here
       var originalIt = it;
       var originalOuterIt = outerIt;
       // Change ends here
    
       outerIt = it;
       ParameterExpression innerIt = Expression.Parameter(elementType, elementType.Name);
       it = innerIt;
       Expression[] args = ParseArgumentList();
    
       // Change starts here
       it = originalIt;
       outerIt = originalOuterIt;
       // Change ends here
    
       ...
    }
    
  2. Add Select and ToList into IEnumerableSignatures, and a respective condition in ParseAggregate:

    interface IEnumerableSignatures
    {
      ...
      void Select(object selector);
      void ToList();
      ...
    }
    
    Expression ParseAggregate(Expression instance, Type elementType, string methodName, int errorPos)
    {
       ...
       if (signature.Name == "Min" || 
           signature.Name == "Max" || 
           signature.Name == "Select")
       ...
    }
    
  3. Finally, Your query would be:

    static void Main()
    {
        // example data
        var warehouses = new List<Warehouse>
        {
            new Warehouse { WarehouseName = "NY1", Quantity = 10 },
            new Warehouse { WarehouseName = "NY2", Quantity = 100 }
        };
        var parts = new List<Part> 
        { 
             new Part { PartNumber = "1", Description = "Hammer", Warehouses = warehouses } 
        };
        // query
        var result =
            parts
            .Select(@"new ( 
               PartNumber,
               Description,
               Warehouses.Select(WarehouseName).ToList() as WarehouseNames
            )");
    }
    
OfirD
  • 9,442
  • 5
  • 47
  • 90