Usare AsNoTracking() con FromSqlRaw

di il
7 risposte

Usare AsNoTracking() con FromSqlRaw

Buongiorno,

ho preso dalla guida di microsoft il codice per visualizzare una tabella di dati in pagine con questo codice

public async Task<IActionResult> Dipendenti(string searchString, string sortOrder, string currentFilter, int? pageNumber)
        {
            string SNome = HttpContext.Session.GetString("NomeCliente");
            ViewData["NomeCliente"] = SNome;
            ViewData["CurrentSort"] = sortOrder;
            ViewData["CognomeSortParm"] = String.IsNullOrEmpty(sortOrder) ? "Cognome_desc" : "";
            ViewData["CodiceSortParm"] = sortOrder == "Codice" ? "Codice_desc" : "Codice";
            
            if (searchString != null)
            {
                pageNumber = 1;
            }
            else
            {
                searchString = currentFilter;
            }
            ViewData["CurrentFilter"] = searchString;

            var Dipendenti = from m in context.Dipendenti select m;

            if (!String.IsNullOrEmpty(searchString))
            {

             Dipendenti = Dipendenti.Where(s => s.Cognome.Contains(searchString) || s.Nome.Contains(searchString) || s.CodiceFiscale.Contains(searchString));
               
            }
            switch (sortOrder)
            {
                case "Cognome_desc":
                    Dipendenti = Dipendenti.OrderByDescending(s => s.Cognome);
                    break;
                case "Codice":
                   Dipendenti = Dipendenti.OrderBy(s => s.Codice);
                    break;
                case "Codice_desc":
                   Dipendenti = Dipendenti.OrderByDescending(s => s.Codice);
                    break;
                default:
                   Dipendenti = Dipendenti.OrderBy(s => s.Cognome);
                    break;
            }
            int pageSize = 8;
            return View(await PaginatedList<Dipendenti>.CreateAsync(Dipendenti.AsNoTracking(), pageNumber ?? 1, pageSize));
          }
con questa Classe PaginatedList

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using paghe.Models;

namespace paghe
{
    public class PaginatedList<T> : List<T>
    {
        public int PageIndex { get; private set; }
        public int TotalPages { get; private set; }

        public PaginatedList(List<T> items, int count, int pageIndex, int pageSize)
        {
            PageIndex = pageIndex;
            TotalPages = (int)Math.Ceiling(count / (double)pageSize);

            this.AddRange(items);
        }

        public bool HasPreviousPage
        {
            get
            {
                return (PageIndex > 1);
            }
        }

        public bool HasNextPage
        {
            get
            {
                return (PageIndex < TotalPages);
            }
        }

        public static async Task<PaginatedList<T>> CreateAsync(IQueryable<T> source, int pageIndex, int pageSize)
        {
            var count = await source.CountAsync();
            var items = await source.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync();
            return new PaginatedList<T>(items, count, pageIndex, pageSize);
        }
    }
}
Tutto ok funziona Perfettamente!

Ora vorrei cambiare la query da

var Dipendenti = from m in context.Dipendenti select m;
a

var Dipendenti = await context.Dipendenti.FromSqlRaw("SELECT * FROM Dipendenti ").ToListAsync();
(ovviamente modificando anche tutte le altre query LINQ

ma mi da errore in

return View(await PaginatedList<Dipendenti>.CreateAsync(Dipendenti.[color=#FF0000]AsNoTracking()[/color], pageNumber ?? 1, pageSize));
a quel che ho capito l' errore stà nella classe PaginatedList
IQueryable<T>
non si può usare con FromSqlRaw.

qualcuno sa dirmi come ovviare al problema?

7 Risposte

  • Re: Usare AsNoTracking() con FromSqlRaw

    Ciao

    La clausola "as no tracking" deve essere impostata prima di fare il ToList.
    Nel tuo caso, quindi, la devi mettere prima del ToListAsync()

    Grosso modo:
    var Dipendenti = await context.Dipendenti.AsNoTracking().FromSqlRaw("SELECT * FROM Dipendenti ").ToListAsync();
  • Re: Usare AsNoTracking() con FromSqlRaw

    Grazie ma mi da errore in FromSqlRaw
    
    var Dipendenti = await context.Dipendenti.AsNoTracking().FromSqlRaw("SELECT * FROM Dipendenti ").ToListAsync();
    
    Errore CS1061 'IQueryable<Dipendenti>' non contiene una definizione di 'FromSqlRaw' e non è stato trovato alcun metodo di estensione accessibile 'FromSqlRaw' che accetta un primo argomento di tipo 'IQueryable<Dipendenti>'. Probabilmente manca una direttiva using o un riferimento all'assembly.
  • Re: Usare AsNoTracking() con FromSqlRaw

    eternityck ha scritto:


    a quel che ho capito l' errore stà nella classe PaginatedList
    IQueryable<T>
    non si può usare con FromSqlRaw.
    qualcuno sa dirmi come ovviare al problema?
    Io userei sempre il tipo più generico possibile, ovvero IEnumerable<T> che si adatta a tutti i contesti, al posto di IQueryable<T>.

    Ciao!
  • Re: Usare AsNoTracking() con FromSqlRaw

    eternityck ha scritto:


    Grazie ma mi da errore in FromSqlRaw
    
    var Dipendenti = await context.Dipendenti.AsNoTracking().FromSqlRaw("SELECT * FROM Dipendenti ").ToListAsync();
    
    Errore CS1061 'IQueryable<Dipendenti>' non contiene una definizione di 'FromSqlRaw' e non è stato trovato alcun metodo di estensione accessibile 'FromSqlRaw' che accetta un primo argomento di tipo 'IQueryable<Dipendenti>'. Probabilmente manca una direttiva using o un riferimento all'assembly.

    Scusa, ho scritto al volo e forse l'ho messo nel posto sbagliato. Prova a spostarlo prima del ToListAsync();

    A parte questo, puoi postare la query esatta che metti nel metodo?
    Personalmente nel codice di solito tendo a:
    - Usare le interfacce di basso livello ove possibile (vedi IEnumerable al posto di IQueryable). Si sa mai che un domani debba usare una base dati non supportata da EntityFramework e sarebbe un casino (caso più unico che raro ma si sa mai, specie se vuoi fare dei test-unit)
    - Usare il più possibile le query LINQ perchè hanno tanti vantaggi in fase di scrittura e manutenzione codice (anche se hanno il risvolto di essere spesso lente in esecuzione)
  • Re: Usare AsNoTracking() con FromSqlRaw

    Sempre errore o meglio
    il metodo del controller origale e Funzionante è :
    
            public async Task<IActionResult> Dipendenti(string searchString, string sortOrder, string currentFilter, int? pageNumber)
            {
                string SNome = HttpContext.Session.GetString("NomeCliente");
                ViewData["NomeCliente"] = SNome;
                ViewData["CurrentSort"] = sortOrder;
                ViewData["CognomeSortParm"] = String.IsNullOrEmpty(sortOrder) ? "Cognome_desc" : "";
                ViewData["CodiceSortParm"] = sortOrder == "Codice" ? "Codice_desc" : "Codice";
                
                if (searchString != null)
                {
                    pageNumber = 1;
                }
                else
                {
                    searchString = currentFilter;
                }
                ViewData["CurrentFilter"] = searchString;
    
                var Dipendenti = from m in context.Dipendenti select m;
      
                 if (!String.IsNullOrEmpty(searchString))
                {
                    Dipendenti = Dipendenti.Where(s => s.Cognome.Contains(searchString) || s.Nome.Contains(searchString) || s.CodiceFiscale.Contains(searchString));
                }
                int pageSize = 8;
                return View(await PaginatedList<Dipendenti>.CreateAsync(Dipendenti.AsNoTracking(), pageNumber ?? 1, pageSize));
              }
    
    Invece la vorrei modificare usando una query sql server pura perchè le conosco meglio ed l ho modificata con .AsNoTracking() prima del ToListAsync
    
            public async Task<IActionResult> Dipendenti(string searchString, string sortOrder, string currentFilter, int? pageNumber)
            {
                string SNome = HttpContext.Session.GetString("NomeCliente");
                ViewData["NomeCliente"] = SNome;
                ViewData["CurrentSort"] = sortOrder;
                ViewData["CognomeSortParm"] = String.IsNullOrEmpty(sortOrder) ? "Cognome_desc" : "";
                ViewData["CodiceSortParm"] = sortOrder == "Codice" ? "Codice_desc" : "Codice";
                
                if (searchString != null)
                {
                    pageNumber = 1;
                }
                else
                {
                    searchString = currentFilter;
                }
                ViewData["CurrentFilter"] = searchString;
    
                var Dipendenti = await context.Dipendenti.FromSqlRaw("SELECT * FROM Dipendenti ").AsNoTracking().ToListAsync();
                 if (!String.IsNullOrEmpty(searchString))
                {
                    Dipendenti = await context.Dipendenti.FromSqlRaw("SELECT * FROM Dipendenti WHERE Cognome='" + searchString + "'").AsNoTracking().ToListAsync();    
                }
                
                int pageSize = 8;
                return View(await PaginatedList<Dipendenti>.CreateAsync(Dipendenti, pageNumber ?? 1, pageSize));
                }
     
    ed ora mi da errore su
    return View(await PaginatedList<Dipendenti>.CreateAsync(Dipendenti, pageNumber ?? 1, pageSize));
    Errore CS1503 Argomento 1: non è possibile convertire da 'System.Collections.Generic.List<paghe.Models.Dipendenti>' a 'System.Linq.IQueryable<paghe.Models.Dipendenti>'
    il model della view è
    
    @model PaginatedList<paghe.Models.Dipendenti>
    
    e la classe PaginateList è
    
        public class PaginatedList<T> : List<T>
        {
            public int PageIndex { get; private set; }
            public int TotalPages { get; private set; }
    
            public PaginatedList(List<T> items, int count, int pageIndex, int pageSize)
            {
                PageIndex = pageIndex;
                TotalPages = (int)Math.Ceiling(count / (double)pageSize);
    
                this.AddRange(items);
            }
    
            public bool HasPreviousPage
            {
                get
                {
                    return (PageIndex > 1);
                }
            }
    
            public bool HasNextPage
            {
                get
                {
                    return (PageIndex < TotalPages);
                }
            }
    
            public static async Task<PaginatedList<T>> CreateAsync(IQueryable<T> source, int pageIndex, int pageSize)
            {
                var count = await source.CountAsync();
                var items = await source.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync();
                return new PaginatedList<T>(items, count, pageIndex, pageSize);
            }
    
        }
    
    se provo a modificare la CreateAsync
    
            public static async Task<PaginatedList<T>> CreateAsync(IEnumerable<T> source, int pageIndex, int pageSize)
            {
                var count = await source.CountAsync();
                var items = await source.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync();
                return new PaginatedList<T>(items, count, pageIndex, pageSize);
            }
    
    mi da errore su
    var count = await source.CountAsync();
    Errore CS1061 'IEnumerable<T>' non contiene una definizione di 'CountAsync' e non è stato trovato alcun metodo di estensione accessibile 'CountAsync' che accetta un primo argomento di tipo 'IEnumerable<T>'. Probabilmente manca una direttiva using o un riferimento all'assembly.
    e su
    var items = await source.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync();
    Gravità Codice Descrizione Progetto File Riga Stato eliminazione
    Errore CS1061 'IEnumerable<T>' non contiene una definizione di 'ToListAsync' e non è stato trovato alcun metodo di estensione accessibile 'ToListAsync' che accetta un primo argomento di tipo 'IEnumerable<T>'. Probabilmente manca una direttiva using o un riferimento all'assembly.
  • Re: Usare AsNoTracking() con FromSqlRaw

    Allora ho studiato un pò ed ho capito che asp.net core 3 non accetta una query scritta con frosqlraw o fromsqlinterpoleted passata con un argomento di tipo IQueryable quindi devo modificare modificare questa classe
    
     public static async Task<PaginatedList<T>> CreateAsync(IQueryable<T> source, int pageIndex, int pageSize)
            {
                var count = await source.CountAsync();
                var items = await source.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync();
                return new PaginatedList<T>(items, count, pageIndex, pageSize);
            }
    
    dato che Ienumerable con riconosce CountAsync e ToListAsync......
    l ho modificata così
    
           public static async Task<PaginatedList<T>> CreateAsync(IEnumerable<T> source, int pageIndex, int pageSize)
            {
                var count = source.Count();
                var items = source.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();
                return new PaginatedList<T>(items, count, pageIndex, pageSize);
            }
    
    e pare che funzioni!!!!
    Consigli? Pareri? punti di Vista?.........
  • Re: Usare AsNoTracking() con FromSqlRaw

    Per me è meglio se impari ad usare per bene le query LINQ

    Usare IEnumerable è più "portabile" in fase di cambio base dati, però ti perdi un sacco di vantaggi.
    Pensa ad esempio se nel database hai 5.000.000 clienti.

    Se passi dalla query manuale e IEnumerable, ti trovi che dal database al .NET passano tutti e 5.000.000 di record, poi il .NET li filtra in memoria per restituirne solo 10 al chiamante (dimensione pagina).

    Se usi IQueryable, invece, il take-skip viene ribaltato in automatico sul DB che così manda solo i 10 elementi dal database al tuo codice .NET, oltretutto in molti casi sfruttando anche la cache di SQL e quindi avendo prestazioni migliori.


    Se decidi di usare le query SQL perchè ti trovi meglio, nulla in contrario. Però in questo caso fatti le tue interfacce che ti consentano di ottimizzare le query anche in fase di estrazione dati (quindi mettere nelle query i vari filtri, ordinamenti, paginazione, ...).

    In tal caso ti conviene però abbandonare il concetto di ORM e passare ad un banale mapper (anche se banale non è). Allo scopo puoi guardare librerie come Dapper o similari.


    Quale strada consiglio?
    Dipende!

    Se stai imparando a programmare e conti di usare solo DB supportati da EntityFramework, secondo me ti conviene senza ombra di dubbio imparare IQueryable: molto più flessibile e grazie al quale puoi centralizzare un sacco di codice di accesso al DB (vedi l'esempio della paginazione che hai postato tu stesso).

    Se invece sei affezionato alle query e vuoi fare delle interfacce che funzionino anche su sistemi che non supportano IQueryable, allora meglio andare di IEnumerable
Devi accedere o registrarti per scrivere nel forum
7 risposte