Surowe zapytanie SQL bez DbSet - Entity Framework Core

120

Po usunięciu Entity Framework Core dbData.Database.SqlQuery<SomeModel>nie mogę znaleźć rozwiązania do zbudowania surowego zapytania SQL dla mojego zapytania wyszukiwania pełnotekstowego, które zwróci dane tabel, a także pozycję.

Jedyną metodą, jaką widziałem do tworzenia surowego zapytania SQL w Entity Framework Core, jest metoda, dbData.Product.FromSql("SQL SCRIPT");która nie jest przydatna, ponieważ nie mam zestawu DbSet, który będzie mapował pozycję zwracaną w zapytaniu.

Jakieś pomysły???

David Harlow
źródło
15
Będzie mi bardzo brakowało SqlQuery <T> i nie będę musiał mapować niestandardowych klas na mój DbContext, kiedy naprawdę potrzebuję prostego DTO dla określonego przypadku użycia. Utworzyłem głos użytkownika, aby zażądać dodania tej funkcji z powrotem do EF Core, aby każdy mógł głosować, jeśli chciałby tę funkcję z powrotem: data.uservoice.com/forums/ ...
Matt Sanders
1
Według github.com/aspnet/EntityFramework/issues/1862 , jest to teraz przeznaczone dla EF core 1.2 i / lub 1.1.0-preview1
Dan Field
2
Opierając się na tym, co właśnie powiedział @Devon, spędziłem zbyt dużo czasu, zastanawiając się, czy są to metody rozszerzające w Microsoft.EntityFrameworkCore.SqlServer. Musisz dodać to do swojego projektu przed uzyskaniem tych metod rozszerzających.
Daniel
3
Westchnij, to wydaje się być jakąś decyzją astronauty architektury: „ludzie nie powinni tego chcieć”. Myślę, że muszę zainstalować Dapper tylko w tym przypadku. Denerwujący.
Dirk Boer
1
@MattSanders - w międzyczasie wydaje się, że odsyłacz głosowy jesteś użytkownikiem. Czy wiesz, gdzie to poszło?
Dirk Boer

Odpowiedzi:

139

To zależy od tego, czy używasz EF Core 2,1 lub EF Core 3 i wyższych wersji .

Jeśli używasz EF Core 2,1

Jeśli używasz EF Core 2.1 Release Candidate 1 dostępnego od 7 maja 2018 r., Możesz skorzystać z proponowanej nowej funkcji, którą jest typ zapytania.

Co to jest typ zapytania ?

Oprócz typów jednostek model EF Core może zawierać typy zapytań, których można używać do wykonywania zapytań bazy danych względem danych, które nie są zamapowane na typy jednostek.

Kiedy używać typu zapytania?

Służy jako typ zwracany dla zapytań ad hoc FromSql ().

Mapowanie do widoków bazy danych.

Mapowanie do tabel, które nie mają zdefiniowanego klucza podstawowego.

Mapowanie do zapytań zdefiniowanych w modelu.

Nie musisz więc już wykonywać wszystkich hacków ani obejść proponowanych jako odpowiedzi na Twoje pytanie. Po prostu wykonaj następujące kroki:

Najpierw zdefiniowałeś nową właściwość typu, DbQuery<T>gdzie Tjest typem klasy, która będzie zawierać wartości kolumn Twojego zapytania SQL. Więc w swoim DbContextbędziesz miał to:

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

Po drugie użyj FromSqlmetody takiej jak w przypadku DbSet<T>:

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

Zauważ również, że DdContextsą to klasy częściowe , więc możesz utworzyć jeden lub więcej oddzielnych plików, aby uporządkować definicje „surowego SQL DbQuery” tak, jak najbardziej Ci odpowiada.


Jeśli używasz EF Core 3,0 i wyższych wersji

Typ zapytania jest teraz znany jako typ jednostki bez kluczy . Jak wspomniano powyżej, typy zapytań zostały wprowadzone w EF Core 2,1. Jeśli używasz EF Core 3,0 lub nowszej wersji, powinieneś teraz używać typów tntity bez klucza, ponieważ typy zapytań są teraz oznaczone jako przestarzałe.

Ta funkcja została dodana w EF Core 2,1 pod nazwą typów zapytań. W EF Core 3,0 nazwę koncepcji zmieniono na bezkluczowe typy jednostek. Adnotacja danych [Keyless] stała się dostępna w EFCore 5.0.

Nadal mamy te same scenariusze, co w przypadku typów zapytań, kiedy należy używać bezkluczowego typu jednostki.

Więc go używać trzeba najpierw zaznaczyć swoją klasę SomeModelz [Keyless]adnotacją danych lub poprzez płynną konfiguracji z .HasNoKey()wywołania metody jak poniżej:

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

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

Po tej konfiguracji możesz użyć jednej z opisanych tutaj metod do wykonania zapytania SQL. Na przykład możesz użyć tego:

var result = context.SomeModels.FromSqlRaw("SQL SCRIPT").ToList();
CodeNotFound
źródło
18
Ta odpowiedź powinna być najlepszym rozwiązaniem w przypadku korzystania z EF Core 2,1 i nowszych. 👍
Will Huang
3
@CodeNotFound Co się stanie, jeśli nie potrzebuję wyniku lub jeśli jest to typ pierwotny (na przykład bit)?
Shimmy Weitzhandler
6
Za pomocą CodeFirst automatycznie utworzono tabelę ze wszystkimi tymi właściwościami, dodawanie [NotMapped]do SomeModelsklasy nie działa dla mnie. Czy coś przegapiłem?
Jean-Paul,
7
EF Core 3,0 przestaje być DbQueryużywany DbSetz typami jednostek bez klucza .
NetMage,
6
Do Twojej wiadomości, z powodu jakiegoś błędu w EF core 3.0, migracja najpierw kodu będzie nadal próbowała utworzyć tabelę nawet dla jednostek oznaczonych HasNoKey (). Musisz więc dodać również .ToView (null). Np. modelBuilder.Entity<MyData>().HasNoKey().ToView(null);@ Jean-Paul Myślę, że to rozwiązuje twój problem
stann1
40

Opierając się na innych odpowiedziach, napisałem pomocnika, który wykonuje zadanie, w tym przykładowe użycie:

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

Stosowanie:

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

Planuję się go pozbyć, gdy tylko zostanie dodana wbudowana obsługa. Według oświadczenia Arthura Vickersa z zespołu EF Core jest to wysoki priorytet dla postu 2.0. Problem jest śledzony tutaj .

pius
źródło
ładna odpowiedź, podobała mi się.
sebu
31

W EF Core nie można już wykonywać „wolnego” surowego sql. Musisz zdefiniować klasę POCO i DbSetdla tej klasy. W twoim przypadku będziesz musiał zdefiniować Rank :

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

Ponieważ z pewnością będzie to tylko do odczytu, przydatne będzie dołączenie .AsNoTracking()wezwania.

EDYCJA - przełomowa zmiana w EF Core 3.0:

DbQuery () jest teraz przestarzała, zamiast tego należy użyć DbSet () (ponownie). Jeśli masz jednostkę bezkluczową, czyli nie wymaga ona klucza podstawowego, możesz użyć metody HasNoKey () :

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

Więcej informacji można znaleźć tutaj

E-Bat
źródło
3
Więc myślę, że będę musiał także rozszerzyć DbContexto nową właściwość DbSet<Rank> Rank { get; set; }. Jakie konsekwencje będzie to miało teraz w odniesieniu do linq? To znaczy DBContext.Rank.Where(i => i.key == 1), czy nie będziemy mogli teraz użyć takiej instrukcji i czy ta instrukcja nie będzie miała implementacji w języku SQL i dlatego zawiedzie?
David Harlow,
Linq wyemitowany w tym zestawie musi zostać rozwiązany w pamięci. Jeśli chcesz emitować inną klauzulę WHERE sql, musisz dołączyć je jako parametry lub zbudować inny skrypt.
E-Bat
Mój zestaw DbSet nie ma metody „FromSql”. Czy to rozszerzenie, którego mi brakuje?
Birwin
1
@birwin, musisz zaimportować przestrzeń nazw Microsoft.EntityFrameworkCore
E-Bat
21

Możesz wykonać surowy plik SQL w EF Core - Dodaj tę klasę do projektu. Umożliwi to wykonanie surowego kodu SQL i uzyskanie surowych wyników bez konieczności definiowania POCO i DBSet. Oryginalny przykład można znaleźć na stronie https://github.com/aspnet/EntityFramework/issues/1862#issuecomment-220787464 .

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

Oto przykład, jak go używać:

// 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
źródło
19

Na razie, dopóki nie pojawi się coś nowego z EFCore, użyłem polecenia i zmapowałem to ręcznie

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

Spróbuj SqlParameter, aby uniknąć iniekcji Sql.

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

FromSql nie działa z pełnym zapytaniem. Przykład, jeśli chcesz dołączyć klauzulę WHERE, zostanie ona zignorowana.

Niektóre linki:

Wykonywanie surowych zapytań SQL przy użyciu Entity Framework Core

Surowe zapytania SQL

Henz
źródło
8

Możesz tego użyć (z 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.Entity<T>().HasNoKey();
            base.OnModelCreating(modelBuilder);
        }
    }
}

I użycie:

    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
źródło
modelBulider.Query<T>()jest przestarzały w EntityFramework Core 3
zafar
Zaktualizowano dla EF Core 3+
ErikEJ
7

W Core 2.1 możesz zrobić coś takiego:

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

a następnie zdefiniuj procedurę SQL, na przykład:

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

W ten sposób model rang nie zostanie utworzony w twojej bazie danych.

Teraz w kontrolerze / akcji możesz wywołać:

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

W ten sposób możesz wywołać Raw SQL Procedures.

RodrigoCampos
źródło
Parametry FromSqlmożna po prostu przekazać bez tworzenia SqlParameterobiektu: FromSql($"STORED_PROCEDURE {value1}, {value2}")lub FromSql("STORED_PROCEDURE {0}, {1}", value1, value2)(zostaną usunięte).
Majid
7

Dodaj pakiet Nuget - Microsoft.EntityFrameworkCore.Relational

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

To zwróci numery wierszy jako int

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

Mohsin
źródło
3
Należy pamiętać, że zwróci to tylko liczbę wierszy, na które ma wpływ polecenie: stackoverflow.com/a/49861799/299756
kalyfe
Dokładnie to, czego potrzebuję. Używam Microsoft.EntityFrameworkCore 3.1.1 i nie ma możliwości wykonania zapytania RAW i SP. Bardzo ci za to dziękuję!
jaysonragasa
5

spróbuj tego: (utwórz metodę rozszerzenia)

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

Stosowanie:

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

mój model: (nie w DbSet):

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

przetestowany w .netCore 2.2 and 3.0.

Uwaga: to rozwiązanie ma niską wydajność

AminRostami
źródło
Spróbuj przeszukać PropertyInfo według nazwy tylko raz dla pierwszego rekordu i zbuduj tablicę PropertyInfo [] według indeksów kolumn do użycia w następnych rekordach.
Petr Voborník
@AminRostami Nice Work
sebu
2

Nie jest ukierunkowany bezpośrednio na scenariusz OP, ale ponieważ walczyłem z tym, chciałbym porzucić te ex. metody, które ułatwiają wykonywanie surowego kodu SQL za pomocą 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
źródło
1

Użyłem Dappera, aby ominąć to ograniczenie Entity Framework Core.

IDbConnection.Query

działa z zapytaniem sql lub procedurą składowaną z wieloma parametrami. Swoją drogą jest trochę szybszy (zobacz testy porównawcze )

Dapper jest łatwy do nauczenia. Zapisanie i uruchomienie procedury składowanej z parametrami zajęło 15 minut. W każdym razie możesz używać zarówno EF, jak i Dapper. Poniżej przykład:

 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
źródło
0

Możesz także użyć QueryFirst . Podobnie jak Dapper, jest to całkowicie poza EF. W przeciwieństwie do Dapper (lub EF), nie musisz utrzymywać POCO, edytujesz SQL SQL w prawdziwym środowisku i jest on stale weryfikowany względem bazy danych. Zastrzeżenie: jestem autorem QueryFirst.

bbsimonbb
źródło
0

W moim przypadku zamiast surowego kodu SQL użyto procedury składowanej

Stworzył klasę

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

Dodano poniżej w mojej DbContextklasie

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

Aby wykonać procedurę składowaną:

var MySchools = _db.SP_Schools.FromSqlRaw("GetSchools @schoolId, @page, @size ",
              new SqlParameter("schoolId", schoolId),
              new SqlParameter("page", page),
              new SqlParameter("size", size)))
.IgnoreQueryFilters();
NoloMokgosi
źródło
0

To rozwiązanie opiera się w dużej mierze na rozwiązaniu firmy @pius. Chciałem dodać opcję obsługi parametrów zapytania, aby pomóc złagodzić wstrzykiwanie SQL, a także chciałem, aby było to rozszerzenie poza DbContext DatabaseFacade dla Entity Framework Core, aby było trochę bardziej zintegrowane.

Najpierw utwórz nową klasę z rozszerzeniem:

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

}

Zauważ w powyższym, że „T” jest typem zwracanej wartości, a „P” jest typem parametrów zapytania, które będą się różnić w zależności od tego, czy używasz MySql, Sql, itd.

Następnie pokażemy przykład. Używam możliwości MySql EF Core, więc zobaczymy, jak możemy użyć powyższego rozszerzenia ogólnego z tą bardziej szczegółową implementacją MySql:

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

Zapytanie zwróci takie wiersze, jak:
„Ford”, „Explorer”, „Ford Explorer”,
„Tesla”, „Model X”, „Tesla Model X”

Tytuł wyświetlany nie jest zdefiniowany jako kolumna bazy danych, więc domyślnie nie byłby częścią modelu EF Car. Podoba mi się to podejście jako jedno z wielu możliwych rozwiązań. Inne odpowiedzi na tej stronie odnoszą się do innych sposobów rozwiązania tego problemu za pomocą dekoratora [NotMapped], który w zależności od przypadku użycia może być bardziej odpowiednim podejściem.

Zwróć uwagę, że kod w tym przykładzie jest oczywiście bardziej szczegółowy, niż powinien, ale pomyślałem, że dzięki temu przykład jest jaśniejszy.

dan-iel
źródło
0

Właściwie możesz utworzyć ogólne repozytorium i zrobić coś takiego

public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : BaseEntity
{
    private readonly DataContext context;
    private readonly DbSet<TEntity> dbSet;

    public GenericRepository(DataContext context)
    {
        this.context = context;
        this.dbSet = context.Set<TEntity>();
    }

   
    public IEnumerable<TEntity> ExecuteCommandQuery(string command)
        => dbSet.FromSqlRaw(command);

}
Denis
źródło
dodatkowo: Microsoft.EntityFrameworkCore nie zawiera FromSqlRaw. Jest konieczne, aby zainstalować Microsoft.EntityFrameworkCore.Relational, aby użyć tej metody.
Kate
-7

Z Entity Framework 6 możesz wykonać coś takiego jak poniżej

Utwórz klasę modalną jako

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

Wykonaj surową komendę DQL SQl jak poniżej:

var userList = datacontext.Database.SqlQuery<User>(@"SELECT u.Id ,fname , lname ,username FROM dbo.Users").ToList<User>();
Siddhartha
źródło