20230223-EF6用原生的命令执行SQL命令

关注公众号 `N学无止界` 获取更多​​​​​​​

目录:

一、环境说明

二、背景

三、示例代码

四、思考总结

一、环境说明

本机环境:windows10 操作系统
使用工具:Visual Studio 2022
Net版本:Net6.0

二、背景

在使用winform EF 开发过程中,需要用到类似 QueryList QueryScalar 等功能的时候,发现EF6只提供标量的List列表,且在一个context中,QueryScalar(执行SQL命令的事务有问题,无法更新到数据),所以需要一个扩展来执行特定的命令(SQL语句),以及可以实现动态的(dynamic)类型来满足开发需要。

官网参考资料 :

SQL 查询 - EF Core | Microsoft Learn

查询标量(非实体)类型

备注

EF Core 7.0 中已引入此功能。

虽然可以使用 FromSql 来查询模型中定义的实体,但如果使用 SqlQuery,你就可以通过 SQL 轻松查询非实体标量类型,无需下降到较低级别的数据访问 API。 例如,以下查询从 Blogs 表中提取所有 ID:C#复制


var ids = context.Database
    .SqlQuery<int>($"SELECT [BlogId] FROM [Blogs]")
    .ToList();

还可以在 SQL 查询的基础上组合使用 LINQ 运算符。 但是,由于 SQL 成为子查询,其输出列需要由 SQL EF 添加项来引用,因此必须为输出列 Value 命名。 例如,以下查询返回的 ID 高于 ID 平均值:C#复制


var overAverageIds = context.Database
    .SqlQuery<int>($"SELECT [BlogId] AS [Value] FROM [Blogs]")
    .Where(id => id > context.Blogs.Average(b => b.BlogId))
    .ToList();

FromSql 可与数据库提供程序支持的任何标量类型配合使用。 如果想要使用数据库提供程序不支持的类型,可以使用约定前配置为其定义值转换。

SqlQueryRaw 允许动态构造 SQL 查询,就像 FromSqlRaw 对实体类型所做的那样。

执行非查询 SQL

在某些情况下,可能需要执行不返回任何数据的 SQL,通常用于修改数据库中的数据或调用不返回任何结果集的存储过程。 可以通过 ExecuteSql 完成此操作:C#复制


using (var context = new BloggingContext())
{
    var rowsModified = context.Database.ExecuteSql($"UPDATE [Blogs] SET [Url] = NULL");
}

它会执行提供的 SQL 并返回修改的行的数目。 ExecuteSql 使用安全的参数化来防止 SQL 注入,就像 FromSql 一样,而 ExecuteSqlRaw 允许动态构造 SQL 查询,就像 FromSqlRaw 对查询所做的那样。

备注

在 EF Core 7.0 之前,有时必须使用 ExecuteSql API 对数据库执行“批量更新”,如上所示;这比在查询所有匹配行后使用 SaveChanges 来修改它们要高效得多。 EF Core 7.0 引入了 ExecuteUpdate 和 ExecuteDelete,因此可以通过 LINQ 表达高效的批量更新操作。 建议尽可能使用这些 API 而非 ExecuteSql。

限制

从 SQL 查询返回实体类型时,需注意以下几个限制:

  • SQL 查询必须返回实体类型的所有属性的数据。

  • 结果集中的列名必须与属性映射到的列名称匹配。 请注意,此行为与 EF6 不同;EF6 忽略了 SQL 查询的属性-列映射,只需结果集列名与这些属性名相匹配即可。

  • SQL 查询不能包含关联数据。 但是,在许多情况下你可以在查询后面紧跟着使用

Include 方法以返回关联数据(请参阅包含关联数据)。

三、示例代码

说明:编写扩展程序,用基本的SQL语句支持动态或者泛型支持执行数据库

程序代码:


using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Data;
using System.Dynamic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OpenCvSharp;

namespace .......config
{
    /// <summary>
    /// DbContextExtension
    /// </summary>
    public static class DbContextExtension
    {
        /// <summary>
        /// QueryList
        /// </summary>
        /// <param name="context"></param>
        /// <param name="sql"></param>
        /// <param name="map"></param>
        /// <returns></returns>
        public static IEnumerable<dynamic> QueryList(this DbContext context, string sql, Dictionary<string, object>? map = null)
        {
            using (var cmd = context.Database.GetDbConnection().CreateCommand())
            {
                CmdExecute(cmd, sql, map);
                using (var dataReader = cmd.ExecuteReader())
                {
                    while (dataReader.Read())
                    {
                        var row = new ExpandoObject() as IDictionary<string, object>;
                        for (var fieldCount = 0; fieldCount < dataReader.FieldCount; fieldCount++)
                        {
                            row.Add(dataReader.GetName(fieldCount), dataReader[fieldCount]);
                        }
                        yield return row;
                    }
                }
            }
        }

        /// <summary>
        /// QueryList
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="context"></param>
        /// <param name="sql"></param>
        /// <param name="itemReader"></param>
        /// <param name="map"></param>
        /// <returns></returns>
        public static IEnumerable<T> QueryList<T>(this DbContext context, string sql, Func<IDataReader, T> itemReader, Dictionary<string, object>? map = null)
        {
            using (var cmd = context.Database.GetDbConnection().CreateCommand())
            {
                CmdExecute(cmd, sql, map);
                using (var dataReader = cmd.ExecuteReader())
                {
                    while (dataReader.Read())
                    {
                        yield return itemReader(dataReader);
                    }
                }
            }
        }

        /// <summary>
        /// QueryList
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="context"></param>
        /// <param name="sql"></param>
        /// <param name="itemReader"></param>
        /// <param name="map"></param>
        /// <returns></returns>
        public static T? QueryScalar<T>(this DbContext context, string sql, Dictionary<string, object>? map = null)
        {
            using (var cmd = context.Database.GetDbConnection().CreateCommand())
            {
                CmdExecute(cmd, sql, map);
                object? result = cmd.ExecuteScalar();
                return result is DBNull ? default(T) : (T)result!;
            }
        }

        /// <summary>
        /// CmdExecute
        /// </summary>
        /// <param name="cmd"></param>
        /// <param name="sql"></param>
        /// <param name="map"></param>
        private static void CmdExecute(DbCommand cmd, string sql, Dictionary<string, object>? map)
        {
            cmd.CommandText = sql;
            if (null != cmd.Connection && cmd.Connection.State != ConnectionState.Open) { cmd.Connection.Open(); }
            if (null != map)
            {
                foreach (KeyValuePair<string, object> p in map)
                {
                    DbParameter dbParameter = cmd.CreateParameter();
                    dbParameter.ParameterName = p.Key;
                    dbParameter.Value = p.Value;
                    cmd.Parameters.Add(dbParameter);
                }
            }
        }
    }
}

调用主要代码:

四、思考总结


///核心代码

        /// <summary>
        /// QueryList
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="context"></param>
        /// <param name="sql"></param>
        /// <param name="itemReader"></param>
        /// <param name="map"></param>
        /// <returns></returns>
        public static T? QueryScalar<T>(this DbContext context, string sql, Dictionary<string, object>? map = null)
        {
            using (var cmd = context.Database.GetDbConnection().CreateCommand())
            {
                CmdExecute(cmd, sql, map);
                object? result = cmd.ExecuteScalar();
                return result is DBNull ? default(T) : (T)result!;
            }
        }

        /// <summary>
        /// CmdExecute
        /// </summary>
        /// <param name="cmd"></param>
        /// <param name="sql"></param>
        /// <param name="map"></param>
        private static void CmdExecute(DbCommand cmd, string sql, Dictionary<string, object>? map)
        {
            cmd.CommandText = sql;
            if (null != cmd.Connection && cmd.Connection.State != ConnectionState.Open) { cmd.Connection.Open(); }
            if (null != map)
            {
                foreach (KeyValuePair<string, object> p in map)
                {
                    DbParameter dbParameter = cmd.CreateParameter();
                    dbParameter.ParameterName = p.Key;
                    dbParameter.Value = p.Value;
                    cmd.Parameters.Add(dbParameter);
                }
            }
        }
    }

上述代码通过 context.Database.GetDbConnection().CreateCommand() 得到Command , 然后根据底层的SQL命令进行执行原来的逻辑。根据需要的场景满足业务需求,后续代码可以进一步封装进行泛型变换,支持VO BO 等面向领域对象的获取,欢迎大家沟通。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jack_software

感谢打赏,我努力提供优质内容~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值