如何通过自定义SQL查询将IQueryable转换为IEnumerable?
当然有可行的方法,但得先明确你的场景——从示例代码看,你用的是内存中的IQueryable(通过List.AsQueryable()生成的),而不是连接数据库的IQueryable(比如EF Core的DbSet)。这两种场景的处理方式不一样,下面给你针对性的解决方案:
针对内存IQueryable的解决方案
你的数据源是内存中的集合,原生SQL语法没法直接被LINQ to Objects解析,所以需要转个弯处理:
方案1:解析SQL转换为LINQ表达式
如果你的SQL比较简单(比如示例里的单条件WHERE查询),可以手动解析SQL中的条件,然后用LINQ表达式树构建过滤逻辑,最后转成IEnumerable。
比如给IQueryable写个扩展方法:
public static IEnumerable<T> ConvertToIEnumerable<T>(this IQueryable<T> source, string sql) { // 仅针对示例中的简单SQL做解析,复杂SQL需要更完善的解析逻辑 if (sql.StartsWith("SELECT * FROM c WHERE c.ID = ")) { // 提取ID值(注意处理单引号) var idValue = sql.Substring("SELECT * FROM c WHERE c.ID = ".Length).Trim('\''); // 用表达式树构建Where条件 var parameter = Expression.Parameter(typeof(T), "c"); var idProperty = Expression.Property(parameter, "ID"); var constantValue = Expression.Constant(idValue); var equalExpr = Expression.Equal(idProperty, constantValue); var filterLambda = Expression.Lambda<Func<T, bool>>(equalExpr, parameter); // 过滤后转成IEnumerable return source.Where(filterLambda).AsEnumerable(); } // 其他SQL场景可以扩展解析逻辑,默认返回全部数据 return source.AsEnumerable(); }
这个方法的好处是性能高,不需要额外依赖,但缺点是只能处理简单SQL,复杂的JOIN、GROUP BY等语法很难解析。
方案2:用内存数据库执行原生SQL
如果需要支持复杂的SQL语法,可以把内存数据导入到内存数据库(比如SQLite内存模式),然后执行原生SQL查询,最后把结果转成IEnumerable。
示例代码如下:
using Microsoft.Data.Sqlite; using System.Data; public static IEnumerable<Book> ConvertToIEnumerable(this IQueryable<Book> source, string sql) { // 创建内存SQLite连接 using var connection = new SqliteConnection("Data Source=:memory:"); connection.Open(); // 创建对应的数据表 using var createTableCmd = connection.CreateCommand(); createTableCmd.CommandText = "CREATE TABLE Book (ID TEXT, Title TEXT)"; createTableCmd.ExecuteNonQuery(); // 将内存中的Book数据导入到SQLite表 var dataTable = new DataTable(); dataTable.Columns.Add("ID", typeof(string)); dataTable.Columns.Add("Title", typeof(string)); foreach (var book in source) { dataTable.Rows.Add(book.ID, book.Title); } using var bulkCopy = new SqliteBulkCopy(connection); bulkCopy.DestinationTableName = "Book"; bulkCopy.WriteToServer(dataTable); // 执行传入的SQL查询 using var queryCmd = connection.CreateCommand(); queryCmd.CommandText = sql; using var reader = queryCmd.ExecuteReader(); // 读取结果并转换为Book集合 var result = new List<Book>(); while (reader.Read()) { result.Add(new Book { ID = reader.GetString(0), Title = reader.GetString(1) }); } return result; }
这个方法能支持几乎所有SQL语法,但会有一定的性能开销(毕竟要把数据导入内存数据库),适合数据量不大的场景。
额外说明:如果是数据库IQueryable(比如EF Core)
如果你的IQueryable是来自EF Core的DbSet(连接真实数据库),那处理起来更简单——直接用EF Core的FromSqlRaw或FromSqlInterpolated方法执行原生SQL,然后调用AsEnumerable()转成IEnumerable:
// 假设dbContext是你的EF Core上下文 var id = "123"; string queryString = "SELECT * FROM Book WHERE ID = {0}"; var expected = dbContext.Books.FromSqlRaw(queryString, id).AsEnumerable();
内容的提问来源于stack exchange,提问作者superninja
相关产品推荐
相关产品推荐

