Can I have a projection method/property, which I can use in another projection method/property?
I have Domain Entities Customer
and Sale
.
public class Customer : IEntity<long>
{
public long Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Sale> Sales { get; set; }
}
public class Sale : IEntity<long>
{
public long Id { get; set; }
public decimal UnitPrice { get; set; }
public int Quantity { get; set; }
public decimal TotalPrice { get; set; }
public DateTimeOffset Date { get; set; }
public long CustomerId { get; set; }
public virtual Customer Customer { get; set; }
public long EmployeeId { get; set; }
public virtual Employee Employee { get; set; }
public long ProductId { get; set; }
public virtual Product Product { get; set; }
I know I can nest a projection as follows:
public class GetCustomerQuery : IGetCustomerQuery
{
private readonly IAppDbContext _context;
public GetCustomerQuery(IAppDbContext context)
{
_context = context;
}
public Task<CustomerModel> ExecuteAsync(long id)
{
return _context
.Customers
.Where(x => x.Id == id)
.Select(GetEmployeeProjection())
.SingleOrDefaultAsync();
}
private Expression<Func<Domain.Customers.Customer, CustomerModel>> GetEmployeeProjection()
{
return x => new CustomerModel
{
Id = x.Id,
Name = x.Name,
Sales = x.Sales.Select(y => new Sale
{
Employee = y.Employee.Name,
Product = y.Product.Name,
ProductCount = y.Quantity,
TotalPrice = y.TotalPrice
})
};
}
}
But can I separate the projections in two different method, f.e.:
private Expression<Func<Domain.Customers.Customer, CustomerModel>> GetEmployeeProjection()
{
return x => new CustomerModel
{
Id = x.Id,
Name = x.Name,
Sales = x.Sales.Select(GetSaleProjection())
};
}
private Expression<Func<Domain.Sales.Sale, Sale>> GetSaleProjection()
{
return y => new Sale
{
Employee = y.Employee.Name,
Product = y.Product.Name,
ProductCount = y.Quantity,
TotalPrice = y.TotalPrice
};
}
And is it more common to write the projection as a method or as a property?
EDIT: 2020-12-14
@joakimriedel, adding .AsQueryable()
does kind of work, but I also need to add .ToList()
, else a runtime exception was thrown (EF Core 5.0.1), as @Ivan Stoev did mention.
public Task<CustomerModel> ExecuteAsync(long id)
{
return _context
.Customers
.Where(x => x.Id == id)
.Select(_projection)
.SingleOrDefaultAsync();
}
private static readonly Expression<Func<Domain.Customers.Customer, CustomerModel>> _projection =
x => new CustomerModel
{
Id = x.Id,
Name = x.Name,
Sales = x.Sales
.AsQueryable()
.Select(_salesProjection)
.ToList()
};
private static readonly Expression<Func<Domain.Sales.Sale, Sale>> _salesProjection =
x => new Sale
{
Employee = x.Employee.Name,
Product = x.Product.Name,
ProductCount = x.Quantity,
TotalPrice = x.TotalPrice
};