DbSet없는 원시 SQL 쿼리-Entity Framework Core


107

Entity Framework Core를 제거 dbData.Database.SqlQuery<SomeModel>하면 테이블 데이터와 순위를 반환하는 전체 텍스트 검색 쿼리에 대한 원시 SQL 쿼리를 빌드하는 솔루션을 찾을 수 없습니다.

Entity Framework Core에서 원시 SQL 쿼리를 작성하는 유일한 방법은 쿼리에서 dbData.Product.FromSql("SQL SCRIPT");반환하는 순위를 매핑하는 DbSet이 없기 때문에 유용하지 않습니다.

어떤 아이디어 ???


15
SqlQuery <T>를 크게 놓치고 특정 사용 사례에 대한 간단한 DTO가 필요한 경우 사용자 지정 클래스를 내 DbContext에 매핑 할 필요가 없습니다. : 나는 그들이이 기능을 다시 좋아하면 누구나 투표를 할 수있는 EF 코어에이 기능을 다시 추가 요청에 사용자의 음성을 만들었습니다 data.uservoice.com/forums/...
매트 샌더스

1
github.com/aspnet/EntityFramework/issues/1862 에 따르면 현재 EF 코어 1.2 및 / 또는 1.1.0-preview1
Dan Field

2
@Devon이 방금 말한 내용을 바탕으로 Microsoft.EntityFrameworkCore.SqlServer의 확장 메서드임을 확인하는 데 너무 오래 걸렸습니다. 이러한 확장 메서드를 가져 오기 전에 프로젝트에 추가해야합니다.
다니엘

3
한숨, 이것은 일종의 건축 우주 비행사 결정처럼 보인다 : "사람들은 이것을 원할 필요가 없다". 이 경우에만 Dapper를 설치해야한다고 생각합니다. 성가신.
Dirk Boer

1
@MattSanders-당신은 uservoice 링크가 그동안 죽은 것 같습니다. 어디로 갔는지 아십니까?
Dirk Boer

답변:


127

EF Core 2.1 또는 EF Core 3 이상 버전을 사용하는지 여부에 따라 다릅니다 .

EF Core 2.1을 사용하는 경우

2018 년 5 월 7 일 이후로 제공되는 EF Core 2.1 릴리스 후보 1을 사용하는 경우 쿼리 유형 인 제안 된 새 기능을 활용할 수 있습니다.

쿼리 유형 이란 무엇입니까 ?

엔터티 형식 외에도 EF Core 모델에는 엔터티 형식에 매핑되지 않은 데이터에 대해 데이터베이스 쿼리를 수행하는 데 사용할 수있는 쿼리 형식이 포함될 수 있습니다.

쿼리 유형은 언제 사용합니까?

임시 FromSql () 쿼리의 반환 유형 역할을합니다.

데이터베이스보기에 매핑.

기본 키가 정의되지 않은 테이블에 매핑.

모델에 정의 된 쿼리에 매핑.

따라서 더 이상 질문에 대한 답변으로 제안 된 모든 해킹이나 해결 방법을 수행 할 필요가 없습니다. 다음 단계를 따르십시오.

먼저 형식의 새 속성 정의 하여 SQL 쿼리의 열 값을 수행하는 클래스의 유형입니다. 그래서 당신은 이것을 가질 것입니다 :DbQuery<T>TDbContext

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

또한주의 DdContext의이 부분 클래스 당신이 가장 적합한대로 '원시 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();

18
이 답변은 EF Core 2.1 이상을 사용할 때 최상의 솔루션이어야합니다. 👍
Will Huang

2
@CodeNotFound 결과가 필요하지 않거나 기본 유형 (예 :)이면 어떻게 bit됩니까?
Shimmy Weitzhandler

5
CodeFirst를 사용하면 이러한 모든 속성이있는 테이블이 자동으로 생성 [NotMapped]되어 SomeModels클래스에 추가 하는 것이 작동하지 않습니다. 내가 놓친 것이 있습니까?
Jean-Paul

7
EF Core 3.0 은 키가없는 엔터티 형식 과 함께 사용 DbQuery하기 위해 더 이상 사용되지 않습니다 . DbSet
NetMage

3
참고로 EF 코어 3.0의 일부 버그로 인해 코드 우선 마이그레이션은 HasNoKey ()로 표시된 엔터티에서도 테이블을 만들려고 시도합니다. 따라서 .ToView (null)도 추가해야합니다. 예 : modelBuilder.Entity<MyData>().HasNoKey().ToView(null);@ Jean-Paul이 방법으로 문제가 해결 된 것 같습니다
stann1

36

다른 답변을 바탕으로 예제 사용을 포함하여 작업을 수행하는이 도우미를 작성했습니다.

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

빌트인 지원이 추가되는 즉시 제거 할 계획입니다. EF Core 팀의 Arthur Vickers 의 성명 에 따르면 이는 2.0 이후의 최우선 순위입니다. 여기에서 문제를 추적하고 있습니다 .


좋은 답변입니다.
sebu

31

EF Core에서는 더 이상 "무료"원시 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)수 없을까요?
David Harlow

이 세트에 대해 방출 된 Linq는 메모리에서 해결되어야합니다. 다른 WHERE sql 절을 내 보내야하는 경우 매개 변수로 포함하거나 다른 스크립트를 작성해야합니다.
E-Bat

내 DbSet에는 "FromSql"메소드가 없습니다. 내가 놓친 확장 프로그램입니까?
birwin

1
@birwin, 네임 스페이스를 가져와야합니다. Microsoft.EntityFrameworkCore
E-Bat

20

EF Core에서 원시 SQL을 실행할 수 있습니다.이 클래스를 프로젝트에 추가합니다. 이렇게하면 POCO 및 DBSet을 정의하지 않고도 원시 SQL을 실행하고 원시 결과를 얻을 수 있습니다. 원본 예는 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]);
    }
}

18

지금은 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
         }
      }
  }

Sql 주입을 피하기 위해 SqlParameter를 시도하십시오.

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

FromSql은 전체 쿼리에서 작동하지 않습니다. 예를 들어 WHERE 절을 포함하려는 경우 무시됩니다.

일부 링크 :

Entity Framework Core를 사용하여 원시 SQL 쿼리 실행

원시 SQL 쿼리


7

Core 2.1에서는 다음과 같이 할 수 있습니다.

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

그런 다음 다음과 같이 SQL 프로 시저를 정의하십시오.

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

이렇게하면 순위 모델이 DB에 생성되지 않습니다.

이제 컨트롤러 / 액션에서 다음을 호출 할 수 있습니다.

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

이렇게하면 원시 SQL 프로 시저를 호출 할 수 있습니다.


FromSqlPARAMS은 단순히 만들지 않고 전달 될 수있다 SqlParameter: 객체 FromSql($"STORED_PROCEDURE {value1}, {value2}")또는 FromSql("STORED_PROCEDURE {0}, {1}", value1, value2)(그들은 탈출됩니다).
Majid

7

이것을 사용할 수 있습니다 ( 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);
        }
    }
}

그리고 사용법 :

    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

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

5

이것을 시도하십시오 : (확장 방법 만들기)

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

Entity Framework Core의 제약 조건을 우회하기 위해 Dapper 를 사용했습니다 .

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


        }
}

0

QueryFirst를 사용할 수도 있습니다 . Dapper와 마찬가지로 이것은 완전히 EF 외부에 있습니다. Dapper (또는 EF)와 달리 POCO를 유지할 필요가없고 실제 환경에서 SQL SQL을 편집하고 DB에 대해 지속적으로 유효성을 다시 확인합니다. 면책 조항 : 저는 QueryFirst의 저자입니다.


0

내 경우는 원시 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();


0

이 솔루션은 @pius의 솔루션에 크게 의존합니다. SQL 주입을 완화하는 데 도움이되는 쿼리 매개 변수를 지원하는 옵션을 추가하고 싶었고 Entity Framework Core 용 DbContext DatabaseFacade에서 확장하여 좀 더 통합되도록 만들고 싶었습니다.

먼저 확장을 사용하여 새 클래스를 만듭니다.

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] 데코레이터로이 문제를 해결하는 다른 방법을 참조하며, 사용 사례에 따라 더 적절한 접근 방식이 될 수 있습니다.

이 예제의 코드는 필요한 것보다 분명히 더 장황하지만 예제가 더 명확 해 졌다고 생각했습니다.


-6

Entity Framework 6을 사용하면 아래와 같이 실행할 수 있습니다.

모달 클래스 만들기

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.