Truy vấn SQL thô không có DbSet - Entity Framework Core


107

Với việc loại bỏ Entity Framework Core, dbData.Database.SqlQuery<SomeModel>tôi không thể tìm thấy giải pháp để xây dựng một Truy vấn SQL thô cho truy vấn tìm kiếm toàn văn của tôi, truy vấn này sẽ trả về dữ liệu bảng và cả xếp hạng.

Phương pháp duy nhất tôi đã thấy để xây dựng một truy vấn SQL thô trong Entity Framework Core là thông qua phương pháp dbData.Product.FromSql("SQL SCRIPT");này không hữu ích vì tôi không có DbSet nào sẽ ánh xạ thứ hạng mà tôi trả về trong truy vấn.

Bất kỳ ý tưởng ???


15
Tôi sẽ rất nhớ SqlQuery <T> và không muốn phải ánh xạ các lớp tùy chỉnh vào DbContext của mình khi tôi thực sự chỉ cần một DTO đơn giản cho một trường hợp sử dụng cụ thể. Tôi đã tạo một giọng nói của người dùng để yêu cầu thêm lại tính năng này vào EF Core mà bất kỳ ai cũng có thể bỏ phiếu nếu họ muốn tính năng này trở lại: data.uservoice.com/forums/…
Matt Sanders

1
Theo github.com/aspnet/EntityFramework/issues/1862 , điều này hiện được nhắm mục tiêu cho EF core 1.2 và / hoặc 1.1.0-preview1
Dan Field

2
Dựa trên những gì @Devon vừa nói, tôi đã mất quá nhiều thời gian để tìm ra chúng là các phương thức mở rộng trong Microsoft.EntityFrameworkCore.SqlServer. Bạn sẽ cần thêm nó vào dự án của mình trước khi nhận được các phương thức mở rộng này.
Daniel

3
Thở dài, điều này có vẻ giống như một loại quyết định của Phi hành gia Kiến trúc: "mọi người không cần phải muốn điều này". Tôi đoán tôi phải cài đặt Dapper chỉ cho trường hợp này. Làm phiền.
Dirk Boer

1
@MattSanders - liên kết hóa đơn của bạn dường như đã chết trong thời gian chờ đợi. Bạn có biết nó đã đi đâu không?
Dirk Boer

Câu trả lời:


125

Tùy thuộc vào việc bạn đang sử dụng EF Core 2.1 hay EF Core 3 và các phiên bản cao hơn .

Nếu bạn đang sử dụng EF Core 2.1

Nếu bạn đang sử dụng EF Core 2.1 Release Candidate 1 có sẵn kể từ ngày 7 tháng 5 năm 2018, bạn có thể tận dụng tính năng mới được đề xuất là Loại truy vấn.

Loại truy vấn là gì?

Ngoài các loại thực thể, một mô hình EF Core có thể chứa các loại truy vấn, có thể được sử dụng để thực hiện các truy vấn cơ sở dữ liệu đối với dữ liệu không được ánh xạ tới các loại thực thể.

Khi nào sử dụng loại truy vấn?

Phục vụ như kiểu trả về cho các truy vấn FromSql () đặc biệt.

Ánh xạ tới các dạng xem cơ sở dữ liệu.

Ánh xạ tới các bảng không có khóa chính được xác định.

Ánh xạ tới các truy vấn được xác định trong mô hình.

Vì vậy, bạn không còn cần phải thực hiện tất cả các thủ thuật hoặc cách giải quyết được đề xuất như câu trả lời cho câu hỏi của mình. Chỉ cần làm theo các bước sau:

Trước tiên, bạn đã xác định một thuộc tính mới của kiểu DbQuery<T>, trong đó Tlà kiểu của lớp sẽ mang các giá trị cột của truy vấn SQL của bạn. Vì vậy, DbContextbạn sẽ có cái này:

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

Thứ hai, sử dụng FromSqlphương pháp như bạn làm với DbSet<T>:

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

Cũng lưu ý rằng DdContexts là các lớp một phần , vì vậy bạn có thể tạo một hoặc nhiều tệp riêng biệt để sắp xếp các định nghĩa 'DbQuery SQL thô' của bạn sao cho phù hợp nhất với bạn.


Nếu bạn đang sử dụng EF Core 3.0 và các phiên bản cao hơn

Loại truy vấn hiện được gọi là loại thực thể không khóa . Như đã nói ở trên, các kiểu truy vấn đã được giới thiệu trong EF Core 2.1. Nếu bạn đang sử dụng phiên bản EF Core 3.0 hoặc cao hơn, bây giờ bạn nên điều chỉnh bằng cách sử dụng các loại tntity không khóa vì các loại truy vấn hiện được đánh dấu là lỗi thời.

Tính năng này đã được thêm vào EF Core 2.1 dưới tên của các loại truy vấn. Trong EF Core 3.0, khái niệm được đổi tên thành các loại thực thể không khóa. Chú thích dữ liệu [Keyless] đã có trong EFCore 5.0.

Chúng tôi vẫn có các tình huống tương tự như đối với các loại truy vấn về thời điểm sử dụng loại thực thể không khóa.

Vì vậy, để sử dụng nó, trước tiên bạn cần đánh dấu lớp của mình SomeModelbằng [Keyless]chú thích dữ liệu hoặc thông qua cấu hình thông thạo với .HasNoKey()cuộc gọi phương thức như dưới đây:

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

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

Sau cấu hình đó, bạn có thể sử dụng một trong các phương pháp được giải thích ở đây để thực thi truy vấn SQL của mình. Ví dụ, bạn có thể sử dụng cái này:

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

18
Câu trả lời này sẽ là giải pháp tốt nhất khi sử dụng EF Core 2.1 trở lên. 👍
Will Huang

2
@CodeNotFound Điều gì sẽ xảy ra nếu tôi không cần kết quả hoặc nếu đó là kiểu nguyên thủy (ví dụ bit)?
Shimmy Weitzhandler

5
Sử dụng CodeFirst, điều này tự động tạo một bảng với tất cả các thuộc tính đó, việc thêm [NotMapped]vào SomeModelslớp không hoạt động đối với tôi. Tôi có bỏ lỡ điều gì không?
Jean-Paul

7
EF Core 3.0 không dùng nữa DbQueryđể chỉ sử dụng DbSetvới các loại thực thể không khóa .
NetMage

3
Chỉ là FYI, do một số lỗi trong EF core 3.0, lần di chuyển đầu tiên bằng mã sẽ vẫn cố gắng tạo bảng ngay cả trên các thực thể được đánh dấu bằng HasNoKey (). Vì vậy, bạn cũng phải thêm .ToView (null). Ví dụ: modelBuilder.Entity<MyData>().HasNoKey().ToView(null);@ Jean-Paul Tôi nghĩ điều này giải quyết được vấn đề của bạn
stann1

36

Dựa trên các câu trả lời khác mà tôi đã viết trình trợ giúp này hoàn thành nhiệm vụ, bao gồm cả cách sử dụng ví dụ:

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

Sử dụng:

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

Tôi dự định loại bỏ nó ngay khi hỗ trợ tích hợp được thêm vào. Theo một tuyên bố của Arthur Vickers từ nhóm EF Core, ưu tiên cao cho hậu 2.0. Vấn đề đang được theo dõi ở đây .


câu trả lời hay, thích nó.
sebu

31

Trong EF Core, bạn không thể thực thi sql thô "miễn phí" nữa. Bạn được yêu cầu xác định một lớp POCO và một DbSetcho lớp đó. Trong trường hợp của bạn, bạn sẽ cần xác định Xếp hạng :

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

Vì nó chắc chắn sẽ chỉ đọc nên sẽ hữu ích khi bao gồm .AsNoTracking()cuộc gọi.

EDIT - Thay đổi đột phá trong EF Core 3.0:

DbQuery () hiện đã lỗi thời, thay vào đó DbSet () nên được sử dụng (một lần nữa). Nếu bạn có một thực thể không có khóa, tức là nó không yêu cầu khóa chính, bạn có thể sử dụng phương thức HasNoKey () :

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

Thông tin thêm có thể được tìm thấy tại đây


3
Vì vậy, tôi đoán tôi cũng sẽ phải mở rộng DbContextđể bao gồm một thuộc tính mới DbSet<Rank> Rank { get; set; }. Điều này sẽ có ý nghĩa gì bây giờ liên quan đến linq? Tức là bây giờ chúng ta sẽ không thể sử dụng một câu lệnh như thế nào DBContext.Rank.Where(i => i.key == 1)và câu lệnh này sẽ không được triển khai trong SQL và do đó không thành công?
David Harlow

Linq phát ra chống lại tập hợp này phải được giải quyết trong bộ nhớ. Nếu bạn cần phát ra mệnh đề WHERE sql khác nhau, bạn phải bao gồm chúng dưới dạng tham số hoặc xây dựng một tập lệnh khác.
E-Bat

DbSet của tôi không có phương thức "FromSql". Đây có phải là tiện ích mở rộng tôi đang thiếu không?
birwin

1
@birwin, bạn cần nhập không gian tên Microsoft.EntityFrameworkCore
E-Bat

20

Bạn có thể thực thi sql thô trong EF Core - Thêm lớp này vào dự án của bạn. Điều này sẽ cho phép bạn thực thi SQL thô và nhận được kết quả thô mà không cần phải xác định POCO và DBSet. Xem https://github.com/aspnet/EntityFramework/issues/1862#issuecomment-220787464 để biết ví dụ ban đầu.

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

Đây là một ví dụ về cách sử dụng nó:

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

18

Hiện tại, cho đến khi có thứ gì đó mới từ EFCore, tôi sẽ sử dụng một lệnh và ánh xạ nó theo cách thủ công

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

Cố gắng SqlParameter để tránh Sql Injection.

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

FromSql không hoạt động với truy vấn đầy đủ. Ví dụ nếu bạn muốn bao gồm mệnh đề WHERE thì nó sẽ bị bỏ qua.

Một số liên kết:

Thực thi truy vấn SQL thô bằng Entity Framework Core

Truy vấn SQL thô


7

Trong Core 2.1, bạn có thể làm như sau:

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

và sau đó xác định cho bạn Thủ tục SQL, như:

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

Bằng cách này, mô hình Xếp hạng sẽ không được tạo trong DB của bạn.

Bây giờ trong bộ điều khiển / hành động của bạn, bạn có thể gọi:

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

Bằng cách này, bạn có thể gọi Thủ tục SQL thô.


Các tham số FromSqlcó thể được truyền đơn giản mà không cần tạo SqlParameterđối tượng: FromSql($"STORED_PROCEDURE {value1}, {value2}")hoặc FromSql("STORED_PROCEDURE {0}, {1}", value1, value2)(chúng sẽ được thoát).
Majid

7

Bạn có thể sử dụng này (từ 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);
        }
    }
}

Và cách sử dụng:

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

6

Thêm gói Nuget - Microsoft.EntityFrameworkCore.Relational

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

Điều này sẽ trả về số hàng dưới dạng int

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


3
Xin lưu ý rằng điều này sẽ chỉ trả về số hàng bị ảnh hưởng bởi lệnh: stackoverflow.com/a/49861799/299756
kalyfe

Chính xác những gì tôi cần. Tôi đang sử dụng Microsoft.EntityFrameworkCore 3.1.1 và không có cách nào để thực thi truy vấn RAW và SP. Cảm ơn bạn rất nhiều cho việc này!
jaysonragasa

5

hãy thử điều này: (tạo phương pháp mở rộng)

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

Sử dụng:

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

mô hình của tôi: (không trong DbSet):

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

đã thử nghiệm .netCore 2.2 and 3.0.

Lưu ý: giải pháp này có hiệu suất chậm


Hãy thử tìm kiếm PropertyInfo theo tên chỉ một lần cho bản ghi đầu tiên và xây dựng mảng PropertyInfo [] theo chỉ mục cột để sử dụng cho các bản ghi tiếp theo.
Petr Voborník

@AminRostami Làm việc tốt đẹp
sebu

2

Không nhắm trực tiếp vào kịch bản của OP, nhưng vì tôi đã phải vật lộn với điều này, tôi muốn bỏ những người yêu cũ này. các phương thức giúp thực thi SQL thô dễ dàng hơn với 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

Tôi đã sử dụng Dapper để bỏ qua ràng buộc này của Core framework Entity.

IDbConnection.Query

đang làm việc với truy vấn sql hoặc thủ tục được lưu trữ với nhiều tham số. Nhân tiện, nó nhanh hơn một chút (xem các bài kiểm tra điểm chuẩn )

Dapper rất dễ học. Mất 15 phút để viết và chạy thủ tục được lưu trữ với các tham số. Dù sao thì bạn cũng có thể sử dụng cả EF và Dapper. Dưới đây là một ví dụ:

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


        }
}

0

Bạn cũng có thể sử dụng QueryFirst . Giống như Dapper, điều này hoàn toàn nằm ngoài EF. Không giống như Dapper (hoặc EF), bạn không cần duy trì POCO, bạn chỉnh sửa SQL sql của mình trong môi trường thực và nó liên tục được xác nhận lại dựa trên DB. Tuyên bố từ chối trách nhiệm: Tôi là tác giả của QueryFirst.


0

Trường hợp của tôi đã sử dụng thủ tục được lưu trữ thay vì SQL thô

Đã tạo một lớp học

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

Đã thêm bên dưới vào DbContextlớp học của tôi

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

Để thực hiện thủ tục đã lưu trữ:

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


0

Giải pháp này chủ yếu dựa vào giải pháp từ @pius. Tôi muốn thêm tùy chọn hỗ trợ các tham số truy vấn để giúp giảm thiểu việc chèn SQL và tôi cũng muốn đặt nó thành một tiện ích mở rộng của DbContext DatabaseFacade cho Entity Framework Core để làm cho nó tích hợp hơn một chút.

Đầu tiên, hãy tạo một lớp mới với phần mở rộng:

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

}

Lưu ý ở trên rằng "T" là loại cho kết quả trả về và "P" là loại tham số truy vấn của bạn sẽ thay đổi tùy theo nếu bạn đang sử dụng MySql, Sql, v.v.

Tiếp theo chúng tôi sẽ đưa ra một ví dụ. Tôi đang sử dụng khả năng MySql EF Core, vì vậy chúng tôi sẽ xem cách chúng tôi có thể sử dụng tiện ích mở rộng chung ở trên với triển khai MySql cụ thể hơn này:

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

Truy vấn sẽ trả về các hàng như:
"Ford", "Explorer", "Ford Explorer"
"Tesla", "Model X", "Tesla Model X"

Tiêu đề hiển thị không được xác định là một cột cơ sở dữ liệu, vì vậy nó sẽ không phải là một phần của mô hình Xe hơi EF theo mặc định. Tôi thích cách tiếp cận này như một trong nhiều giải pháp khả thi. Các câu trả lời khác trên trang này đề cập đến các cách khác để giải quyết vấn đề này với trình trang trí [NotMapped], tùy thuộc vào trường hợp sử dụng của bạn có thể là cách tiếp cận phù hợp hơn.

Lưu ý rằng mã trong ví dụ này rõ ràng là dài dòng hơn mức cần thiết, nhưng tôi nghĩ rằng nó làm cho ví dụ rõ ràng hơn.


-6

Với Entity Framework 6, bạn có thể thực thi một cái gì đó như dưới đây

Tạo lớp phương thức dưới dạng

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

Thực hiện lệnh Raw DQL SQl như sau:

var userList = datacontext.Database.SqlQuery<User>(@"SELECT u.Id ,fname , lname ,username FROM dbo.Users").ToList<User>();
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.