0

I have methods used in IQueryable.Select(...) that shares the same logic of mapping an Entity class to a DTO class, but filter the navigation properties in the projection differently. The problem is there are many different ways needed to filter the child collections. This means they will all shares the same mapping logic to map ParentEntity to ParentDTO. Hence, I'm trying to extract this mapping logic.

The reason why I didn't just do 2 separate queries for Products and Labels is that if there are N products, there will be N extra SQL queries to Labels (and N could be large).

Note: .AsExpandable() and .Invoke() are from LinqKit Core so that I can have nested Expression being translated correctly by Linq to Sql.) Also, I'm using .NET Framework 4.8 with Entity Framework 6.

Below is the code I have now (and the generated SQL Query looks fine. No N+1 issue.) Is there anyway to extract the mapping logic? (I'm open to use AutoMapper if that should be the way to go.)

/*DAO methods*/
public List<ProductDTO> ListProductsFilteredByLabelCustomerID
    (string productType, string customerID) 
{
    return context.Product
       .AsExpandable()
       .Where(product => product.ProductType == productType)
       .Select(ToDTOFilteredByLabelCustomerID(customerID));
}

public List<ProductDTO> ListProductsFilteredByLabelCustomerType
    (string productType, string customerType) 
{
    return context.Product
       .AsExpandable()
       .Where(product => product.ProductType == productType)
       .Select(ToDTOFilteredByLabelCustomerType(customerID));
}

/*Projection Expressions*/
public static Expression<Func<Product, ProductDTO>>
    ToDTOFilteredByLabelCustomerID(string customerID) 
{
    return product => new ProductLabelsDTO {
        // I hope to extract these assignments
        productName = product.Name,
        productDescription = product.Description,
        productSKU = product.SKU,
        // skipped bunch of other product's fields for simplicity...
        //
        
        Labels = product.Labels
            .Where(label => label.CustomerID == customerID)
            .Select(label => LabelDTO.ToDTO().Invoke(label))
    };
}

public static Expression<Func<Product, ProductDTO>> 
    ToDTOFilteredByLabelCustomerType(string customerType) 
{
    return product => new ProductLabelsDTO {
        // I hope to extract these assignments
        productName = product.Name,
        productDescription = product.Description,
        productSKU = product.SKU,
        // skipped bunch of other product's fields for simplicity...
        //

        Labels = product.Labels
            .Where(label => label.CustomerType == customerType)
            .Select(label => LabelDTO.ToDTO().Invoke(label))
    };
}
6
  • What is needed to extract? I know how LINQKit works but I cannot get what is needed. Commented Apr 14 at 20:26
  • The part which assign all the properties from Product to all the properties in ProductDTO. This mapping logic will be shared among all methods that maps Product to ProductDTO (despite having different filters on Labels). I've updated the question, hope this makes it clearer. Thanks! Commented Apr 14 at 20:27
  • 1
    I recommend handling this manually or using a library like AutoMapper. Combining expressions is possible but adds complexity to the code. Commented Apr 14 at 20:35
  • I'm open to AutoMapper, but I can't find a way to do so without installing even more third party automapper extensions for EF6. Hence I'm asking here to see if there are simpler solutions... But yeah, I'm open to AutoMapper if required! Also, I can see that this should be a common pattern in our application, so if possible I want to avoid doing the same mapping manually multiple times. Commented Apr 14 at 20:36
  • 1
    Write the largest possible projection, then run an expression visitor to filter the list of MemberInitExpression.Bindings? Commented Apr 15 at 0:58

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.