Requête SQL brute sans DbSet - Entity Framework Core

107

Avec la suppression d'Entity Framework Core, dbData.Database.SqlQuery<SomeModel>je ne trouve pas de solution pour créer une requête SQL brute pour ma requête de recherche en texte intégral qui renverra les données des tables ainsi que le classement.

La seule méthode que j'ai vue pour créer 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 mappera le rang que je renvoie dans la requête.

Des idées???

David Harlow
la source
15
Le SqlQuery <T> me manquera énormément et je ne veux pas avoir à mapper des classes personnalisées à mon DbContext alors que j'ai vraiment besoin d'un simple DTO pour un cas d'utilisation spécifique. J'ai créé une voix utilisateur pour demander l'ajout de cette fonctionnalité à EF Core que tout le monde peut voter s'il souhaite récupérer cette fonctionnalité: data.uservoice.com/forums
Matt Sanders
1
Selon github.com/aspnet/EntityFramework/issues/1862 , ceci est maintenant ciblé pour EF core 1.2 et / ou 1.1.0-preview1
Dan Field
2
En me basant sur ce que @Devon vient de dire, j'ai passé beaucoup trop de temps à comprendre qu'il s'agissait de méthodes d'extension dans Microsoft.EntityFrameworkCore.SqlServer. Vous devrez l'ajouter à votre projet avant d'obtenir ces méthodes d'extension.
Daniel le
3
Soupir cela ressemble à une sorte de décision d'astronaute d'architecture: "les gens ne devraient pas avoir besoin de vouloir ça". Je suppose que je dois installer Dapper juste pour ce cas. Énervant.
Dirk Boer le
1
@MattSanders - votre lien uservoice semble être mort entre-temps. Savez-vous où il est allé?
Dirk Boer le

Réponses:

127

Cela dépend si vous utilisez EF Core 2.1 ou EF Core 3 et les versions supérieures .

Si vous utilisez EF Core 2.1

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 de type Requête.

Quel est le type de requête ?

En plus des types d'entité, un modèle EF Core 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 qui ne sont pas mappées à des types d'entité.

Quand utiliser le type de requête?

Servant de type de retour pour les requêtes FromSql () ad hoc.

Mappage aux vues de base de données.

Mappage vers des tables pour lesquelles aucune clé primaire n'est définie.

Mappage aux requêtes définies dans le modèle.

Vous n'avez donc plus besoin de faire tous les hacks ou solutions de contournement proposés comme réponses à votre question. Suivez simplement ces étapes:

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

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

Ensuite, utilisez la FromSqlméthode comme vous le faites avec DbSet<T>:

var result = context.SomeModels.FromSql("SQL_SCRIPT").ToList();
var result = await context.SomeModels.FromSql("SQL_SCRIPT").ToListAsync();

Notez également que les DdContexts sont des classes partielles , vous pouvez donc créer un ou plusieurs fichiers séparés pour organiser vos définitions «SQL DbQuery brutes» comme vous le souhaitez.


Si vous utilisez EF Core 3.0 et les versions supérieures

Le type de requête est désormais appelé type d'entité sans clé . Comme indiqué ci-dessus, les types de requêtes ont été introduits dans EF Core 2.1. Si vous utilisez EF Core 3.0 ou une version supérieure, vous devez maintenant utiliser des types de tntity sans clé, car les types de requête sont désormais marqués comme obsolètes.

Cette fonctionnalité a été ajoutée dans EF Core 2.1 sous le nom de types de requête. Dans EF Core 3.0, le concept a été renommé en types d'entités sans clé. L'annotation de données [sans clé] est devenue disponible dans EFCore 5.0.

Nous avons toujours les mêmes scénarios que pour les types de requête pour savoir quand utiliser le type d'entité sans clé.

Donc, pour l'utiliser, vous devez d'abord marquer votre classe SomeModelavec [Keyless]une annotation de données ou via une configuration fluide avec un .HasNoKey()appel de méthode comme ci-dessous:

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

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<SomeModel>().HasNoKey();
}

Après cette configuration, vous pouvez utiliser l'une des méthodes expliquées ici pour exécuter votre requête SQL. Par exemple, vous pouvez utiliser celui-ci:

var result = context.SomeModels.FromSqlRaw("SQL SCRIPT").ToList();
CodeNotFound
la source
18
Cette réponse doit être la meilleure solution lors de l'utilisation d'EF Core 2.1 et supérieur. 👍
Will Huang
2
@CodeNotFound Que faire si je n'ai pas besoin du résultat ou s'il s'agit d'un type primitif (par exemple bit)?
Shimmy Weitzhandler
5
L'utilisation de CodeFirst a créé automatiquement une table avec toutes ces propriétés, l'ajout [NotMapped]à la SomeModelsclasse ne fonctionne pas pour moi. Ai-je manqué quelque chose?
Jean-Paul
7
EF Core 3.0 est obsolète DbQueryen faveur de l'utilisation uniquement DbSetavec des types d'entités sans clé .
NetMage
3
Juste pour info, en raison d'un bogue dans EF core 3.0, une migration code-first tentera toujours de créer une table même sur les entités marquées avec HasNoKey (). Vous devez donc ajouter également .ToView (null). Par exemple modelBuilder.Entity<MyData>().HasNoKey().ToView(null);@ Jean-Paul Je pense que cela résout votre problème
stann1
36

En me basant sur les autres réponses, j'ai écrit cet assistant qui accomplit la tâche, y compris un 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;
                }
            }
        }
    }

Usage:

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 une déclaration d'Arthur Vickers de l'équipe EF Core, il s'agit d'une priorité élevée pour le post 2.0. Le problème est suivi ici .

pie
la source
belle réponse, j'ai aimé.
sebu
31

Dans EF Core, vous ne pouvez plus exécuter SQL brut "gratuit". Vous devez définir une classe POCO et une DbSetpour cette classe. Dans votre cas, vous devrez définir le 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.

EDIT - Changement de rupture dans EF Core 3.0:

DbQuery () est maintenant obsolète, à la place DbSet () doit être utilisé (à nouveau). Si vous avez une entité sans clé, c'est-à-dire qu'elle ne nécessite pas de clé primaire, vous pouvez utiliser la méthode HasNoKey () :

ModelBuilder.Entity<SomeModel>().HasNoKey()

Plus d'informations peuvent être trouvées ici

E-Bat
la source
3
Je suppose donc que je devrai également étendre le DbContextpour inclure une nouvelle propriété DbSet<Rank> Rank { get; set; }. Quelles implications cela aura-t-il maintenant en référence à linq? C'est-à-dire que nous ne pourrons pas maintenant utiliser une instruction comme DBContext.Rank.Where(i => i.key == 1), et cette instruction n'aura-t-elle pas d'implémentation en SQL et échouera donc?
David Harlow
Les Linq émis contre cet ensemble doivent être résolus en mémoire. Si vous avez besoin d'émettre une clause WHERE sql différente, vous devez les inclure en tant que paramètres ou créer un script différent.
E-Bat
Mon DbSet n'a pas de méthode "FromSql". S'agit-il d'une extension qui me manque?
birwin
1
@birwin, vous devez importer l'espace de noms Microsoft.EntityFrameworkCore
E-Bat
20

Vous pouvez exécuter 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 un 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 son utilisation:

// 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]);
    }
}
Yehuda Goldenberg
la source
18

Pour l'instant, jusqu'à ce qu'il y ait quelque chose de nouveau d'EFCore, j'utiliserais une commande et la mapperais manuellement

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

      this.DbContext.Database.OpenConnection();

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

Essayez de SqlParameter pour éviter l'injection SQL.

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

FromSql ne fonctionne pas avec une requête complète. Exemple si vous souhaitez inclure une clause WHERE, elle sera ignorée.

Quelques liens:

Exécution de requêtes SQL brutes à l'aide d'Entity Framework Core

Requêtes SQL brutes

Henri
la source
7

Dans Core 2.1, vous pouvez faire quelque chose comme ceci:

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

puis définissez votre procédure SQL, comme:

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 Ranks ne sera pas créé dans votre base de données.

Maintenant, dans votre contrôleur / action, vous pouvez appeler:

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

De cette façon, vous pouvez appeler des procédures SQL brutes.

RodrigoCampos
la source
Les FromSqlparamètres peuvent être simplement passés sans créer d' SqlParameterobjet: FromSql($"STORED_PROCEDURE {value1}, {value2}")ou FromSql("STORED_PROCEDURE {0}, {1}", value1, value2)(ils seront échappés).
Majid
7

Vous pouvez utiliser ceci (à partir de https://github.com/aspnet/EntityFrameworkCore/issues/1862#issuecomment-451671168 ):

public static class SqlQueryExtensions
{
    public static IList<T> SqlQuery<T>(this DbContext db, string sql, params object[] parameters) where T : class
    {
        using (var db2 = new ContextForQueryType<T>(db.Database.GetDbConnection()))
        {
            return db2.Query<T>().FromSql(sql, parameters).ToList();
        }
    }

    private class ContextForQueryType<T> : DbContext where T : class
    {
        private readonly DbConnection connection;

        public ContextForQueryType(DbConnection connection)
        {
            this.connection = connection;
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            // switch on the connection type name to enable support multiple providers
            // var name = con.GetType().Name;
            optionsBuilder.UseSqlServer(connection, options => options.EnableRetryOnFailure());

            base.OnConfiguring(optionsBuilder);
        }

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

Et l'utilisation:

    using (var db = new Db())
    {
        var results = db.SqlQuery<ArbitraryType>("select 1 id, 'joe' name");
        //or with an anonymous type like this
        var results2 = db.SqlQuery(() => new { id =1, name=""},"select 1 id, 'joe' name");
    }
ErikEJ
la source
6

Ajouter un package Nuget - Microsoft.EntityFrameworkCore.Relational

using Microsoft.EntityFrameworkCore;
...
await YourContext.Database.ExecuteSqlCommandAsync("... @p0, @p1", param1, param2 ..)

Cela renverra les numéros de ligne sous la forme d'un entier

Voir - https://docs.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.relationaldatabasefacadeextensions.executesqlcommand?view=efcore-3.0

Mohsin
la source
3
Veuillez noter que cela ne renverra que le nombre de lignes affectées par la commande: stackoverflow.com/a/49861799/299756
kalyfe
Exactement ce dont j'ai besoin. J'utilise Microsoft.EntityFrameworkCore 3.1.1 et aucun moyen d'exécuter la requête RAW et le SP. Merci beaucoup pour cela!
jaysonragasa le
5

essayez ceci: (créer une méthode d'extension)

public static List<T> ExecuteQuery<T>(this dbContext db, string query) where T : class, new()
        {
            using (var command = db.Database.GetDbConnection().CreateCommand())
            {
                command.CommandText = query;
                command.CommandType = CommandType.Text;

                db.Database.OpenConnection();

                using (var reader = command.ExecuteReader())
                {
                    var lst = new List<T>();
                    var lstColumns = new T().GetType().GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).ToList();
                    while (reader.Read())
                    {
                        var newObject = new T();
                        for (var i = 0; i < reader.FieldCount; i++)
                        {
                            var name = reader.GetName(i);
                            PropertyInfo prop = lstColumns.FirstOrDefault(a => a.Name.ToLower().Equals(name.ToLower()));
                            if (prop == null)
                            {
                                continue;
                            }
                            var val = reader.IsDBNull(i) ? null : reader[i];
                            prop.SetValue(newObject, val, null);
                        }
                        lst.Add(newObject);
                    }

                    return lst;
                }
            }
        }

Usage:

var db = new dbContext();
string query = @"select ID , Name from People where ... ";
var lst = db.ExecuteQuery<PeopleView>(query);

mon modèle: (pas en DbSet):

public class PeopleView
{
    public int ID { get; set; }
    public string Name { get; set; }
}

testé en .netCore 2.2 and 3.0.

Remarque: cette solution a les performances lentes

AminRostami
la source
Essayez de rechercher PropertyInfo par nom une seule fois pour un premier enregistrement uniquement et créez un tableau de PropertyInfo [] par index de colonne à utiliser sur les enregistrements suivants.
Petr Voborník le
@AminRostami Nice Work
sebu
2

Ne ciblant pas directement le scénario du PO, mais depuis que j'ai du mal avec cela, j'aimerais abandonner ces ex. méthodes qui facilitent l'exécution du SQL brut avec DbContext:

public static class DbContextCommandExtensions
{
  public static async Task<int> ExecuteNonQueryAsync(this DbContext context, string rawSql,
    params object[] parameters)
  {
    var conn = context.Database.GetDbConnection();
    using (var command = conn.CreateCommand())
    {
      command.CommandText = rawSql;
      if (parameters != null)
        foreach (var p in parameters)
          command.Parameters.Add(p);
      await conn.OpenAsync();
      return await command.ExecuteNonQueryAsync();
    }
  }

  public static async Task<T> ExecuteScalarAsync<T>(this DbContext context, string rawSql,
    params object[] parameters)
  {
    var conn = context.Database.GetDbConnection();
    using (var command = conn.CreateCommand())
    {
      command.CommandText = rawSql;
      if (parameters != null)
        foreach (var p in parameters)
          command.Parameters.Add(p);
      await conn.OpenAsync();
      return (T)await command.ExecuteScalarAsync();
    }
  }
}
Shimmy Weitzhandler
la source
1

J'ai utilisé Dapper pour contourner cette contrainte de Entity Framework Core.

IDbConnection.Query

fonctionne avec une requête SQL ou une procédure stockée avec plusieurs paramètres. Au fait c'est un peu plus rapide (voir les tests de référence )

Dapper est facile à apprendre. Il a fallu 15 minutes pour écrire et exécuter une procédure stockée avec des paramètres. Quoi qu'il en soit, vous pouvez utiliser à la fois EF et Dapper. Voici un exemple:

 public class PodborsByParametersService
{
    string _connectionString = null;


    public PodborsByParametersService(string connStr)
    {
        this._connectionString = connStr;

    }

    public IList<TyreSearchResult> GetTyres(TyresPodborView pb,bool isPartner,string partnerId ,int pointId)
    {

        string sqltext  "spGetTyresPartnerToClient";

        var p = new DynamicParameters();
        p.Add("@PartnerID", partnerId);
        p.Add("@PartnerPointID", pointId);

        using (IDbConnection db = new SqlConnection(_connectionString))
        {
            return db.Query<TyreSearchResult>(sqltext, p,null,true,null,CommandType.StoredProcedure).ToList();
        }


        }
}
Lapenkov Vladimir
la source
0

Vous pouvez également utiliser QueryFirst . Comme Dapper, c'est totalement en dehors d'EF. Contrairement à Dapper (ou EF), vous n'avez pas besoin de maintenir le POCO, vous modifiez votre SQL SQL dans un environnement réel et il est continuellement revalidé par rapport à la base de données. Avertissement: je suis l'auteur de QueryFirst.

bbsimonbb
la source
0

Mon cas utilisait une procédure stockée au lieu du SQL brut

Créé une classe

Public class School
{
    [Key]
    public Guid SchoolId { get; set; }
    public string Name { get; set; }
    public string Branch { get; set; }
    public int NumberOfStudents  { get; set; }
}

Ajouté ci-dessous sur ma DbContextclasse

public DbSet<School> SP_Schools { get; set; }

Pour exécuter la procédure stockée:

var MySchools = _db.SP_Schools.FromSqlRaw("GetSchools @schoolId, @page, @size ",
              new SqlParameter("schoolId", schoolId),
              new SqlParameter("page", page),
              new SqlParameter("size", size)))
.IgnoreQueryFilters();
NoloMokgosi
la source
0

Je sais que c'est une vieille question, mais peut-être que cela aide quelqu'un à appeler des procédures stockées sans ajouter de DTO en tant que DbSets.

https://stackoverflow.com/a/62058345/3300944

Andrei
la source
0

Cette solution s'appuie fortement sur la solution de @pius. Je voulais ajouter l'option de prise en charge des paramètres de requête pour aider à atténuer l'injection SQL et je voulais également en faire une extension de DbContext DatabaseFacade pour Entity Framework Core pour le rendre un peu plus intégré.

Créez d'abord une nouvelle classe avec l'extension:

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Threading.Tasks;

namespace EF.Extend
{

    public static class ExecuteSqlExt
    {
        /// <summary>
        /// Execute raw SQL query with query parameters
        /// </summary>
        /// <typeparam name="T">the return type</typeparam>
        /// <param name="db">the database context database, usually _context.Database</param>
        /// <param name="query">the query string</param>
        /// <param name="map">the map to map the result to the object of type T</param>
        /// <param name="queryParameters">the collection of query parameters, if any</param>
        /// <returns></returns>
        public static List<T> ExecuteSqlRawExt<T, P>(this DatabaseFacade db, string query, Func<DbDataReader, T> map, IEnumerable<P> queryParameters = null)
        {
            using (var command = db.GetDbConnection().CreateCommand())
            {
                if((queryParameters?.Any() ?? false))
                    command.Parameters.AddRange(queryParameters.ToArray());

                command.CommandText = query;
                command.CommandType = CommandType.Text;

                db.OpenConnection();

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

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

                    return entities;
                }
            }
                
        }
    }

}

Notez dans ce qui précède que "T" est le type de retour et "P" est le type de vos paramètres de requête qui varieront selon que vous utilisez MySql, Sql, etc.

Ensuite, nous montrerons un exemple. J'utilise la fonctionnalité MySql EF Core, nous verrons donc comment nous pouvons utiliser l'extension générique ci-dessus avec cette implémentation MySql plus spécifique:

//add your using statement for the extension at the top of your Controller
//with all your other using statements
using EF.Extend;

//then your your Controller looks something like this
namespace Car.Api.Controllers
{

    //Define a quick Car class for the custom return type
    //you would want to put this in it's own class file probably
    public class Car
    {
        public string Make { get; set; }
        public string Model { get; set; }
        public string DisplayTitle { get; set; }
    }

    [ApiController]
    public class CarController : ControllerBase
    {
        private readonly ILogger<CarController> _logger;
        //this would be your Entity Framework Core context
        private readonly CarContext _context;

        public CarController(ILogger<CarController> logger, CarContext context)
        {
            _logger = logger;
            _context = context;
        }

        //... more stuff here ...

       /// <summary>
       /// Get car example
       /// </summary>
       [HttpGet]
       public IEnumerable<Car> Get()
       {
           //instantiate three query parameters to pass with the query
           //note the MySqlParameter type is because I'm using MySql
           MySqlParameter p1 = new MySqlParameter
           {
               ParameterName = "id1",
               Value = "25"
           };

           MySqlParameter p2 = new MySqlParameter
           {
               ParameterName = "id2",
               Value = "26"
           };

           MySqlParameter p3 = new MySqlParameter
           {
               ParameterName = "id3",
               Value = "27"
           };

           //add the 3 query parameters to an IEnumerable compatible list object
           List<MySqlParameter> queryParameters = new List<MySqlParameter>() { p1, p2, p3 };

           //note the extension is now easily accessed off the _context.Database object
           //also note for ExecuteSqlRawExt<Car, MySqlParameter>
           //Car is my return type "T"
           //MySqlParameter is the specific DbParameter type MySqlParameter type "P"
           List<Car> result = _context.Database.ExecuteSqlRawExt<Car, MySqlParameter>(
        "SELECT Car.Make, Car.Model, CONCAT_WS('', Car.Make, ' ', Car.Model) As DisplayTitle FROM Car WHERE Car.Id IN(@id1, @id2, @id3)",
        x => new Car { Make = (string)x[0], Model = (string)x[1], DisplayTitle = (string)x[2] }, 
        queryParameters);

           return result;
       }
    }
}

La requête renverrait des lignes telles que:
"Ford", "Explorer", "Ford Explorer"
"Tesla", "Model X", "Tesla Model X"

Le titre d'affichage n'est pas défini comme une colonne de base de données, il ne ferait donc pas partie du modèle EF Car par défaut. J'aime cette approche comme l'une des nombreuses solutions possibles. Les autres réponses de cette page font référence à d'autres moyens de résoudre ce problème avec le décorateur [NotMapped], qui selon votre cas d'utilisation pourrait être l'approche la plus appropriée.

Notez que le code de cet exemple est évidemment plus détaillé qu'il ne devrait l'être, mais j'ai pensé qu'il rendait l'exemple plus clair.

dan-iel
la source
-6

Avec Entity Framework 6, vous pouvez exécuter quelque chose comme ci-dessous

Créer une classe modale comme

Public class User
{
        public int Id { get; set; }
        public string fname { get; set; }
        public string lname { get; set; }
        public string username { get; set; }
}

Exécutez la commande Raw DQL SQl comme ci-dessous:

var userList = datacontext.Database.SqlQuery<User>(@"SELECT u.Id ,fname , lname ,username FROM dbo.Users").ToList<User>();
Siddhartha
la source