При удалении Entity Framework Core dbData.Database.SqlQuery Я не могу найти решение для создания необработанного SQL-запроса для моего полнотекстового поискового запроса, который вернет данные таблиц, а также звание.

Единственный метод, который я видел для создания необработанного SQL-запроса в Entity Framework Core, - это dbData.Product.FromSql ("SQL SCRIPT");, который бесполезен, поскольку у меня нет DbSet который отобразит ранг, который я возвращаю в запросе.

Любые идеи ???

David Harlow

Ответов: 20

Ответы (20)

Это зависит от того, используете ли вы EF Core 2.1 или EF Core 3 и более поздние версии.

Если вы используете EF Core 2.1

Если вы используете EF Core 2.1 Release Candidate 1, доступный с 7 мая 2018 г., вы можете воспользоваться преимуществами предлагаемой новой функции, которая представляет собой тип запроса.

Что такое тип запроса?

Помимо типов сущностей, модель EF Core может содержать типы запросов, который можно использовать для выполнения запросов к базе данных с данными, которые не сопоставляется с типами сущностей.

Когда использовать тип запроса?

Служит типом возвращаемого значения для специальных запросов FromSql ().

Сопоставление с представлениями базы данных.

Сопоставление с таблицами, для которых не определен первичный ключ.

Сопоставление с запросами, определенными в модели.

Таким образом, вам больше не нужно использовать все хитрости или обходные пути, предложенные в качестве ответов на ваш вопрос. Просто выполните следующие действия:

Сначала вы определили новое свойство типа DbQuery , где T - это тип класса, который будет нести значения столбцов вашего SQL-запроса. Итак, в вашем DbContext у вас будет это:

public DbQuery SomeModels { get; set; }

Во-вторых, используйте метод FromSql, как вы делаете с DbSet :

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

Также обратите внимание, что DdContexts - это частичные классы, поэтому вы можете создать один или несколько отдельных файлов для организации ваших определений «сырого SQL DbQuery» в соответствии с вашими предпочтениями.


Если вы используете EF Core 3.0 и более поздние версии

Тип запроса теперь известен как Тип объекта Keyless. Как было сказано выше, типы запросов были введены в EF Core 2.1. Если вы используете EF Core 3.0 или более позднюю версию, вам следует подумать об использовании типов сущностей без ключа, поскольку типы запросов теперь помечены как устаревшие.

Эта функция была добавлена ​​в EF Core 2.1 под названием типов запросов. В EF Core 3.0 концепция была переименована в типы сущностей без ключа. В [Keyless] Аннотация к данным стала доступна в EFCore 5.0.

У нас все еще есть те же сценарии, что и для типов запросов, когда следует использовать тип сущности без ключа.

Итак, чтобы использовать его, вам нужно сначала пометить свой класс SomeModel с помощью [Keyless] аннотации данных или путем быстрой настройки с помощью .HasNoKey () вызов метода как показано ниже:

public DbSet SomeModels { get; set; }

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

После этой конфигурации вы можете использовать один из описанных здесь методов здесь для выполнения вашего SQL-запроса. Например, вы можете использовать это:

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

В EF Core вы больше не можете выполнять «бесплатный» необработанный sql. Вам необходимо определить класс POCO и DbSet для этого класса. В вашем случае вам нужно будет определить Rank:

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

As it will be surely readonly it will be useful to include the .AsNoTracking() call.

EDIT - Критическое изменение в EF Core 3.0:

DbQuery () теперь устарело, вместо него следует использовать DbSet () (снова). Если у вас есть сущность без ключа, т.е. для нее не требуется первичный ключ, вы можете использовать метод HasNoKey ():

ModelBuilder.Entity().HasNoKey()

Более подробную информацию можно найти здесь

Добавить пакет 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

Вы можете использовать это (от https://github.com/aspnet/EntityFrameworkCore/issues/1862#issuecomment-451671168):

public static class SqlQueryExtensions
{
    public static IList SqlQuery(this DbContext db, string sql, params object[] parameters) where T : class
    {
        using (var db2 = new ContextForQueryType(db.Database.GetDbConnection()))
        {
            // share the current database transaction, if one exists
            var transaction = db.CurrentTransaction;
            if (transaction != null)
                db2.Database.UseTransaction(transaction.GetDbTransaction());
            return db2.Query().FromSql(sql, parameters).ToList();
        }
    }

    public static IList SqlQuery(this DbContext db, Func anonType, string sql, params object[] parameters) where T : class
        => SqlQuery(db, sql, parameters);

    private class ContextForQueryType : 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().HasNoKey();
            base.OnModelCreating(modelBuilder);
        }
    }
}

И использование:

    using (var db = new Db())
    {
        var results = db.SqlQuery("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");
    }

Я знаю, что это старый вопрос, но, возможно, он поможет кому-то вызывать хранимые процедуры, не добавляя DTO как DbSets.

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

попробуйте следующее: (создать метод расширения)

public static List ExecuteQuery(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();
                    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(query);

моя модель: (нет в DbSet):

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

протестировано в .netCore 2.2 и 3.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
    {
        /// 
        /// Execute raw SQL query with query parameters
        /// 
        /// the return type
        /// the database context database, usually _context.Database
        /// the query string
        /// the map to map the result to the object of type T
        /// the collection of query parameters, if any
        /// 
        public static List ExecuteSqlRawExt(this DatabaseFacade db, string query, Func map, IEnumerable

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(); 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 _logger;
        //this would be your Entity Framework Core context
        private readonly CarContext _context;

        public CarController(ILogger logger, CarContext context)
        {
            _logger = logger;
            _context = context;
        }

        //... more stuff here ...

       /// 
       /// Get car example
       /// 
       [HttpGet]
       public IEnumerable 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 queryParameters = new List() { p1, p2, p3 };

           //note the extension is now easily accessed off the _context.Database object
           //also note for ExecuteSqlRawExt
           //Car is my return type "T"
           //MySqlParameter is the specific DbParameter type MySqlParameter type "P"
           List result = _context.Database.ExecuteSqlRawExt(
        "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;
       }
    }
}

Запрос вернет такие строки, как:
«Форд», «Эксплорер», «Форд Эксплорер»
«Тесла», «Модель X», «Тесла Модель X»

Отображаемый заголовок не определяется как столбец базы данных, поэтому по умолчанию он не будет частью модели EF Car. Мне этот подход нравится как одно из многих возможных решений. Другие ответы на этой странице ссылаются на другие способы решения этой проблемы с помощью декоратора [NotMapped], который в зависимости от вашего варианта использования может быть более подходящим подходом.

Обратите внимание, что код в этом примере, очевидно, более подробен, чем должен быть, но я думал, что он сделал пример более понятным.

Я пришел к этому вопросу, потому что у нас есть более 100 экземпляров без сущностного использования SqlQuery в Entity Framework 6, и поэтому использование предлагаемых Microsoft способов просто не может легко работать в нашем чехол.

Вдобавок нам пришлось поддерживать единую кодовую базу EF (Entity Framework 6) / EFC (Entity Framework Core 5) в течение нескольких месяцев при переходе с EF до EFC. База кода довольно большая и мигрировать «за ночь» было просто невозможно.

Ответ ниже основан на отличных ответах выше, и это всего лишь небольшое расширение, чтобы заставить их работать еще для нескольких крайних случаев.

Во-первых, для каждого проекта на основе EF мы создали проект на основе EFC (например, MyProject.csproj ==> MyProject_EFC.csproj) и внутри всех таких проектов EFC мы определяли константу EFCORE. Если вы выполняете быструю одноразовую миграцию с EF на EFC, то вам это не нужно, и вы можете просто оставить то, что находится внутри #, если EFCORE ... # else и удалите то, что внутри # else ... # endif ниже.

Вот основной класс расширения взаимодействия.

using System;
using System.Collections.Generic;
using System.Threading;

#if EFCORE
using System.ComponentModel.DataAnnotations.Schema;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Storage;
using Database = Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade;
using MoreLinq.Extensions;
#else
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
#endif

namespace YourNameSpace.EntityFrameworkCore
{
    /// 
    /// Collection of extension methods to simplify migration from EF to EFC.
    /// 
    public static class EntityFrameworkCoreInterop
    {
        /// 
        /// https://stackoverflow.com/questions/6637679/reflection-get-attribute-name-and-value-on-property
        /// 
        public static TAttribute? TryGetAttribute(this PropertyInfo prop) where TAttribute : Attribute =>
            prop.GetCustomAttributes(true).TryGetAttribute();

        public static TAttribute? TryGetAttribute(this Type t) where TAttribute : Attribute =>
            t.GetCustomAttributes(true).TryGetAttribute();

        public static TAttribute? TryGetAttribute(this IEnumerable attrs) where TAttribute : Attribute
        {
            foreach (object attr in attrs)
            {
                switch (attr)
                {
                    case TAttribute t:
                    {
                        return t;
                    }
                }
            }

            return null;
        }

        /// 
        /// Returns true if the source string matches *any* of the passed-in strings (case insensitive)
        /// 
        public static bool EqualsNoCase(this string? s, params string?[]? targets)
        {
            if (s == null && (targets == null || targets.Length == 0))
            {
                return true;
            }

            if (targets == null)
            {
                return false;
            }

            return targets.Any(t => string.Equals(s, t, StringComparison.OrdinalIgnoreCase));
        }

#if EFCORE
        public class EntityException : Exception
        {
            public EntityException(string message) : base(message)
            {
            }
        }

        public static TEntity GetEntity(this EntityEntry entityEntry)
            where TEntity : class => entityEntry.Entity;

        #region SqlQuery Interop

        /// 
        /// kk:20210727 - This is a little bit ugly but given that this interop method is used just once,
        /// it is not worth spending more time on it.
        /// 
        public static List ToList(this IOrderedAsyncEnumerable e) =>
            Task.Run(() => e.ToListAsync().AsTask()).GetAwaiter().GetResult();

        private static string GetColumnName(this MemberInfo info) =>
            info.GetCustomAttributes().TryGetAttribute()?.Name ?? info.Name;

        /// 
        /// See: https://stackoverflow.com/questions/35631903/raw-sql-query-without-dbset-entity-framework-core
        /// Executes raw query with parameters and maps returned values to column property names of Model provided.
        /// Not all properties are required to be present in the model. If not present then they will be set to nulls.
        /// 
        private static async IAsyncEnumerable ExecuteQuery(this Database database, string query, params object[] parameters)
        {
            await using DbCommand command = database.GetDbConnection().CreateCommand();
            command.CommandText = query;
            command.CommandType = CommandType.Text;

            if (database.CurrentTransaction != null)
            {
                command.Transaction = database.CurrentTransaction.GetDbTransaction();
            }

            foreach (var parameter in parameters)
            {
                // They are supposed to be of SqlParameter type but are passed as objects.
                command.Parameters.Add(parameter);
            }

            await database.OpenConnectionAsync();
            await using DbDataReader reader = await command.ExecuteReaderAsync();
            var t = typeof(T);

            // TODO kk:20210825 - I do know that the code below works as we use it in some other place where it does work.
            // However, I am not 100% sure that R# proposed version does. Check and refactor when time permits.
            //
            // ReSharper disable once CheckForReferenceEqualityInstead.1
            if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
            {
                t = Nullable.GetUnderlyingType(t)!;
            }

            var lstColumns = t
                .GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
                .ToList();

            while (await reader.ReadAsync())
            {
                if (t.IsPrimitive || t == typeof(string) || t == typeof(DateTime) || t == typeof(Guid) || t == typeof(decimal))
                {
                    var val = await reader.IsDBNullAsync(0) ? null : reader[0];
                    yield return (T) val!;
                }
                else
                {
                    var newObject = Activator.CreateInstance();

                    for (var i = 0; i < reader.FieldCount; i++)
                    {
                        var name = reader.GetName(i);
                        var val = await reader.IsDBNullAsync(i) ? null : reader[i];
                        var prop = lstColumns.FirstOrDefault(a => a.GetColumnName().EqualsNoCase(name));

                        if (prop == null)
                        {
                            continue;
                        }

                        prop.SetValue(newObject, val, null);
                    }

                    yield return newObject;
                }
            }
        }

        #endregion

        public static DbRawSqlQuery SqlQuery(this Database database, string sql, params object[] parameters) =>
            new(database, sql, parameters);

        public class DbRawSqlQuery : IAsyncEnumerable
        {
            private readonly IAsyncEnumerable _elements;

            internal DbRawSqlQuery(Database database, string sql, params object[] parameters) =>
                _elements = ExecuteQuery(database, sql, parameters);

            public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = new ()) =>
                _elements.GetAsyncEnumerator(cancellationToken);

            public async Task SingleAsync() => await _elements.SingleAsync();
            public TElement Single() => Task.Run(SingleAsync).GetAwaiter().GetResult();
            public async Task FirstAsync() => await _elements.FirstAsync();
            public TElement First() => Task.Run(FirstAsync).GetAwaiter().GetResult();
            public async Task SingleOrDefaultAsync() => await _elements.SingleOrDefaultAsync();
            public async Task CountAsync() => await _elements.CountAsync();
            public async Task> ToListAsync() => await _elements.ToListAsync();
            public List ToList() => Task.Run(ToListAsync).GetAwaiter().GetResult();

        }
#endif
    }
}

и использование неотличимо от предыдущего EF использования:

public async Task> GetMyResults()
{
    using var ctx = GetMyDbContext();
    const string sql = "select 1 as Result";
    return await ctx.GetDatabase().SqlQuery(sql).ToListAsync();
}

, где GetMyDbContext - это метод для получения контекста вашей базы данных, а GetDatabase - однострочный интерфейс, который возвращает ((DbContext) context). Database для заданный IMyDbContext: DbContext. Это сделано для упрощения одновременных операций EF / EFC.

Это работает для примитивных типов (пример выше), сущностей, локальных классов (но не анонимных). Переименование столбца поддерживается через GetColumnName, но, ... это уже было сделано выше.

На самом деле вы можете создать общий репозиторий и сделать что-то вроде этого

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

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

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

}

Не нацелен непосредственно на сценарий OP, но, поскольку я боролся с этим, я бы хотел отбросить эти ex. методы, которые упрощают выполнение необработанного SQL с DbContext:

public static class DbContextCommandExtensions
{
  public static async Task 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 ExecuteScalarAsync(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();
    }
  }
}

Вы также можете использовать QueryFirst. Как и Dapper, это полностью вне EF. В отличие от Dapper (или EF), вам не нужно поддерживать POCO, вы редактируете свой sql SQL в реальной среде, и он постоянно проверяется на соответствие базе данных. Отказ от ответственности: я являюсь автором QueryFirst.

Вы можете выполнить необработанный 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();

            using (concurrencyDetector.EnterCriticalSection())
            {
                var rawSqlCommand = databaseFacade
                    .GetService()
                    .Build(sql, parameters);

                return rawSqlCommand
                    .RelationalCommand
                    .ExecuteReader(
                        databaseFacade.GetService(),
                        parameterValues: rawSqlCommand.ParameterValues);
            }
        }

        public static async Task ExecuteSqlQueryAsync(this DatabaseFacade databaseFacade, 
                                                             string sql, 
                                                             CancellationToken cancellationToken = default(CancellationToken),
                                                             params object[] parameters)
        {

            var concurrencyDetector = databaseFacade.GetService();

            using (concurrencyDetector.EnterCriticalSection())
            {
                var rawSqlCommand = databaseFacade
                    .GetService()
                    .Build(sql, parameters);

                return await rawSqlCommand
                    .RelationalCommand
                    .ExecuteReaderAsync(
                        databaseFacade.GetService(),
                        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]);
    }
}

Я использовал 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 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(sqltext, p,null,true,null,CommandType.StoredProcedure).ToList();
        }


        }
}

Для запроса данных: без существующего объекта

               string query = "SELECT r.Name as roleName, ur.roleId, u.Id as userId   FROM dbo.AspNetUserRoles AS ur INNER JOIN dbo.AspNetUsers AS u ON ur.UserId = u.Id INNER JOIN dbo.AspNetRoles AS r ON ur.RoleId = r.Id ";

               ICollection usersWithRoles = new List();
                using (var command = _identityDBContext.Database.GetDbConnection().CreateCommand())
                {
                    command.CommandText = query;
                    command.CommandType = CommandType.Text;

                    await _identityDBContext.Database.OpenConnectionAsync();

                    using (var reader = await command.ExecuteReaderAsync())
                    {      
                        while (await reader.ReadAsync())
                        {
                            usersWithRoles.Add(new { 
                                roleName = reader.GetFieldValueAsync(0).Result, 
                                roleId = reader.GetFieldValueAsync(1).Result,
                                userId = reader.GetFieldValueAsync(2).Result
                            });
                        }    
                    }
                }

Подробно:

 [HttpGet]
    [Route("GetAllUsersWithRoles")]
    public async Task GetAllUsersWithRoles()
    {
        string query = "SELECT r.Name as roleName, ur.roleId, u.Id as userId   FROM dbo.AspNetUserRoles AS ur INNER JOIN dbo.AspNetUsers AS u ON ur.UserId = u.Id INNER JOIN dbo.AspNetRoles AS r ON ur.RoleId = r.Id ";
        try
        {
            ICollection usersWithRoles = new List();
            using (var command = _identityDBContext.Database.GetDbConnection().CreateCommand())
            {
                command.CommandText = query;
                command.CommandType = CommandType.Text;

                await _identityDBContext.Database.OpenConnectionAsync();

                using (var reader = await command.ExecuteReaderAsync())
                {      
                    while (await reader.ReadAsync())
                    {
                        usersWithRoles.Add(new { 
                            roleName = reader.GetFieldValueAsync(0).Result, 
                            roleId = reader.GetFieldValueAsync(1).Result,
                            userId = reader.GetFieldValueAsync(2).Result
                        });
                    }    
                }
            }
                return StatusCode(200, usersWithRoles); // Get all users   
        }
        catch (Exception e)
        {
            return StatusCode(500, e);
        }
    }

РЕЗУЛЬТАТ выглядит так:

[
  {
    "roleName": "admin",
    "roleId": "7c9cb1be-e987-4ec1-ae4d-e4c9790f57d8",
    "userId": "12eadc86-6311-4d5e-8be8-df30799df265"
  },
  {
    "roleName": "user",
    "roleId": "a0d5ef46-b1e6-4a53-91ce-9ff5959f1ed8",
    "userId": "12eadc86-6311-4d5e-8be8-df30799df265"
  },
  {
    "roleName": "user",
    "roleId": "a0d5ef46-b1e6-4a53-91ce-9ff5959f1ed8",
    "userId": "3e7cd970-8c52-4dd1-847c-f824671ea15d"
  }
]

С 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(@"SELECT u.Id ,fname , lname ,username FROM dbo.Users").ToList();

В моем случае использовалась хранимая процедура вместо необработанного 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 class

public DbSet 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();
• 100001

Само расширение:

public static class QueryHelper
{
    private static string GetColumnName(this MemberInfo info)
    {
        List list = info.GetCustomAttributes().ToList();
        return list.Count > 0 ? list.Single().Name : info.Name;
    }
    /// 
    /// Executes raw query with parameters and maps returned values to column property names of Model provided.
    /// Not all properties are required to be present in model (if not present - null)
    /// 
    public static async IAsyncEnumerable ExecuteQuery(
        [NotNull] this DbContext db,
        [NotNull] string query,
        [NotNull] params SqlParameter[] parameters)
        where T : class, new()
    {
        await using DbCommand command = db.Database.GetDbConnection().CreateCommand();
        command.CommandText = query;
        command.CommandType = CommandType.Text;
        if (parameters != null)
        {
            foreach (SqlParameter parameter in parameters)
            {
                command.Parameters.Add(parameter);
            }
        }
        await db.Database.OpenConnectionAsync();
        await using DbDataReader reader = await command.ExecuteReaderAsync();
        List lstColumns = new T().GetType()
            .GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).ToList();
        while (await reader.ReadAsync())
        {
            T newObject = new();
            for (int i = 0; i < reader.FieldCount; i++)
            {
                string name = reader.GetName(i);
                PropertyInfo prop = lstColumns.FirstOrDefault(a => a.GetColumnName().Equals(name));
                if (prop == null)
                {
                    continue;
                }
                object val = await reader.IsDBNullAsync(i) ? null : reader[i];
                prop.SetValue(newObject, val, null);
            }
            yield return newObject;
        }
    }
}

Используемая модель (обратите внимание, что имена столбцов отличаются от фактических имен свойств):

public class School
{
    [Key] [Column("SCHOOL_ID")] public int SchoolId { get; set; }

    [Column("CLOSE_DATE", TypeName = "datetime")]
    public DateTime? CloseDate { get; set; }

    [Column("SCHOOL_ACTIVE")] public bool? SchoolActive { get; set; }
}

Фактическое использование:

public async Task ActivateSchool(int schoolId)
{
    // note that we're intentionally not returning "SCHOOL_ACTIVE" with select statement
    // this might be because of certain IF condition where we return some other data
    return await _context.ExecuteQuery(
        "UPDATE SCHOOL SET SCHOOL_ACTIVE = 1 WHERE SCHOOL_ID = @SchoolId; SELECT SCHOOL_ID, CLOSE_DATE FROM SCHOOL",
        new SqlParameter("@SchoolId", schoolId)
    ).SingleAsync();
}

В Core 2.1 вы можете сделать что-то вроде этого:

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

, а затем определите вашу процедуру SQL, например:

public async Task> GetRanks(string value1, Nullable value2)
{
    SqlParameter value1Input = new SqlParameter("@Param1", value1?? (object)DBNull.Value);
    SqlParameter value2Input = new SqlParameter("@Param2", value2?? (object)DBNull.Value);

    List getRanks = await this.Query().FromSql("STORED_PROCEDURE @Param1, @Param2", value1Input, value2Input).ToListAsync();

    return getRanks;
}

Таким образом, модель рангов не будет создаваться в вашей БД.

Теперь в вашем контроллере / действии вы можете вызвать:

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

Таким образом вы можете вызывать процедуры Raw SQL.

Основываясь на других ответах, я написал этого помощника, который выполняет задачу, включая пример использования:

public static class Helper
{
    public static List RawSqlQuery(string query, Func 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();

                    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, это высокий приоритет для публикации 2.0. Проблема отслеживается здесь.

Пока что, пока не появится что-то новое от 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.

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

FromSql не работает с полным запросом. Пример: если вы хотите включить предложение WHERE, оно будет проигнорировано.

Некоторые ссылки:

Executing Raw SQL Queries using Entity Framework Core

Необработанные SQL-запросы

2022 WebDevInsider