แบบสอบถาม SQL ดิบที่ไม่มี DbSet - Entity Framework Core


124

ด้วยการลบ Entity Framework Core dbData.Database.SqlQuery<SomeModel>ฉันไม่พบวิธีแก้ปัญหาในการสร้างแบบสอบถาม SQL ดิบสำหรับข้อความค้นหาแบบเต็มของฉันที่จะส่งคืนข้อมูลตารางและอันดับด้วย

วิธีเดียวที่ฉันเคยเห็นในการสร้างแบบสอบถาม SQL ดิบใน Entity Framework Core คือผ่านdbData.Product.FromSql("SQL SCRIPT");ซึ่งไม่มีประโยชน์เนื่องจากฉันไม่มี DbSet ที่จะแมปอันดับที่ฉันกลับมาในแบบสอบถาม

ความคิดใด ๆ ???


16
ฉันจะพลาด SqlQuery <T> อย่างมากและไม่ต้องการแมปคลาสที่กำหนดเองกับ DbContext ของฉันเมื่อฉันต้องการ DTO ง่ายๆสำหรับกรณีการใช้งานเฉพาะ ฉันได้สร้างเสียงของผู้ใช้เพื่อขอให้เพิ่มคุณสมบัตินี้กลับเข้าไปใน EF Core ซึ่งทุกคนสามารถโหวตได้หากต้องการคุณสมบัตินี้กลับคืน: data.uservoice.com/forums/…
Matt Sanders

1
อ้างอิงจากgithub.com/aspnet/EntityFramework/issues/1862ตอนนี้กำหนดเป้าหมายสำหรับ EF core 1.2 และ / หรือ 1.1.0-preview1
Dan Field

2
จากสิ่งที่ @Devon กล่าวไว้ฉันใช้เวลานานเกินไปในการหาวิธีการขยายใน Microsoft.EntityFrameworkCore.SqlServer คุณจะต้องเพิ่มสิ่งนั้นในโครงการของคุณก่อนที่จะรับวิธีการขยายเหล่านี้
Daniel

3
เฮ้อนี้ดูเหมือนว่าชนิดของการตัดสินใจสถาปัตยกรรมนักบินอวกาศบางส่วน: "คนที่ไม่ควรจะต้องอยากให้เรื่องนี้" ฉันเดาว่าฉันต้องติดตั้ง Dapper สำหรับกรณีนี้เท่านั้น น่ารำคาญ.
Dirk Boer

1
@MattSanders - คุณเป็นผู้ใช้บริการลิงค์ดูเหมือนจะตายในระหว่างนี้ คุณรู้ไหมว่ามันไปไหน?
Dirk Boer

คำตอบ:


143

มันขึ้นอยู่กับว่าคุณกำลังใช้EF หลัก 2.1หรือEF หลัก 3 และรุ่นที่สูงขึ้น

หากคุณใช้ EF Core 2.1

หากคุณใช้ EF Core 2.1 Release Candidate 1 ที่พร้อมใช้งานตั้งแต่วันที่ 7 พฤษภาคม 2018 คุณสามารถใช้ประโยชน์จากคุณลักษณะใหม่ที่เสนอซึ่งเป็นประเภท Query

อะไรคือสิ่งที่ชนิดของแบบสอบถาม ?

นอกเหนือจากประเภทเอนทิตีโมเดล EF Core ยังมีประเภทคิวรีซึ่งสามารถใช้เพื่อดำเนินการสืบค้นฐานข้อมูลกับข้อมูลที่ไม่ได้แมปกับประเภทเอนทิตี

เมื่อใดควรใช้ประเภทแบบสอบถาม

ทำหน้าที่เป็นประเภทผลตอบแทนสำหรับแบบสอบถาม FromSql () เฉพาะกิจ

การแม็พกับมุมมองฐานข้อมูล

การแมปไปยังตารางที่ไม่ได้กำหนดคีย์หลัก

การแมปกับคิวรีที่กำหนดไว้ในโมเดล

ดังนั้นคุณจึงไม่จำเป็นต้องทำการแฮ็กหรือวิธีแก้ไขปัญหาทั้งหมดที่เสนอเป็นคำตอบสำหรับคำถามของคุณอีกต่อไป เพียงทำตามขั้นตอนเหล่านี้:

ก่อนอื่นคุณกำหนดคุณสมบัติใหม่ของประเภทDbQuery<T>ซึ่งTเป็นประเภทของคลาสที่จะนำค่าคอลัมน์ของแบบสอบถาม SQL ของคุณ ดังนั้นในของDbContextคุณคุณจะมีสิ่งนี้:

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

ประการที่สองใช้FromSqlวิธีการเช่นเดียวกับที่คุณทำกับDbSet<T>:

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

โปรดทราบว่าDdContexts เป็นคลาสบางส่วนดังนั้นคุณสามารถสร้างไฟล์แยกต่างหากตั้งแต่หนึ่งไฟล์ขึ้นไปเพื่อจัดระเบียบนิยาม 'SQL DbQuery ดิบ' ของคุณให้เหมาะกับคุณที่สุด


หากคุณใช้ EF Core 3.0 และเวอร์ชันที่สูงกว่า

ประเภทการสืบค้นปัจจุบันเรียกว่าประเภทเอนทิตีแบบไม่ใช้คีย์ ดังที่กล่าวไว้ข้างต้นประเภทการค้นหาถูกนำมาใช้ใน EF Core 2.1 หากคุณใช้ EF Core 3.0 หรือเวอร์ชันที่สูงกว่าตอนนี้คุณควรปรับตัวโดยใช้ประเภท tntity แบบไม่ใช้คีย์เนื่องจากขณะนี้ประเภทการสืบค้นถูกทำเครื่องหมายว่าล้าสมัย

คุณลักษณะนี้ถูกเพิ่มเข้ามาใน EF Core 2.1 ภายใต้ชื่อประเภทแบบสอบถาม ใน EF Core 3.0 แนวคิดถูกเปลี่ยนชื่อเป็นประเภทเอนทิตีแบบไม่ใช้คีย์ คำอธิบายประกอบข้อมูล [Keyless] พร้อมใช้งานใน EFCore 5.0

เรายังคงมีสถานการณ์เช่นเดียวกับประเภทคิวรีเมื่อจะใช้ประเภทเอนทิตีแบบไม่ใช้คีย์

ดังนั้นในการใช้งานคุณต้องทำเครื่องหมายชั้นเรียนของคุณSomeModelด้วย[Keyless]คำอธิบายประกอบข้อมูลก่อนหรือผ่านการกำหนดค่าอย่างคล่องแคล่วด้วย.HasNoKey()การเรียกวิธีการดังต่อไปนี้:

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

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

หลังจากกำหนดค่าแล้วคุณสามารถใช้หนึ่งในวิธีการที่อธิบายไว้ที่นี่เพื่อดำเนินการสืบค้น SQL ของคุณ ตัวอย่างเช่นคุณสามารถใช้อันนี้:

var result = context.SomeModels.FromSqlRaw("SQL SCRIPT").ToList();

19
คำตอบนี้น่าจะเป็นทางออกที่ดีที่สุดเมื่อใช้ EF Core 2.1 ขึ้นไป 👍
Will Huang

3
@CodeNotFound จะเกิดอะไรขึ้นถ้าฉันไม่ต้องการผลลัพธ์หรือถ้าเป็นประเภทดั้งเดิม (ตัวอย่างbit)
Shimmy Weitzhandler

7
การใช้ CodeFirst สิ่งนี้จะสร้างตารางโดยอัตโนมัติพร้อมคุณสมบัติเหล่านั้นทั้งหมดการเพิ่ม[NotMapped]ลงในSomeModelsคลาสไม่ได้ผลสำหรับฉัน ฉันพลาดอะไรไปหรือเปล่า?
Jean-Paul

8
EF หลัก 3.0 deprecates DbQueryในความโปรดปรานของเพียงแค่ใช้DbSetกับประเภทนิติบุคคล keyless
NetMage

8
เพียงแค่ FYI เนื่องจากข้อบกพร่องบางอย่างใน EF core 3.0 การย้ายรหัสครั้งแรกจะยังคงพยายามสร้างตารางแม้ในเอนทิตีที่มีเครื่องหมาย HasNoKey () ดังนั้นคุณต้องเพิ่ม. ToView (null) ด้วย เช่นmodelBuilder.Entity<MyData>().HasNoKey().ToView(null);@ Jean-Paul ฉันคิดว่าสิ่งนี้ช่วยแก้ปัญหาของคุณได้
stann1

44

จากคำตอบอื่น ๆ ที่ฉันได้เขียนผู้ช่วยนี้เพื่อให้งานสำเร็จรวมถึงตัวอย่างการใช้งาน:

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

การใช้งาน:

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

ฉันวางแผนที่จะกำจัดมันทันทีที่มีการเพิ่มการสนับสนุนในตัว ตามคำกล่าวของ Arthur Vickers จากทีม EF Core มีความสำคัญสูงสำหรับโพสต์ 2.0 ปัญหาที่มีการติดตามที่นี่


คำตอบที่ดีชอบมัน
sebu

32

ใน EF Core คุณจะไม่สามารถรันไฟล์ raw sql ได้อีกต่อไป คุณจะต้องกำหนดคลาส POCO และDbSetคลาสนั้น ในกรณีของคุณคุณจะต้องกำหนดอันดับ :

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

เนื่องจากจะเป็นแบบอ่านอย่างเดียวจึงเป็นประโยชน์ในการรวมการ.AsNoTracking()โทร

แก้ไข - การเปลี่ยนแปลงใน EF Core 3.0:

ขณะนี้DbQuery ()ล้าสมัยแล้วควรใช้DbSet ()แทน(อีกครั้ง) หากคุณมีเอนทิตีแบบไม่ใช้คีย์กล่าวคือไม่ต้องการคีย์หลักคุณสามารถใช้HasNoKey ()เมธอด:

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

สามารถดูข้อมูลเพิ่มเติมได้ที่นี่


3
ดังนั้นฉันเดาว่าฉันจะต้องขยายDbContextเพื่อรวมคุณสมบัติใหม่DbSet<Rank> Rank { get; set; }ด้วย สิ่งนี้จะมีผลกระทบอะไรบ้างในการอ้างอิงถึง linq? เช่นตอนนี้เราไม่สามารถใช้คำสั่งเช่นDBContext.Rank.Where(i => i.key == 1)นี้ได้หรือไม่และคำสั่งนี้จะไม่มีการใช้งานใน SQL ดังนั้นจึงล้มเหลวหรือไม่?
David Harlow

Linq ที่ปล่อยออกมาจากชุดนี้จะต้องได้รับการแก้ไขในหน่วยความจำ หากคุณต้องการเปล่งคำสั่ง WHERE sql ที่แตกต่างกันคุณต้องรวมไว้เป็นพารามิเตอร์หรือสร้างสคริปต์อื่น
E-Bat

DbSet ของฉันไม่มีเมธอด "FromSql" นี่คือส่วนขยายที่ฉันหายไปหรือไม่
birwin

1
@birwin คุณต้องนำเข้าเนมสเปซ Microsoft.EntityFrameworkCore
E-Bat

21

คุณสามารถรัน raw sql ใน EF Core - เพิ่มคลาสนี้ในโปรเจ็กต์ของคุณ สิ่งนี้จะช่วยให้คุณดำเนินการ SQL ดิบและรับผลลัพธ์ดิบโดยไม่ต้องกำหนด POCO และ DBSet ดู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);
            }
        }
    }
}

นี่คือตัวอย่างวิธีการใช้งาน:

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

20

สำหรับตอนนี้จนกว่าจะมีสิ่งใหม่จาก EFCore ฉันจะใช้คำสั่งและแมปด้วยตนเอง

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

พยายาม SqlParameter เพื่อหลีกเลี่ยง Sql Injection

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

FromSql ไม่ทำงานกับแบบสอบถามแบบเต็ม ตัวอย่างเช่นหากคุณต้องการรวมคำสั่ง WHERE คำสั่งนั้นจะถูกละเว้น

ลิงค์บางส่วน:

การดำเนินการสืบค้น Raw SQL โดยใช้ Entity Framework Core

แบบสอบถาม SQL ดิบ


8

คุณสามารถใช้สิ่งนี้ (จาก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);
        }
    }
}

และการใช้งาน:

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

modelBulider.Query<T>()เลิกใช้แล้วใน EntityFramework Core 3
zafar

อัปเดตสำหรับ EF Core 3+
ErikEJ

7

ใน Core 2.1 คุณสามารถทำสิ่งนี้ได้:

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

จากนั้นกำหนด SQL Procedure ของคุณเช่น:

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

วิธีนี้จะไม่สร้างโมเดลอันดับในฐานข้อมูลของคุณ

ตอนนี้ในตัวควบคุม / การกระทำของคุณคุณสามารถโทร:

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

วิธีนี้คุณสามารถเรียก Raw SQL Procedures


FromSqlparams จะถูกส่งผ่านไปเพียงโดยไม่ต้องสร้างSqlParameterวัตถุ: FromSql($"STORED_PROCEDURE {value1}, {value2}")หรือFromSql("STORED_PROCEDURE {0}, {1}", value1, value2)(พวกเขาจะหนี)
Majid

7

เพิ่มแพ็คเกจ Nuget - Microsoft.EntityFrameworkCore.Relational

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

สิ่งนี้จะส่งคืนหมายเลขแถวเป็น int

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


3
โปรดทราบว่าสิ่งนี้จะส่งคืนเฉพาะจำนวนแถวที่ได้รับผลกระทบจากคำสั่ง: stackoverflow.com/a/49861799/299756
kalyfe

สิ่งที่ฉันต้องการ ฉันใช้ Microsoft.EntityFrameworkCore 3.1.1 และไม่มีวิธีเรียกใช้แบบสอบถาม RAW และ SP ขอบคุณมากสำหรับสิ่งนี้!
jaysonragasa

6

ลองทำสิ่งนี้: (สร้างวิธีการขยาย)

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

การใช้งาน:

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

รุ่นของฉัน: (ไม่ได้อยู่ในDbSet):

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

ทดสอบใน.netCore 2.2 and 3.0.

หมายเหตุ:โซลูชันนี้มีประสิทธิภาพช้า


ลองค้นหา PropertyInfo ด้วยชื่อเพียงครั้งเดียวสำหรับระเบียนแรกเท่านั้นและสร้างอาร์เรย์ของ PropertyInfo [] ตามดัชนีคอลัมน์เพื่อใช้ในระเบียนถัดไป
Petr Voborník

@AminRostami Nice Work
sebu

2

ไม่ได้กำหนดเป้าหมายไปที่สถานการณ์ของ OP โดยตรง แต่เนื่องจากฉันมีปัญหากับเรื่องนี้ฉันจึงต้องการทิ้งอดีตเหล่านี้ วิธีการที่ทำให้ง่ายต่อการดำเนินการ SQL ดิบด้วย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();
    }
  }
}

1

ฉันใช้Dapperเพื่อข้ามข้อ จำกัด ของ Entity framework Core

IDbConnection.Query

กำลังทำงานกับแบบสอบถาม sql หรือกระบวนงานที่จัดเก็บที่มีพารามิเตอร์หลายตัว อย่างไรก็ตามมันเร็วกว่าเล็กน้อย (ดูการทดสอบเกณฑ์มาตรฐาน )

Dapper เป็นเรื่องง่ายที่จะเรียนรู้ ใช้เวลา 15 นาทีในการเขียนและรันกระบวนงานที่จัดเก็บพร้อมพารามิเตอร์ อย่างไรก็ตามคุณสามารถใช้ทั้ง EF และ Dapper ได้ ด้านล่างนี้คือตัวอย่าง:

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


        }
}

1

กรณีของฉันใช้กระบวนงานที่เก็บไว้แทน SQL ดิบ

สร้างชั้นเรียน

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

เพิ่มไว้ด้านล่างในDbContextชั้นเรียนของฉัน

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

ในการดำเนินการตามขั้นตอนที่จัดเก็บ:

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

1

จริงๆแล้วคุณสามารถสร้างที่เก็บข้อมูลทั่วไปและทำสิ่งนี้ได้

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

}

นอกจากนี้: Microsoft.EntityFrameworkCore ไม่มี FromSqlRaw ไม่จำเป็นต้องติดตั้ง Microsoft.EntityFrameworkCore.Relational เพื่อใช้วิธีนี้
Kate

0

นอกจากนี้คุณยังสามารถใช้QueryFirst เช่นเดียวกับ Dapper สิ่งนี้อยู่นอก EF โดยสิ้นเชิง ซึ่งแตกต่างจาก Dapper (หรือ EF) คุณไม่จำเป็นต้องดูแลรักษา POCO คุณแก้ไข sql SQL ของคุณในสภาพแวดล้อมจริงและได้รับการตรวจสอบซ้ำอย่างต่อเนื่องเมื่อเทียบกับ DB คำเตือน: ฉันเป็นผู้เขียน QueryFirst



0

โซลูชันนี้ให้ความสำคัญกับโซลูชันจาก @pius ฉันต้องการเพิ่มตัวเลือกเพื่อสนับสนุนพารามิเตอร์การสืบค้นเพื่อช่วยลดการแทรก SQL และฉันยังต้องการทำให้เป็นส่วนขยายจาก DbContext DatabaseFacade สำหรับ Entity Framework Core เพื่อให้รวมเข้าด้วยกันได้มากขึ้น

ขั้นแรกให้สร้างคลาสใหม่โดยใช้ส่วนขยาย:

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

}

โปรดทราบในด้านบนว่า "T" คือประเภทของผลตอบแทนและ "P" คือประเภทของพารามิเตอร์การสืบค้นของคุณซึ่งจะแตกต่างกันไปตามว่าคุณกำลังใช้ MySql, Sql เป็นต้น

ต่อไปเราจะแสดงตัวอย่าง ฉันใช้ความสามารถ MySql EF Core ดังนั้นเราจะดูว่าเราสามารถใช้ส่วนขยายทั่วไปด้านบนกับการใช้งาน 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;
       }
    }
}

คำค้นหาจะแสดงแถวเช่น
"Ford", "Explorer", "Ford Explorer"
"Tesla", "Model X", "Tesla Model X"

ชื่อที่แสดงไม่ได้กำหนดเป็นคอลัมน์ฐานข้อมูลดังนั้นจะไม่เป็นส่วนหนึ่งของโมเดล EF Car โดยค่าเริ่มต้น ฉันชอบแนวทางนี้เป็นหนึ่งในวิธีแก้ปัญหาที่เป็นไปได้มากมาย คำตอบอื่น ๆ ในหน้านี้อ้างอิงวิธีอื่นในการแก้ไขปัญหานี้กับมัณฑนากร [NotMapped] ซึ่งขึ้นอยู่กับกรณีการใช้งานของคุณอาจเป็นแนวทางที่เหมาะสมกว่า

สังเกตว่าโค้ดในตัวอย่างนี้มีรายละเอียดมากกว่าที่จำเป็น แต่ฉันคิดว่ามันทำให้ตัวอย่างชัดเจนขึ้น


-7

ด้วย Entity Framework 6 คุณสามารถดำเนินการบางอย่างดังต่อไปนี้

สร้าง Modal Class เป็น

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

ดำเนินการคำสั่ง Raw DQL SQl ดังต่อไปนี้:

var userList = datacontext.Database.SqlQuery<User>(@"SELECT u.Id ,fname , lname ,username FROM dbo.Users").ToList<User>();
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.