C#列表上的动态SQL查询(基于字符串)

时间:2012-08-25 22:15:55

标签: c# sql

我试图找到一种方法,使用字符串sql查询将C#列表作为数据库进行查询,例如:

List<Customer> customers = new List<Customer>(){
    new Customer(){Name = "joe", Age = 20},
    new Customer(){Name = "john", Age = 25}
};
string query = @"Select * from customers";
List<Tuple> tuples = EntityDB.query(customers, query);
Console.Writeline(tuples[0][0]); //prints "joe"

我使用此功能需要满足几个要求,

  • 它需要支持动态SQL查询(字符串查询),因为我需要从此列表中提取特定行,并且未预定义过滤。 (linq在这里没有帮助)
  • 它需要能够像SQL一样支持多行(如Average,Count ...)的功能。
  • 它需要支持提取与SQL完全相同的指定列(选择column1,column2 ...)

我没有SQL服务器,我没有额外的mdf DB,只是这个客户对象列表,有没有办法做到这一点?

我的动机是能够从这个列表中提取特定信息到2D表并将其输出到excel,但行和列过滤不是预定义的,需要多行操作,用户将能够使用SQL查询指定要提取的确切信息。

6 个答案:

答案 0 :(得分:4)

LINQ允许您对各种数据源执行类似查询的命令 - 数据库,XML文件,应用程序对象。您可以非常轻松地使用LINQ从列表中选择项目:

items.Select(c => c.Customer)

在内存对象中执行此操作时,您正在使用LINQ-to-Objects。

我更喜欢编写SQL-to-objects框架。这听起来像是一场真正的维护噩梦。

答案 1 :(得分:1)

您是否考虑过以与构建字符串相同的方式构建Linq?

这样的东西
var customers = new List<Customer> { … };
IQueryable<Customer> results = customers.AsQueryable();


if(!string.IsNullOrEmpty(CustomerName))
    results = results.Where(x => x.Name = CustomerName);

if(CustomerMaxAge != 0)
    results = results.Where(x => x.Age <= CustomerMaxAge);

//etc....

return results.ToList();

使用IQueryable,您可以推迟内存中的内容,直到您完成添加条件。

答案 2 :(得分:1)

您是否考虑使用轻量级数据库来解决问题,而不是尝试在代码中模拟数据库?想到像SQLite这样的东西。通常它会将文件写入磁盘,但实际上也支持在内存中加载。

根据文档,内存中的SQLite数据库不需要将文件写入磁盘,并且在打开连接时存在于内存中。一旦连接关闭,它就不复存在了。

http://www.sqlite.org/inmemorydb.html

打开连接后,您可以像使用常规SQL字符串的任何其他数据库一样查询数据,或者如果您愿意,可以使用LINQ和NHibernate或其他一些支持的ORM查询数据。

SQLite特别适用于需要SQL强大但没有设置完整数据库服务器的开销的时候。事实上,如果您使用Firefox作为浏览器,那么您现在正在使用SQLite;)

答案 3 :(得分:0)

也许这就是你想要的?

 List<Tuple<string,int>> tuples = customers.Select(x => Tuple.Create(x.Name,x.Age)).ToList();

答案 4 :(得分:0)

  

P.S。:请参阅my other answer,它更好,可能更接近您所寻找的内容。

为了完整起见,这里是一个非常动态解决方案 - 尽管我发布这样糟糕的代码几乎感到羞耻:

// using System.Reflection;

// converts an object's public properties into an `object` sequence: 
static IEnumerable<object> Terribelize<T>(this T obj)
{
    foreach (PropertyInfo property in obj.GetType().GetProperties())
    {
        yield return property.GetValue(obj, null); // works if readable & non-indexed
    }
}

// does the above to all objects in a sequence: 
static IEnumerable<IEnumerable<object>> TerribelizeAll<T>(this IEnumerable<T> objs)
{
    foreach (T obj in objs)
    {
        yield return obj.Terribelize();
    }
}

然后你可以这样做:

var customers = new List<Customer> { … };
var abominations = customers.TerribelizeAll();
Console.Writeline(abominations.First().First());

为什么你会希望代码 无类型和非结构化超出我的范围。

答案 5 :(得分:0)

似乎基于ADO.NET DataTables的解决方案可能正是您所寻找的。 DataTableDataSet基本上用于表示内存中的关系数据库对象。你可以,例如使用本答案末尾定义的扩展方法执行以下操作:

List<Customers> customers = …;
DataTable table = customers.CopyToDataTable(); // see implementation below

// non-LINQ select:
DataRow[] preferredCustomers = table.Select("Age > 15 AND Age < 50");

// LINQ select (requires project reference to System.Data.DatasetExtensions.dll):
IEnumerable<int> agesOfJCustomers = from row in table.AsEnumerable()
                                    where row.Field<string>("Name").StartsWith("J")
                                    select row.Field<int>("Age");

以下是将任何对象集合转换为数据表的方法:

// using System.Reflection;
// using System.Data;

public static DataTable CopyToDataTable<T>(this IEnumerable<T> objs)
{
    var table = new DataTable();
    // create the column definitions, based on the public properties of type T: 
    foreach (var property in typeof(T).GetProperties())
    {
        table.Columns.Add(property.Name, property.PropertyType); 
    }
    // add one row per object in the given object collection: 
    foreach (T obj in objs) 
    {
        var row = table.NewRow();
        foreach (var column in table.Columns.Cast<DataColumn>())
        {
            var property = typeof(T).GetProperty(column.ColumnName);
            var propertyValue = property.GetValue(obj, null);
            row[column] = propertyValue;
        }
        table.Rows.Add(row);
    }
    return table;
}

此代码当然可以改进:查看MSDN文章"How to: Implement CopyToDataTable Where the Generic Type T Is Not a DataRow"

  

另请参阅: Programming Guide (LINQ to DataSet)