【转载】EF Core下 怎么跑sql语句———————感谢大佬的分享

 

版权声明:本文为CSDN博主「sslyc8991」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/sslyc8991/article/details/79756569

再次感谢大佬的分享 

兴致来了,多写一篇吧。

所有转netcore的小伙伴们都发现了: ef core跟以前的ef差距非常大,view(视图)无法通过dbfirst生成了,存储过程也一样(虽然我现在开始转codefirst了)。 然而,如果真的想直接执行sql语句怎么办? 我们发现context下的Database属性跟以前也不一样了,只能做些事务操作,没有执行sql了。可以执行sql的变成了每张具体的表(DbSet<T>)下面的FromSql方法了(需要显式引用Microsoft.EntityFrameworkCore命名空间)。 但是这个方法存在问题,只能返回该表类型的结果,无法返回任意类型。

so,跟大家一样,我去网上搜搜解决方案。查到了一个方案。

然而,为了响应ef本身跨数据库种类的设计要求,我觉得应该做一个可以自行根据数据库类型判断需要执行什么语句的组件,所以我给我们亲爱的dbcontext写了一个扩展:

首先请自觉nuget拉一下包 Microsoft.EntityFrameworkCore.Relational。

整个扩展的大体思路是: 针对一次查询,将各种数据库的查询语句封装到一起,在运行时自动判断当前连接的数据库环境,执行相应的语句。这样不破坏依赖注入的原则,需要的是编程人员根据可能连接的数据库种类,写对应库的查询语句。

另外这里还用了一个神奇的技术(我自己早就在使用了),以方便查询参数的操作。就是使用匿名类型来提供查询语句参数,如"select * from abc where id between @start and @end"语句,只需要提供参数 new {start = 1, end=100} 作为参数传入即可。不用再去new 一个parameter,每次填一堆。

 

废话够多了,直接上代码了:

 

  1 using Microsoft.Data.Sqlite;
  2 using Microsoft.EntityFrameworkCore;
  3 using MySql.Data.MySqlClient;
  4 using System;
  5 using System.Collections.Generic;
  6 using System.Data;
  7 using System.Data.Common;
  8 using System.Data.SqlClient;
  9 using System.Text;
 10  
 11 namespace SS.Data.EntityFramework.Core
 12 {
 13     /// <summary>
 14     /// 数据库查询语句
 15     /// </summary>
 16     public class DbContextSqlQueryCommand
 17     {
 18         /// <summary>
 19         /// 使用不含参数的查询语句
 20         /// </summary>
 21         /// <param name="query"></param>
 22         public DbContextSqlQueryCommand(string query)
 23         {
 24             Query = query;
 25         }
 26         /// <summary>
 27         /// 使用包含参数的查询语句
 28         /// </summary>
 29         /// <param name="query"></param>
 30         public DbContextSqlQueryCommand(string query, object @params)
 31         {
 32             Query = query;
 33             Parameters = @params;
 34         }
 35         /// <summary>
 36         /// 查询语句
 37         /// </summary>
 38         public string Query { get; set; }
 39         /// <summary>
 40         /// 参数
 41         /// </summary>
 42         public object Parameters { get; set; }
 43     }
 44  
 45     /// <summary>
 46     /// 数据库查询语句集合
 47     /// </summary>
 48     public class DbContextSqlQueryCommands
 49     {
 50         /// <summary>
 51         /// 数据库为SqlServer时使用的查询语句
 52         /// </summary>
 53         public DbContextSqlQueryCommand Sql { get; set; }
 54         /// <summary>
 55         /// 数据库为MySql时使用的查询语句
 56         /// </summary>
 57         public DbContextSqlQueryCommand MySql { get; set; }
 58         /// <summary>
 59         /// 数据库为InMemory时使用的查询语句
 60         /// </summary>
 61         public DbContextSqlQueryCommand InMemory { get; set; }
 62         /// <summary>
 63         /// 数据库为Sqlite时使用的查询语句
 64         /// </summary>
 65         public DbContextSqlQueryCommand Sqlite { get; set; }
 66     }
 67  
 68     /// <summary>
 69     /// 数据库类型
 70     /// </summary>
 71     public enum DbContextType
 72     {
 73         InMemory = 0,
 74         SqlServer = 1,
 75         MySql = 2,
 76         Sqlite = 3,
 77     }
 78  
 79     /// <summary>
 80     /// EF上下文扩展
 81     /// </summary>
 82     public static class DbContextExtensions
 83     {
 84         //拼接参数
 85         private static void combineParams(DbContextType type, ref DbCommand command, object @params = null)
 86         {
 87             if (@params != null)
 88             {
 89                 Type paramType;
 90                 string prefix;
 91                 switch (type)
 92                 {
 93                     case DbContextType.InMemory:
 94                         throw new Exception("未实现的数据库类型");
 95                     case DbContextType.SqlServer:
 96                         paramType = typeof(SqlParameter);
 97                         prefix = "@";
 98                         break;
 99                     case DbContextType.MySql:
100                         paramType = typeof(MySqlParameter);
101                         prefix = "@";
102                         break;
103                     case DbContextType.Sqlite:
104                         paramType = typeof(SqliteParameter);
105                         prefix = "@";
106                         break;
107                     default:
108                         throw new Exception("未实现的数据库类型");
109                 }
110                 foreach (var param in @params.GetType().GetProperties())
111                 {
112                     var paramItem = Activator.CreateInstance(paramType, $"{prefix}{param.Name}", (object)param.GetValue(@params));
113                     command.Parameters.Add(paramItem);
114                 }
115             }
116         }
117         //创建命令(同时返回连接符)
118         private static DbCommand createCommand(DbContext context, DbContextSqlQueryCommands commands, out DbConnection connection)
119         {
120             var conn = context.Database.GetDbConnection();
121             connection = conn;
122             conn.Open();
123             var cmd = conn.CreateCommand();
124             if (commands.Sqlite != null && context.Database.IsSqlite())
125             {
126                 cmd.CommandText = commands.Sqlite.Query;
127                 combineParams(DbContextType.Sqlite, ref cmd, commands.Sqlite.Parameters);
128             }
129             else if(commands.MySql != null && context.Database.IsMySql())
130             {
131                 cmd.CommandText = commands.MySql.Query;
132                 combineParams(DbContextType.MySql, ref cmd, commands.MySql.Parameters);
133             }
134             else if (commands.Sql != null && context.Database.IsSqlServer())
135             {
136                 cmd.CommandText = commands.Sql.Query;
137                 combineParams(DbContextType.SqlServer, ref cmd, commands.Sql.Parameters);
138             }
139             else if (commands.InMemory != null)
140             {
141                 throw new NotImplementedException();
142             }
143             return cmd;
144         }
145  
146         /// <summary>
147         /// 执行sql语句,返回受影响行数
148         /// </summary>
149         /// <param name="context">EF上下文</param>
150         /// <param name="commands">数据库查询语句集合</param>
151         /// <returns>受影响行数</returns>
152         public static int Exec(this DbContext context, DbContextSqlQueryCommands commands)
153         {
154             var command = createCommand(context, commands, out var conn);
155             var rsl = command.ExecuteNonQuery();
156             conn.Close();
157             return rsl;
158         }
159  
160         /// <summary>
161         /// 查询数据库
162         /// </summary>
163         /// <param name="context">EF上下文</param>
164         /// <param name="commands">数据库查询语句集合</param>
165         /// <returns>数据DataTable</returns>
166         public static DataTable Query(this DbContext context, DbContextSqlQueryCommands commands)
167         {
168             var command = createCommand(context, commands, out var conn);
169             var reader = command.ExecuteReader();
170             DataTable dt = new DataTable();
171             dt.Load(reader);
172             reader.Close();
173             conn.Close();
174             return dt;
175         }
176  
177         /// <summary>
178         /// 查询数据库,返回多个查询结果集
179         /// </summary>
180         /// <param name="context">EF上下文</param>
181         /// <param name="commands">数据库查询语句集合</param>
182         /// <returns>数据DataSet</returns>
183         public static DataSet QuerySet(this DbContext context, DbContextSqlQueryCommands commands)
184         {
185             var dt = Query(context, commands);
186             var ds = new DataSet();
187             ds.Tables.Add(dt);
188             return ds;
189         }
190  
191         /// <summary>
192         /// 查询数据库,返回IEnumerable的强类型数据
193         /// </summary>
194         /// <typeparam name="T">查询结果类型</typeparam>
195         /// <param name="context">EF上下文</param>
196         /// <param name="commands">数据库查询语句集合</param>
197         /// <returns>IEnumerable的强类型数据</returns>
198         public static IEnumerable<T> Query<T>(this DbContext context, DbContextSqlQueryCommands commands)
199         {
200             var dt = Query(context, commands);
201             return dt.ToEnumerable<T>();
202         }
203  
204         /// <summary>
205         /// 查询数据库,返回第一条数据
206         /// </summary>
207         /// <param name="context">EF上下文</param>
208         /// <param name="commands">数据库查询语句集合</param>
209         /// <returns>查询到的第一条数据或null</returns>
210         public static DataRow QueryOne(this DbContext context, DbContextSqlQueryCommands commands)
211         {
212             var dt = Query(context, commands);
213             return dt.Rows.Count > 0 ? dt.Rows[0] : null;
214         }
215  
216         /// <summary>
217         /// 查询数据库,返回第一条强类型数据
218         /// </summary>
219         /// <typeparam name="T">查询结果类型</typeparam>
220         /// <param name="context">EF上下文</param>
221         /// <param name="commands">数据库查询语句集合</param>
222         /// <returns>查询到的第一条强类型数据</returns>
223         public static T QueryOne<T>(this DbContext context, DbContextSqlQueryCommands commands)
224         {
225             var dr = QueryOne(context, commands);
226             return dr.ToObject<T>();
227         }
228  
229         /// <summary>
230         /// 查询数据库,返回唯一数据
231         /// </summary>
232         /// <param name="context">EF上下文</param>
233         /// <param name="commands">数据库查询语句集合</param>
234         /// <returns>查询到的唯一数据</returns>
235         public static object QueryObject(this DbContext context, DbContextSqlQueryCommands commands)
236         {
237             var command = createCommand(context, commands, out var conn);
238             var rsl = command.ExecuteScalar();
239             conn.Close();
240             return rsl;
241         }
242  
243         /// <summary>
244         /// 查询数据库,返回唯一强类型数据
245         /// </summary>
246         /// <typeparam name="T">查询结果类型</typeparam>
247         /// <param name="context">EF上下文</param>
248         /// <param name="commands">数据库查询语句集合</param>
249         /// <returns>查询到的唯一强类型数据</returns>
250         public static T QueryObject<T>(this DbContext context, DbContextSqlQueryCommands commands)
251         {
252             return (T)QueryObject(context, commands);
253         }
254     }
255 }

这里并未提供ToObject和ToEnumerable等扩展方法的实现,这些方法只是简单的将DataTable或者DataRow转换成强类型,利用反射可以轻松做到,有兴趣的朋友可以自己实现一下。 或者maybe我会在后面的文章里贴一下。

 

这个扩展是针对DbContext的扩展,暴露的方法大体上是:Exec 返回影响行数, Query 返回查询的列表,可以通过泛型重载Query<T>直接返回强类型的IEnumerable接口对象,QueryOne返回一行,同理QueryOne<T>返回强类型对象。QueryObject返回object,同理QueryObject<T>返回简单类型值。

 

具体用法示例:

1 var list = db.Query<xxxxxxxClass>(new DbContextSqlQueryCommands
2         {
3             //调用存储过程
4             Sql = new DbContextSqlQueryCommand(@"exec GetXXXProcedure @ids", new
5             {
6                 ids = string.Join(",", cids),
7             })
8         });

so, that's it. 好好工作,天天向太阳……

 

----------------------------------------华丽的分割线-------------------------------------

补充一句,此扩展依赖Microsoft.EntityFrameWorkCore.xxx 的一堆,包括.SqlServer, .Sqlite,.InMemory等,其中Mysql微软未提供官方库,因而用了Pomelo.EntityFrameworkCore.MySql这个第三方库。
另外一点就是,上述代码其实未实现InMemory的库的操作,主要是我还没搞清InMemory的库到底怎么用

-----------------------------------------我是最新分割线-------------------------------------------

有朋友问我ToEnumerable和ToObject的实现,这里贴一下吧
————————————————

    /// <summary>
    /// 数据相关扩展
    /// </summary>
    public static class DataExtensions
    {
        //从属性列表为DataTable创建列
        private static void createColumns(DataTable table, PropertyInfo[] piArr)
        {
            table.Columns.Clear();
            foreach (var pi in piArr)
            {
                table.Columns.Add(pi.Name, pi.PropertyType);
            }
        }
 
        //用obj的属性填充row
        private static void fillDataRow(DataRow row, object obj, PropertyInfo[] piArr)
        {
            foreach (var pi in piArr)
            {
                row[pi.Name] = pi.GetValue(obj);
            }
        }
 
        //用row的栏填充obj
        public static void fillObject<T>(T obj, DataRow row, PropertyInfo[] piArr)
        {
            foreach (var pi in piArr)
            {
                try
                {
                    pi.SetValue(obj, row[pi.Name]);
                }
                catch
                {
                }
            }
        }
 
        /// <summary>
        /// 从类型为DataTable创建Columns
        /// </summary>
        /// <param name="table">DataTable对象</param>
        /// <param name="type">作为创建模板的类型</param>
        public static void CreateColumsFromType(this DataTable table, Type type)
        {
            PropertyInfo[] piArr = type.GetProperties();
            createColumns(table, piArr);
        }
 
        /// <summary>
        /// 从object为DataTable创建Columns
        /// </summary>
        /// <param name="table">DataTable对象</param>
        /// <param name="obj">作为创建模板的object</param>
        public static void CreateColumsFromObject(this DataTable table, object obj)
        {
            CreateColumsFromType(table, obj.GetType());
        }
 
        /// <summary>
        /// 将DataRow转换为强类型
        /// </summary>
        /// <typeparam name="T">要转换为的强类型</typeparam>
        /// <param name="row">要转换的DataRow对象</param>
        /// <returns>转换后的强类型对象</returns>
        public static T ToObject<T>(this DataRow row)
        {
            if (row == null)
            {
                return default(T);
            }
            var obj = Activator.CreateInstance<T>();
            PropertyInfo[] piArr = typeof(T).GetProperties();
            fillObject(obj, row, piArr);
            return obj;
        }
 
        /// <summary>
        /// 将对象转换为DataRow对象
        /// </summary>
        /// <param name="obj">要转换的对象</param>
        /// <returns>转换后的DataRow对象</returns>
        public static DataRow ToDataRow(this object obj)
        {
            if (obj == null)
            {
                return null;
            }
            PropertyInfo[] piArr = obj.GetType().GetProperties();
            DataTable dt = new DataTable();
            createColumns(dt, piArr);
 
            DataRow row = dt.NewRow();
            fillDataRow(row, obj, piArr);
 
            return row;
        }
 
        /// <summary>
        /// 将对象转换为属于指定DataTable的DataRow对象
        /// </summary>
        /// <param name="table">属于的table</param>
        /// <param name="obj">要转换的对象</param>
        /// <returns>转换后的属于指定DataTable的DataRow对象</returns>
        public static DataRow ToDataRow(this object obj, DataTable table)
        {
            if (obj == null)
            {
                return null;
            }
            PropertyInfo[] piArr = obj.GetType().GetProperties();
            createColumns(table, piArr);
 
            DataRow row = table.NewRow();
            fillDataRow(row, obj, piArr);
 
            return row;
        }
 
 
        /// <summary>
        /// 将DataTable转换为IEnumerable的强类型对象
        /// </summary>
        /// <typeparam name="T">要转换为的强类型</typeparam>
        /// <param name="table">要转换的DataTable对象</param>
        /// <returns>转换后的IEnumerable的强类型对象</returns>
        public static IEnumerable<T> ToEnumerable<T>(this DataTable table)
        {
            List<T> list = new List<T>();
            PropertyInfo[] piArr = typeof(T).GetProperties();
            foreach (DataRow row in table.Rows)
            {
                var obj = Activator.CreateInstance<T>();
                fillObject(obj, row, piArr);
                list.Add(obj);
            }
            return list.AsEnumerable<T>();
        }
        
        /// <summary>
        /// 将IEnumerable的强类型对象转换为DataTable
        /// </summary>
        /// <typeparam name="T">要转换的强类型</typeparam>
        /// <param name="objArr">要转换的IEnumerable的强类型对象</param>
        /// <returns>转换后的DataTable对象</returns>
        public static DataTable ToDataTable<T>(this IEnumerable<T> objArr)
        {
            DataTable dt = new DataTable();
            PropertyInfo[] piArr = typeof(T).GetProperties();
            createColumns(dt, piArr);
 
            foreach (var obj in objArr)
            {
                DataRow row = dt.NewRow();
                fillDataRow(row, obj, piArr);
                dt.Rows.Add(row);
            }
            return dt;
        }
 
        /// <summary>
        /// 将DataSet转换为IEnumerable的IEnumerable的强类型对象
        /// </summary>
        /// <typeparam name="T">要转换为的强类型</typeparam>
        /// <param name="set">要转换的DataSet对象</param>
        /// <returns>转换后的IEnumerable的IEnumerable的强类型对象</returns>
        public static IEnumerable<IEnumerable<T>> ToEnumerableEnumerable<T>(this DataSet set)
        {
            List<IEnumerable<T>> rsl = new List<IEnumerable<T>>();
            PropertyInfo[] piArr = typeof(T).GetProperties();
 
            foreach (DataTable dt in set.Tables)
            {
                List<T> list = new List<T>();
                foreach (DataRow row in dt.Rows)
                {
                    var obj = Activator.CreateInstance<T>();
                    fillObject(obj, row, piArr);
                    list.Add(obj);
                }
                rsl.Add(list.AsEnumerable<T>());
            }
            return rsl.AsEnumerable<IEnumerable<T>>();
        }
 
    }

版权声明:本文为CSDN博主「sslyc8991」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/sslyc8991/article/details/79756569

posted @ 2022-04-13 21:05  .NetCat  阅读(238)  评论(0)    收藏  举报