【转载】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
浙公网安备 33010602011771号