Kueri SQL Mentah tanpa DbSet - Entity Framework Core

109

Dengan penghapusan Entity Framework Core, dbData.Database.SqlQuery<SomeModel>saya tidak dapat menemukan solusi untuk membuat SQL Query mentah untuk kueri penelusuran teks lengkap saya yang akan mengembalikan data tabel dan juga peringkatnya.

Satu-satunya metode yang saya lihat untuk membangun kueri SQL mentah di Entity Framework Core adalah melalui dbData.Product.FromSql("SQL SCRIPT");yang tidak berguna karena saya tidak memiliki DbSet yang akan memetakan peringkat yang saya kembalikan dalam kueri.

Ada Ide ???

David Harlow
sumber
15
Saya akan sangat merindukan SqlQuery <T> dan tidak ingin harus memetakan kelas khusus ke DbContext saya ketika saya benar-benar hanya membutuhkan DTO sederhana untuk kasus penggunaan tertentu. Saya telah membuat suara pengguna untuk meminta penambahan fitur ini kembali ke EF Core sehingga siapa pun dapat memilih jika mereka menginginkan fitur ini kembali: data.uservoice.com/forums/…
Matt Sanders
1
Menurut github.com/aspnet/EntityFramework/issues/1862 , ini sekarang ditargetkan untuk EF core 1.2 dan / atau 1.1.0-preview1
Dan Field
2
Berdasarkan apa yang baru saja dikatakan @Devon, saya menghabiskan waktu terlalu lama untuk mencari tahu bahwa itu adalah metode ekstensi di Microsoft.EntityFrameworkCore.SqlServer. Anda harus menambahkannya ke proyek Anda sebelum mendapatkan metode ekstensi ini.
Daniel
3
Sigh ini sepertinya semacam keputusan Astronot Arsitektur: "orang tidak perlu menginginkan ini". Saya rasa saya harus menginstal Dapper hanya untuk kasus ini. Mengganggu.
Dirk Boer
1
@MattSanders - tautan suaramu sepertinya mati untuk sementara. Tahukah Anda kemana perginya?
Dirk Boer

Jawaban:

129

Itu tergantung jika Anda menggunakan EF Core 2.1 atau EF Core 3 dan versi yang lebih tinggi .

Jika Anda menggunakan EF Core 2.1

Jika Anda menggunakan EF Core 2.1 Release Candidate 1 yang tersedia sejak 7 Mei 2018, Anda dapat memanfaatkan fitur baru yang diusulkan yaitu tipe Query.

Apa itu tipe kueri ?

Selain tipe entitas, model EF Core bisa berisi tipe kueri, yang bisa digunakan untuk menjalankan kueri database terhadap data yang tidak dipetakan ke tipe entitas.

Kapan menggunakan tipe kueri?

Berfungsi sebagai tipe kembalian untuk kueri FromSql () ad hoc.

Memetakan ke tampilan database.

Memetakan ke tabel yang tidak memiliki kunci utama yang ditentukan.

Memetakan ke kueri yang ditentukan dalam model.

Jadi, Anda tidak perlu lagi melakukan semua peretasan atau solusi yang diusulkan sebagai jawaban atas pertanyaan Anda. Ikuti saja langkah-langkah ini:

Pertama, Anda menentukan properti tipe baru di DbQuery<T>mana Ttipe kelas yang akan membawa nilai kolom kueri SQL Anda. Jadi di dalam diri Anda, DbContextAnda akan memiliki ini:

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

Kedua, gunakan FromSqlmetode seperti yang Anda lakukan dengan DbSet<T>:

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

Perhatikan juga bahwa DdContexts adalah kelas parsial , sehingga Anda dapat membuat satu atau beberapa file terpisah untuk mengatur definisi 'SQL DbQuery mentah' yang paling sesuai untuk Anda.


Jika Anda menggunakan EF Core 3.0 dan versi yang lebih tinggi

Jenis kueri sekarang dikenal sebagai jenis entitas tanpa kunci . Seperti yang dikatakan di atas, jenis kueri diperkenalkan di EF Core 2.1. Jika Anda menggunakan EF Core 3.0 atau versi yang lebih tinggi, Anda sekarang harus mempertimbangkan untuk menggunakan tipe tntity tanpa kunci karena tipe kueri sekarang ditandai usang.

Fitur ini ditambahkan di EF Core 2.1 dengan nama tipe kueri. Dalam EF Core 3.0, konsep tersebut diubah namanya menjadi tipe entitas tanpa kunci. Anotasi Data [Tanpa Kunci] tersedia di EFCore 5.0.

Kami masih memiliki skenario yang sama seperti untuk tipe kueri tentang kapan harus menggunakan tipe entitas tanpa kunci.

Jadi untuk menggunakannya, Anda perlu menandai kelas Anda SomeModeldengan [Keyless]anotasi data atau melalui konfigurasi yang lancar dengan .HasNoKey()pemanggilan metode seperti di bawah ini:

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

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

Setelah konfigurasi tersebut, Anda dapat menggunakan salah satu metode yang dijelaskan di sini untuk menjalankan kueri SQL Anda. Misalnya Anda bisa menggunakan yang ini:

var result = context.SomeModels.FromSqlRaw("SQL SCRIPT").ToList();
CodeNotFound
sumber
18
Jawaban ini seharusnya menjadi solusi terbaik saat menggunakan EF Core 2.1 ke atas. 👍
Will Huang
2
@CodeNotFound Bagaimana jika saya tidak memerlukan hasilnya atau jika itu adalah tipe primitif (misalnya bit)?
Shimmy Weitzhandler
6
Menggunakan CodeFirst ini secara otomatis membuat tabel dengan semua properti tersebut, menambahkan [NotMapped]ke SomeModelskelas tidak bekerja untuk saya. Apakah saya melewatkan sesuatu?
Jean-Paul
7
EF Core 3.0 tidak digunakan lagi DbQuerykarena hanya menggunakan DbSetdengan jenis entitas tanpa kunci .
NetMage
6
Untuk diketahui, karena beberapa bug di EF core 3.0, migrasi kode-pertama masih akan mencoba membuat tabel bahkan pada entitas yang ditandai dengan HasNoKey (). Jadi, Anda juga harus menambahkan .ToView (null). Misalnya modelBuilder.Entity<MyData>().HasNoKey().ToView(null);@ Jean-Paul Saya pikir ini menyelesaikan masalah Anda
stann1
37

Berdasarkan jawaban lain, saya telah menulis pembantu ini yang menyelesaikan tugas, termasuk contoh penggunaan:

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

Pemakaian:

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

Saya berencana untuk menyingkirkannya segera setelah dukungan bawaan ditambahkan. Menurut pernyataan Arthur Vickers dari tim EF Core, pos 2.0 adalah prioritas tinggi. Masalahnya dilacak di sini .

pius
sumber
jawaban yang bagus, menyukainya.
sebu
31

Di EF Core Anda tidak lagi dapat mengeksekusi sql "gratis". Anda diminta untuk menentukan kelas POCO dan DbSetkelas itu. Dalam kasus Anda, Anda perlu menentukan Peringkat :

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

Karena akan menjadi hanya-baca, akan berguna untuk menyertakan .AsNoTracking()panggilan tersebut.

EDIT - Perubahan besar dalam EF Core 3.0:

DbQuery () sekarang sudah usang, sebagai gantinya DbSet () harus digunakan (lagi). Jika Anda memiliki entitas tanpa kunci, yaitu tidak memerlukan kunci utama, Anda dapat menggunakan metode HasNoKey () :

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

Informasi lebih lanjut dapat ditemukan di sini

E-Bat
sumber
3
Jadi saya rasa saya juga harus memperluas DbContextuntuk menyertakan properti baru DbSet<Rank> Rank { get; set; }. Implikasi apa yang akan dimilikinya sekarang sehubungan dengan LINQ? Ie Bukankah sekarang kita dapat menggunakan pernyataan seperti DBContext.Rank.Where(i => i.key == 1), dan bukankah pernyataan ini tidak memiliki implementasi dalam SQL dan karena itu gagal?
David Harlow
Linq yang dipancarkan terhadap set ini harus diselesaikan dalam memori. Jika Anda perlu mengeluarkan klausa WHERE sql yang berbeda, Anda harus memasukkannya sebagai parameter atau membuat skrip yang berbeda.
E-Bat
DbSet saya tidak memiliki metode "FromSql". Apakah ini perpanjangan yang saya lewatkan?
birwin
1
@birwin, Anda perlu mengimpor namespace Microsoft.EntityFrameworkCore
E-Bat
20

Anda dapat mengeksekusi sql mentah di EF Core - Tambahkan kelas ini ke proyek Anda. Ini akan memungkinkan Anda untuk mengeksekusi SQL mentah dan mendapatkan hasil mentah tanpa harus mendefinisikan POCO dan DBSet. Lihat https://github.com/aspnet/EntityFramework/issues/1862#issuecomment-220787464 untuk contoh asli.

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

Berikut contoh cara menggunakannya:

// 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
sumber
18

Untuk saat ini, sampai ada sesuatu yang baru dari EFCore saya akan menggunakan perintah dan memetakannya secara manual

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

Cobalah untuk SqlParameter untuk menghindari Sql Injection.

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

FromSql tidak berfungsi dengan kueri lengkap. Contoh jika Anda ingin memasukkan klausa WHERE maka akan diabaikan.

Beberapa Tautan:

Menjalankan Kueri SQL Mentah menggunakan Entity Framework Core

Kueri SQL Mentah

Henry
sumber
7

Di Core 2.1 Anda dapat melakukan sesuatu seperti ini:

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

lalu tentukan Prosedur SQL Anda, seperti:

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

Dengan cara ini model Peringkat tidak akan dibuat di DB Anda.

Sekarang di pengontrol / tindakan Anda, Anda dapat memanggil:

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

Dengan cara ini Anda dapat memanggil Prosedur SQL Baku.

RodrigoCampos
sumber
The FromSqlparams bisa hanya berlalu tanpa membuat SqlParameterobjek: FromSql($"STORED_PROCEDURE {value1}, {value2}")atau FromSql("STORED_PROCEDURE {0}, {1}", value1, value2)(mereka akan melarikan diri).
Majid
7

Anda dapat menggunakan ini (dari 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);
        }
    }
}

Dan penggunaannya:

    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
sumber
6

Tambahkan paket Nuget - Microsoft.EntityFrameworkCore.Relational

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

Ini akan mengembalikan nomor baris sebagai int

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

Mohsin
sumber
3
Harap dicatat bahwa ini hanya akan mengembalikan jumlah baris yang dipengaruhi oleh perintah: stackoverflow.com/a/49861799/299756
kalyfe
Persis yang saya butuhkan. Saya menggunakan Microsoft.EntityFrameworkCore 3.1.1 dan tidak ada cara untuk menjalankan kueri RAW dan SP. Terima kasih banyak untuk ini!
jaysonragasa
5

coba ini: (buat metode ekstensi)

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

Pemakaian:

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

model saya: (tidak dalam DbSet):

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

diuji .netCore 2.2 and 3.0.

Catatan: solusi ini memiliki kinerja yang lambat

AminRostami
sumber
Coba cari PropertyInfo berdasarkan nama hanya sekali untuk catatan pertama saja dan buat array PropertyInfo [] menurut indeks kolom untuk digunakan pada catatan berikutnya.
Petr Voborník
@AminRostami Nice Work
sebu
2

Tidak secara langsung menargetkan skenario OP, tetapi karena saya telah berjuang dengan ini, saya ingin melepaskan mantan ini. metode yang membuatnya lebih mudah untuk mengeksekusi SQL mentah dengan 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
sumber
1

Saya menggunakan Dapper untuk melewati kendala Entity framework Core ini.

IDbConnection.Query

bekerja dengan kueri sql atau prosedur tersimpan dengan beberapa parameter. Ngomong-ngomong, ini sedikit lebih cepat (lihat tes benchmark )

Dapper mudah dipelajari. Butuh waktu 15 menit untuk menulis dan menjalankan prosedur tersimpan dengan parameter. Bagaimanapun Anda dapat menggunakan EF dan Dapper. Berikut ini contohnya:

 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
sumber
0

Anda juga dapat menggunakan QueryFirst . Seperti Dapper, ini sama sekali di luar EF. Tidak seperti Dapper (atau EF), Anda tidak perlu mempertahankan POCO, Anda mengedit SQL sql Anda di lingkungan nyata, dan terus divalidasi ulang terhadap DB. Penafian: Saya adalah penulis QueryFirst.

bbsimonbb
sumber
0

Kasus saya menggunakan prosedur tersimpan, bukan SQL mentah

Membuat kelas

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

Ditambahkan di bawah di DbContextkelas saya

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

Untuk menjalankan prosedur tersimpan:

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

Saya tahu ini pertanyaan lama, tapi mungkin ini membantu seseorang untuk memanggil prosedur yang tersimpan tanpa menambahkan DTO sebagai DbSets.

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

Andrei
sumber
0

Solusi ini sangat bergantung pada solusi dari @pius. Saya ingin menambahkan opsi untuk mendukung parameter kueri guna membantu mengurangi injeksi SQL dan saya juga ingin menjadikannya perpanjangan dari DbContext DatabaseFacade untuk Entity Framework Core untuk membuatnya sedikit lebih terintegrasi.

Pertama buat kelas baru dengan ekstensi:

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

}

Perhatikan di atas bahwa "T" adalah tipe untuk pengembalian dan "P" adalah jenis parameter kueri Anda yang akan bervariasi berdasarkan jika Anda menggunakan MySql, Sql, dan seterusnya.

Selanjutnya kami akan menunjukkan contoh. Saya menggunakan kemampuan MySql EF Core, jadi kita akan melihat bagaimana kita dapat menggunakan ekstensi generik di atas dengan implementasi MySql yang lebih spesifik ini:

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

Kueri akan mengembalikan baris seperti:
"Ford", "Explorer", "Ford Explorer"
"Tesla", "Model X", "Tesla Model X"

Judul tampilan tidak didefinisikan sebagai kolom database, jadi tidak akan menjadi bagian dari model Mobil EF secara default. Saya menyukai pendekatan ini sebagai salah satu dari banyak kemungkinan solusi. Jawaban lain di halaman ini merujuk pada cara lain untuk mengatasi masalah ini dengan dekorator [NotMapped], yang bergantung pada kasus penggunaan Anda bisa menjadi pendekatan yang lebih tepat.

Perhatikan kode dalam contoh ini jelas lebih bertele-tele daripada yang seharusnya, tetapi saya pikir itu membuat contoh lebih jelas.

dan-iel
sumber
0

Sebenarnya Anda dapat membuat repositori generik dan melakukan sesuatu seperti ini

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

Dengan Entity Framework 6 Anda dapat menjalankan sesuatu seperti di bawah ini

Buat Kelas Modal sebagai

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

Jalankan perintah Raw DQL SQl seperti di bawah ini:

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