FromSqlRaw won't work in Application
SuliemanMansouri opened this issue · comments
I am trying to run the following SQL Query in EF Core using FromSqlRaw and key-less entities.
SELECT Products.Name, ExpiretionDates.InvoiceNumber,
SUM(CASE WHEN ExpiretionDates.ExpirationDate < GETDATE() THEN ExpiretionDates.Quantity END) AS ExpiredCount,
SUM(CASE WHEN abs(DATEDIFF(day, ExpiretionDates.ExpirationDate, GETDATE())) <= 30 THEN ExpiretionDates.Quantity END) AS WarningCount,
SUM(CASE WHEN abs(DATEDIFF(day, ExpiretionDates.ExpirationDate, GETDATE())) >= 30 THEN ExpiretionDates.Quantity END) AS ValidCount
FROM ExpiretionDates INNER JOIN
Products ON ExpiretionDates.ProductId = Products.Id
GROUP BY Products.Name, ExpiretionDates.InvoiceNumber
Because the template is using an interface to the context I can not do that. I tried to reference ApplicationContext directly in my Application project it caused a circular dependency.
Why do we need the Interfaces inside the Application project? shouldn't they be in a separate project? Or is it force the use of the interface instead of the original context?
How do I enable DataBase in my IApplicationContext?
My second option is to use LINQ and my context, here is my code:
public class GetQuantitiesCountByExpiryStatusQuery : IRequest<ProductsVM>
{
public int NumberOfDays { get; set; }
public class GetQuantitiesCountByExpiryStatusQueryHandler : IRequestHandler<GetQuantitiesCountByExpiryStatusQuery, ProductsVM>
{
private readonly IApplicationDbContext _context;
private readonly IMapper _mapper;
public GetQuantitiesCountByExpiryStatusQueryHandler(IApplicationDbContext context, IMapper mapper)
{
_context = context;
_mapper = mapper;
}
public async Task<ProductsVM> Handle(GetQuantitiesCountByExpiryStatusQuery request, CancellationToken cancellationToken)
{
return new ProductsVM
{
List = await (from p in _context.Products.AsNoTracking()
join ed in _context.ExpiretionDates.AsNoTracking() on p.Id equals ed.ProductId
group ed by p.Name into products
select new
{
Name = products.Key,
ExpriedCount = products.Where(y => y.ExpirationDate < DateTime.Now).Sum(x => x.Quantity),
ValidCount = products.Where(y => EF.Functions.DateDiffDay(y.ExpirationDate, DateTime.Now) >= request.NumberOfDays).Sum(x => x.Quantity),
WarningCount = products.Where(y => EF.Functions.DateDiffDay(y.ExpirationDate, DateTime.Now) <= request.NumberOfDays).Sum(x => x.Quantity)
}).ToListAsync(cancellationToken)
};
}
}
}
Here is my problem I am not very good with LINQ, and as I understand it there are queries that just can't be translated into an efficient LINQ Query if at all. I ran the linq in LinqPad the resulted query has 8 nested Select statements.
Just by comparing the execution plan for both queries the first one is much cheaper and faster. My test data was 10 rows in Products table and 5 children per parent in ExpirationDates table, I can only imagine if the tables were much larger how would that affect the result.
Is there a posibility of enabling FromSqlRaw for these fringe cases.
Cheers
Hi @SuliemanMansouri
First of all - read this article https://stackoverflow.com/questions/35631903/raw-sql-query-without-dbset-entity-framework-core
How to use RawSQL in EF Core. It depends on the used EF Core version.
Official docs for the latest Ef Core version.
https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.relationalqueryableextensions.fromsql?view=efcore-3.1
What EF Core version do you use? And could you please show the code?
About LINQ or RawSQL - both work fine with 10 rows. You need more data to see the actual execution plan.
Why do we need the Interfaces inside the Application project?
It is a very good question!
I believe better to move interfaces to separate project and architecture layer.
And add a project which "implements" these interfaces.
And "use" these interfaces from the Application layer (and project).
It is Dependency Inversion principle in practice :)
Hi, @AndreiTsvettsikh
I was adamant on using Raw SQL using Context.Database, the way I used to do it in EF6. That's what happens when trying to code after 3:00 am in the morning.
Using FromSQL on entities DBSets works fine with keyless entities.
Thanks @AndreiTsvettsikh for pointing which EF Core version I am using.