Requête SQL brute sans DbSet-Entity Framework Core

Avec Entity Framework, la suppression de Base dbData.Database.SqlQuery<SomeModel> Je ne trouve pas de solution pour construire une requête SQL brute pour ma requête de recherche en texte intégral qui retournera les données des tables et aussi le rang.

la seule méthode que j'ai vu pour construire une requête SQL brute dans Entity Framework Core est via dbData.Product.FromSql("SQL SCRIPT"); ce qui n'est pas utile car je n'ai pas de DbSet qui va cartographier le rang que je renvoie dans la requête.

Des Idées???

31
demandé sur steamrolla 2016-02-25 18:44:12

6 réponses

dans EF Core, vous ne pouvez plus exécuter de SQL brut "libre". Vous devez définir une classe POCO et un DbSet pour la classe. Dans votre cas, vous devez définir Rang:

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

Comme il sera sûrement en lecture seule, il sera utile d'inclure l' .AsNoTracking() appel.

18
répondu E-Bat 2016-08-24 07:08:09

vous pouvez exécuter du sql brut dans EF Core-Ajoutez cette classe à votre projet. Cela vous permettra d'exécuter du SQL brut et d'obtenir les résultats bruts sans avoir à définir un POCO et un DBSet. Voir https://github.com/aspnet/EntityFramework/issues/1862#issuecomment-220787464 pour l'exemple original.

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);
            }
        }
    }
}

Voici un exemple de comment l'utiliser:

// 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]);
    }
}
10
répondu Yehuda Goldenberg 2017-03-21 16:34:27

en me basant sur les autres réponses j'ai écrit cet helper qui accomplit la tâche, y compris l'exemple d'utilisation:

public static class Helper
{
    public static List<T> RawSqlQuery<T>(string query, Func<DbDataReader, T> map)
    {
        using (var context = new DbContext())
        {
            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;
                }
            }
        }
    }

Utilisation:

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}"));

je prévois de m'en débarrasser dès que le support intégré sera ajouté. Selon un déclaration par Arthur Vickers de L'équipe de base de L'EF il s'agit d'une priorité élevée pour post 2.0. La question est suivie ici.

8
répondu pius 2018-06-19 11:36:20

si vous utilisez EF Core 2.1 Release Candidate 1 Disponible depuis le 7 mai 2018, vous pouvez profiter de la nouvelle fonctionnalité proposée qui est le type de requête.

qu'est-Ce que type de requête?

en plus des types d'entités, un modèle de base de L'EE peut contenir des types de requête, qui peuvent être utilisés pour effectuer des requêtes de base de données sur des données n'est pas associé aux types d'entités.

Quand utiliser le type de requête?

Siégeant en tant que type de retour pour les ad hoc FromSql() requêtes.

Cartographie de vues de base de données.

Cartographie des tables qui n'ont pas de clé primaire.

Cartographie de requêtes définies dans le modèle.

ainsi, vous n'avez plus besoin de faire tous les piratages ou les solutions de rechange proposés pour répondre à votre question. Il suffit de suivre ces étapes:

vous avez d'abord défini une nouvelle propriété de type DbQuery<T>T est le type de la classe qui portera les valeurs de la colonne de votre requête SQL. Donc, dans votre DbContext vous aurez ceci:

public DbQuery<SomeModel> SomeModels { get; set; }

d'autre part utiliser FromSql méthode comme vous le faites avec DbSet<T>:

var result = context.SomeModels.FromSql("SQL_SCRIPT").ToList();
7
répondu CodeNotFound 2018-05-21 15:54:14

pour l'instant, jusqu'à ce Qu'il y ait quelque chose de nouveau de EFCore j'utiliserais une commande et la carte manuellement

  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
                }
            }
        }

essayez de SqlParameter pour éviter L'Injection de Sql.

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

FromSql ne fonctionne pas avec une requête complète. Exemple si vous voulez inclure une clause où elle sera ignorée.

Quelques Liens:

exécuter des requêtes SQL brutes en utilisant Entity Framework Core

requêtes SQL brutes

2
répondu Henry 2017-07-23 06:22:48

Core 2.1, vous pouvez faire quelque chose comme ceci:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
       modelBuilder.Query<Ranks>();
}

et puis vous définir Procédure SQL, de la forme:

public async Task<List<Ranks>> GetRanks(string value1, Nullable<decimal> value2)
{
    SqlParameter value1Input = new SqlParameter("@Param1", value1?? (object)DBNull.Value);
    SqlParameter value2Input = new SqlParameter("@Param2", value2?? (object)DBNull.Value);

    List<Ranks> getRanks = await this.Query<Ranks>().FromSql("STORED_PROCEDURE @Param1, @Param2", value1Input, value2Input).ToListAsync();

    return getRanks;
}

de cette façon, le modèle de classement ne sera pas créé dans votre base de données.

maintenant dans votre controller / action vous pouvez appeler:

List<Ranks> gettingRanks = _DbContext.GetRanks(value1,value2).Result.ToListAsync();

de cette façon, vous pouvez appeler les procédures brutes SQL.

1
répondu RodrigoCampos 2018-06-19 10:22:30