I have a very simple entity called Product
. It contains an explicit casting to the DTO type ProductListDTO
.
public class Product
{
public Guid ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string? Notes { get; set; }
public static explicit operator ProductListDTO (Product p)
{
return new ProductListDTO
{
ID = p.ID,
Name = p.Name,
Description = p.Description
};
}
}
Below are 4 ways that accessed the data. One of them (Which is the desired one) fails.
(1) - this works
var allProducts = db.Products.Select(x => new ProductListDTO
{
Name = x.Name,
Description = x.Description
}).ToList();
(2) - this also works
var allProducts_Casting = db.Products.Select(x => (ProductListDTO)x).ToList();
(3) - and this also works
var filteredProducts = db.Products.Select(x => new ProductListDTO
{
Name = x.Name,
Description = x.Description
})
.Where(x => x.Name == "Product 1")
.ToList();
(4) - this however does NOT work
var filteredProducts_Casting = db.Products.Select(x => (ProductListDTO) x)
.Where(x => x.Name == "Product 1")
.ToList();
The exception is:
System.InvalidOperationException : The LINQ expression 'DbSet()
.Where(p => !(p.IsDeleted))
.Where(p => ((ProductListDTO)p).Name == "Product 1")' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
Some context
My only goal is to have a single place where I do the conversion between Product
to ProductListDTO
and that it can be re-used. And I prefer a simple approach like below.
Desired:
public IQueryable<ProductListDTO> OdataList()
{
return db.Products.Select(x => (ProductListDTO)x);
}
public IQueryable<PurchaseOrderItemListDTO> OdataList()
{
return db.PurchaseOrderItems.Select(p => new PurchaseOrderItemListDTO
{
ID = p.ID,
UnitPrice = p.UnitPrice,
Quantity = p.Quantity,
Product = (ProductListDTO) p.Product,
});
}
But this is where I run into the problem I explained earlier
Trying to avoid
There's also the below approach that works fine but it's more complex and hard to explain to other developers:
public static readonly Expression<Func<Product, ProductListDTO>> ProductListQuery = p => new ProductListDTO
{
ID = p.ID,
Name = p.Name,
Description = p.Description
};
public IQueryable<ProductListDTO> OdataList()
{
return db.Products.Select(ProductListQuery);
}
public IQueryable<PurchaseOrderItemListDTO> OdataList()
{
var productListing = ProductListQuery.Compile();
return db.PurchaseOrderItems.Select(p => new PurchaseOrderItemListDTO
{
ID = p.ID,
UnitPrice = p.UnitPrice,
Quantity = p.Quantity,
Product = productListing(p.Product),
});
}