原始 SQL 运行 与 Entity Framework 核心上下文相比如何?

How is raw SQL run against an Entity Framework Core context?

我已经使用 Entity Framework 很长时间了,但在极端情况下我需要使用 SQL。我想知道我是否可以为此使用现有的 Entity Framework 核心上下文。这是我目前拥有的,但是 queryResults 变量包含一个“-1”值,而不是学生列表,在 运行 之后:

string tableName = "Students";
var queryResults = db.Database.ExecuteSqlRaw(@"SELECT * FROM {0}", tableName);

有什么想法吗?


"I was wondering if I could use my existing Entity Framework Core context for this or not":

是的,您可以使用 existing databaseContext,但您必须在 dbContext Entity 上执行该查询,请参见下面的示例:

var sqlCommand = $"SELECT * FROM Students";
var executeSQL = await _context.Students.FromSqlRaw(sqlCommand).ToListAsync();
return Ok(executeSQL);

Output:

Note: As you can see I am executing sqlCommand on Students dbContext this is valid. But using DbContext you cannot pass the table name dynamically. You must need to define it explicitly.

希望以上步骤对您有相应的指导,您可以看看official document for more details here

Update Using Ado.Net Connection:

     using (var connection = _context.Database.GetDbConnection())
            {
                connection.Open();
                var tableName = "Students";
                List<Student> _listStudent = new List<Student>();
                var command = connection.CreateCommand();
                command.CommandType = CommandType.Text;
                command.CommandText = string.Format("SELECT * FROM [{0}];", tableName);
                SqlDataReader reader = (SqlDataReader)command.ExecuteReader();
                while (reader.Read())
                {
                    var student = new Student(); // You have to bind dynamic property here based on your table entities
                    student.FirstName = reader["FirstName"].ToString(); // Remember Type Casting is required here it has to be according to database column data type
                    student.LastName = reader["LastName"].ToString();
                    _listStudent.Add(student);

                }
                reader.Close();
                command.Dispose();
                connection.Close();

            }

有可能;我只是为了一个宠物项目而不得不这样做。

您需要引用 Microsoft.EntityFrameworkCore.Relational NuGet。

ConsoleApp 示例:

Program.cs

using System.Collections.Generic;

namespace EfDirectSql
{
    class Program
    {
        /*
         * written:  VS2019 .Net Core 3.1 Console App
         * 
         * used nugets:
         * 
         *      Microsoft.EntityFrameworkCore.SqlServer  3.1.0
         *      Microsoft.EntityFrameworkCore.Relational 3.1.0
         *
        */
        static void Main(string[] args)
        {
            // attention: supply your database server name
            ApplicationContext context = new ApplicationContext("?YOURSERVER?", "Master");

            //  note:   leveraging included extension methods for the dbContext class.
            object scalarResult = context.ExecuteScalar("SELECT COUNT(1) FROM Master.dbo.SysObjects");
            object nonQueryResult = context.ExecuteNonQuery("SELECT * FROM Master.dbo.SysObjects");  // likely your -1 

            IEnumerable<SysObject> readerResult = context.ExecuteReader<SysObject>("SELECT * FROM Master.dbo.SysObjects");
        }
    }
}

ApplicationContext.cs

using Microsoft.EntityFrameworkCore;

namespace EfDirectSql
{
    public class ApplicationContext
    : DbContext
    {
        public ApplicationContext(string serverName, string catalogName)
        {
            this.ServerName = serverName;
            this.CatalogName = catalogName;
        }

        public readonly string ServerName;

        public readonly string CatalogName;

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer($"Data Source={this.ServerName};Initial Catalog={this.CatalogName};Integrated Security=true;");

            base.OnConfiguring(optionsBuilder);
        }
    }
}

DbContextExtensions.cs

using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection;

namespace EfDirectSql
{
    public static class DbContextExtensions
    {
        public static object ExecuteScalar
        (
            this DbContext context,
            string sql
        )
        {
            IDbConnection connection = context.Database.GetDbConnection();
            IDbCommand command = connection.CreateCommand();
            object result = null;

            try
            {
                connection.Open();          

                command.CommandText = sql;
                command.CommandType = CommandType.Text;

                result = command.ExecuteScalar();
            }
            finally
            {
                connection.Close();
            }

            return result;
        }

        public static int ExecuteNonQuery
        (
            this DbContext context,
            string sql
        )
        {
            IDbConnection connection = context.Database.GetDbConnection();
            IDbCommand command = connection.CreateCommand();

            int result;

            try
            {
                connection.Open();          

                command.CommandText = sql;
                command.CommandType = CommandType.Text;

                result = command.ExecuteNonQuery();

                //  likely the -1
            }
            finally
            {
                connection.Close();
            }

            return result;
        }

        public static IEnumerable<TType> ExecuteReader<TType>
        (
            this DbContext context,
            string sql
        )
        where TType : class, new()
        {
            IDbConnection connection = context.Database.GetDbConnection();
            IDbCommand command = connection.CreateCommand();

            IEnumerable<TType> result = new List<TType>();

            try
            {
                connection.Open();          

                command.CommandText = sql;
                command.CommandType = CommandType.Text;

                IDataReader reader = command.ExecuteReader(CommandBehavior.Default);

                result = Convert<TType>(reader);
            }
            finally
            {
                connection.Close();
            }

            return result;
        }

        private static IEnumerable<TType> Convert<TType>(IDataReader reader)
        where TType : class, new()
        {
            List<PropertyInfo> properties = typeof(TType)
                .GetProperties(BindingFlags.Public | BindingFlags.Instance)
                .Where(p => p.CanWrite)
                .ToList();

            IList<TType> instances = new List<TType>();

            while (reader.Read())
            {
                TType instance = new TType();

                properties
                    .ForEach
                    (p =>
                        // for the purposes of the example, this works - could be outliers.
                        p.SetValue(instance, reader[p.Name] == DBNull.Value ? null : reader[p.Name])
                    );

                instances.Add(instance);
            }

            return instances;
        }
    }
}

SysObject.cs

namespace EfDirectSql
{
    //  shortened represenation of the MS-SQL sysobject table
    public class SysObject
    {
        public string name { get; set; }

        public int id { get; set; }

        public string xtype { get; set; }

        public int uid { get; set; }

        public int info { get; set; }

        public int status { get; set; }

        //  the rest are not needed for a demo.
    }
}