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))
};
}
Productto all the properties inProductDTO. This mapping logic will be shared among all methods that mapsProducttoProductDTO(despite having different filters onLabels). I've updated the question, hope this makes it clearer. Thanks!MemberInitExpression.Bindings?