2016-02-25 2 views
19

Con Entity Framework Core rimozione dbData.Database.SqlQuery<SomeModel> Non riesco a trovare una soluzione per creare una query SQL raw per la query di ricerca full-text che restituirà i dati delle tabelle e anche il rango.Query SQL raw senza DbSet - Entity Framework Core

L'unico metodo che ho visto per creare una query SQL raw in Entity Framework Core è tramite dbData.Product.FromSql("SQL SCRIPT"); che non è utile in quanto non ho DbSet che mapperà il rango che restituisco nella query.

Qualsiasi idea ???

+8

Mi mancherà molto la SqlQuery e non voglio dover associare classi personalizzate al mio DbContext quando ho davvero bisogno di un semplice DTO per un caso d'uso specifico. Ho creato una voce utente per richiedere l'aggiunta di questa funzione a EF Core che chiunque può votare se desidera che questa funzione venga restituita: https: //data.uservoice.it/forums/72025-entity-framework-feature-suggestions/suggestions/13183638-add-dbcontext-database-sqlquery-to-entity-framewor –

+1

Secondo https://github.com/aspnet/EntityFramework/issues/1862, questo è ora mirato per EF core 1.2 e/o 1.1.0-preview1 –

risposta

15

In EF Core non è più possibile eseguire sql raw "liberi". È necessario definire una classe POCO e una DbSet per quella classe. Nel tuo caso sarà necessario definire Classifica:

var ranks = DbContext.Ranks 
    .FromSql("SQL_SCRIPT OR STORED_PROCEDURE @p0,@p1,...etc", parameters) 
    .AsNoTracking().ToList(); 

Come sarà sicuramente in sola lettura sarà utile includere la chiamata .AsNoTracking().

+2

Quindi suppongo che dovrò anche estendere il 'DbContext' per includere una nuova proprietà' DbSet Rank {get; impostato; } '. Quali implicazioni avrà ora in riferimento a linq? I.e. Ora non saremo in grado di usare un'istruzione come 'DBContext.Rank.Where (i => i.key == 1)', e questa affermazione non avrà alcuna implementazione in SQL e quindi fallirà? –

+0

I Linq emessi contro questo set devono essere risolti in memoria. Se è necessario emettere una clausola WHERE diversa WHERE, è necessario includerli come parametri o creare uno script diverso. –

+0

Il mio DbSet non ha un metodo "FromSql". Questa è un'estensione che mi manca? – birwin

8

È possibile eseguire sql raw in EF Core: aggiungere questa classe al progetto. Ciò consentirà di eseguire SQL raw e ottenere risultati grezzi senza dover definire un POCO e un DBSet. Vedere https://github.com/aspnet/EntityFramework/issues/1862#issuecomment-220787464 per l'esempio originale.

using Microsoft.EntityFrameworkCore.Infrastructure; 
using Microsoft.EntityFrameworkCore.Internal; 
using Microsoft.EntityFrameworkCore.Storage; 
using System.Threading; 
using System.Threading.Tasks; 

namespace Microsoft.EntityFrameworkCore 
{ 
    public static class RDFacadeExtensions 
    { 
     public static RelationalDataReader ExecuteSqlQuery(this DatabaseFacade databaseFacade, string sql, params object[] parameters) 
     { 
      var concurrencyDetector = databaseFacade.GetService<IConcurrencyDetector>(); 

      using (concurrencyDetector.EnterCriticalSection()) 
      { 
       var rawSqlCommand = databaseFacade 
        .GetService<IRawSqlCommandBuilder>() 
        .Build(sql, parameters); 

       return rawSqlCommand 
        .RelationalCommand 
        .ExecuteReader(
         databaseFacade.GetService<IRelationalConnection>(), 
         parameterValues: rawSqlCommand.ParameterValues); 
      } 
     } 

     public static async Task<RelationalDataReader> ExecuteSqlQueryAsync(this DatabaseFacade databaseFacade, 
                  string sql, 
                  CancellationToken cancellationToken = default(CancellationToken), 
                  params object[] parameters) 
     { 

      var concurrencyDetector = databaseFacade.GetService<IConcurrencyDetector>(); 

      using (concurrencyDetector.EnterCriticalSection()) 
      { 
       var rawSqlCommand = databaseFacade 
        .GetService<IRawSqlCommandBuilder>() 
        .Build(sql, parameters); 

       return await rawSqlCommand 
        .RelationalCommand 
        .ExecuteReaderAsync(
         databaseFacade.GetService<IRelationalConnection>(), 
         parameterValues: rawSqlCommand.ParameterValues, 
         cancellationToken: cancellationToken); 
      } 
     } 
    } 
} 

Ecco un esempio di come usarlo:

// Execute a query. 
using(var dr = await db.Database.ExecuteSqlQueryAsync("SELECT ID, Credits, LoginDate FROM SamplePlayer WHERE " + 
                  "Name IN ('Electro', 'Nitro')")) 
{ 
    // Output rows. 
    var reader = dr.DbDataReader; 
    while (reader.Read()) 
    { 
     Console.Write("{0}\t{1}\t{2} \n", reader[0], reader[1], reader[2]); 
    } 
} 
2

Per ora, fino a quando c'è qualcosa di nuovo da EFCore avrei utilizzato un comando e mappare manualmente

using (var command = this.DbContext.Database.GetDbConnection().CreateCommand()) 
     { 
      command.CommandText = "SELECT ... WHERE ...> @p1)"; 
      command.CommandType = CommandType.Text; 
      var parameter = new SqlParameter("@p1",...); 

      this.DbContext.Database.OpenConnection(); 

      using (var result = command.ExecuteReader()) 
      { 
       while (result.Read()) 
       { 
        .... // Map to your entity 
       } 
      } 
     } 

Prova a SqlParameter per evitare Sql Injection.

 dbData.Product.FromSql("SQL SCRIPT"); 

FromSql non funziona con query completa. Esempio se vuoi includere una clausola WHERE verrà ignorata.

Alcuni vicini:

Executing Raw SQL Queries using Entity Framework Core

Raw SQL Queries

3

Costruire sulle altre risposte che ho scritto questo helper che compie il compito, tra cui esempio di utilizzo:

public static class Helper 
{ 
    public static List<T> RawSqlQuery<T>(string query, Func<DbDataReader, T> map) 
    { 
     using (var context = new WannaSportContext()) 
     { 
      using (var command = context.Database.GetDbConnection().CreateCommand()) 
      { 
       command.CommandText = query; 
       command.CommandType = CommandType.Text; 

       context.Database.OpenConnection(); 

       using (var result = command.ExecuteReader()) 
       { 
        var entities = new List<T>(); 

        while (result.Read()) 
        { 
         entities.Add(map(result)); 
        } 

        return entities; 
       } 
      } 
     } 
    } 

di utilizzo:

public class TopUser 
{ 
    public string Name { get; set; } 

    public int Count { get; set; } 
} 

var result = Helper.RawSqlQuery(
    "SELECT TOP 10 Name, COUNT(*) FROM Users U" 
    + " INNER JOIN Signups S ON U.UserId = S.UserId" 
    + " GROUP BY U.Name ORDER BY COUNT(*) DESC", 
    x => new TopUser { Name = (string)x[0], Count = (int)x[1] }); 

result.ForEach(x => Console.WriteLine($"{x.Name,-25}{x.Count}")); 

Ho intenzione di sbarazzarcene non appena viene aggiunto il supporto integrato. Secondo uno statement di Arthur Vickers del team EF Core, è una priorità elevata per il post 2.0. Il problema è stato rintracciato here.