jasontaylordev / NorthwindTraders

Northwind Traders is a sample application built using ASP.NET Core and Entity Framework Core.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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.