C# 动态 Linq/查询

13

更新2 我开始更多地尝试使用反射,得到了一个可以根据字符串变量返回属性值的可用方法。但是我暂时不想将其作为答案发布,因为我感觉这可能不是最佳解决方案。以下是代码:

DataContext dataBase = new DataContext();
List<string> listOfFields = new List<string>;
List<string> listOfUsers = new List<string>


//strFields and strNames are strings generated from listOfFields and listOfUsers
IEnumerable<myUserData> userInfo = dataBase.ExecuteQuery<myUserData>
                       ("select " + strFields + " from myUserData where user_id                          in (" + strNames + ")");    

foreach(var user in userInfo)
{
    foreach(string field in listOfFields)
    {
        string fieldValue = user.GetType().GetProperty(field).GetValue(user,null).ToString
        Console.WriteLine(fieldValue);
    }

}

更新: 我正在取得进展!我想。这可能是一种非常糟糕的解决方案和资源浪费,我还不确定,但它比我之前看到的空白屏幕要好。现在的问题是找到一种方法只返回值而不带有字段名。以下是我的代码:

foreach(string userID in listOfUserIDs)
{
    //dataBase is a Datacontext
    var userInfo = dataBase.myTable
                   .Where("user_id == @0", userID)
                   .Select("New(" + strFields + ")");

    foreach(var user in userInfo)
    {
         Console.WriteLine(user);
    }
}

这将输出{first_name = JOHN,last_name = DOE}等等其他字段。我希望能找到一种修改我的Select子句以仅选择值的方法。


我是一位初学者程序员,对C#和涉及数据库的任何内容都很新鲜。这是我第一个使用查询和数据库的项目,我在成功根据动态查询返回结果方面遇到了问题。

我已经成功创建了一个可以检索正确信息的查询,但我不知道如何访问检索到的特定字段。

IEnumerable<myTable> userInfo = dataBase.ExecuteQuery<myTable>
("select " + Fields + " from myTable where userID in                 
(" + userNames + ")");

编辑: 请忽略我糟糕的命名规范,但myTable的类型为:System.Collections.Generic.IEnumerable<DatabaseConnection.myTable>{System.Data.Linq.SqlClient.SqlProvider.OneTimeEnumerable<DatabaseConnection.myTable>} 这只是一个占位符名称,在我的数据库中有一个特定的名称。它位于Tables文件夹中 XD

myTable是DatabaseConnection类的类型。Fields是我正在查找的字段字符串,而userNames是由逗号分隔的多个用户名的字符串。 例如:Fields =“firstName,lastName,idNumber” userNames =“John,Jane,Bob”

我的问题是,是否有可能遍历当前返回的对象类型中每个用户的每个字段? 我只看到过像这样明确地完成:

    foreach(var x in userinfo)
{
        Console.Writeline(x.firstName);
}
在我目前的情况下,这将不起作用,因为用户可能需要与特定用户名相关的其他字段。 我还尝试使用Dynamic Linq Library,虽然我成功设置了我的选择字符串,但当userID等于几个不同的字符串时,我不确定如何正确设置where子句。(名称数量并不总是相同的)
var dataInfo = dataBase.myTable
                       .Where("userID == @0", "(" + userIDs + ")")
                       .Select("New(" + fields + ")");

我已经卡在这里好几天了。非常感激任何帮助。也许我认为解决方案太简单了?我觉得应该能在我的第一段代码中使用GetProperties()或类似的东西。

如果这个问题已经被回答过并且我的格式不佳(我在这里发的第一篇文章),我很抱歉。在我的研究中,我没有找到我需要的东西,或者可能我只是没有正确理解代码。再次感谢任何帮助!

编辑:所有这些都是为我目前正在开发的Excel插件而做的。我将需要将信息放入列表或某种对象中,以便将每个字段结果放入特定的单元格中。

编辑:如果我使用了错误的术语,请原谅我。我觉得我的错误可能在于我的Enumerable类型,并将其命名为“myTable”可能会令人困惑。它是从我的项目中的.dbml文件中的服务器资源管理器中提取出来的表格。

编辑:对于每个用户运行查询并检索每个请求的字段需要多么低效/耗时?如果这只是需要添加一个简单循环的东西,那我会感到很愚蠢。


2
我会使用Entity Framework而不是ADO.NET来完成这个任务。 - Sachin Kainth
这已经远远超出了初学者的范畴,尽管您似乎已经在做了。 :) 这可能会帮助如何使用in子句构建lionq查询。http://stackoverflow.com/questions/10143999/linq-query-with-dynamic-where-clause - Tony Hopkinson
EF 无法解决你的问题,它只会改变它的定义。 - Tony Hopkinson
哦,糟糕,那是我的笔误。那个userinfo应该是dataBase,而那个注释甚至不应该存在。谢谢你指出来。我会进行编辑和修复! - user1711952
告诉我更多关于那75个字段以及它们在实体类中是如何表示的。有了这些信息,提出解决方案会更容易--而且似乎也是你问题的症结所在,即如何呈现那些信息。 - code4life
显示剩余4条评论
4个回答

1
如果我正确理解了您的问题,并且您想要遍历对象的所有属性(在编译时不知道它们是什么),您可以使用反射,例如:
foreach (var prop in userInfo.GetType().GetProperties())
{
    Console.WriteLine("Property name: " + prop.Name);
    Console.WriteLine("Property value: " + prop.GetValue(userInfo, null));
}

谢谢您的快速回复!不幸的是,当我尝试这个时,没有任何东西被打印出来。但是我的userInfo对象显示它检索到了两个对象,每个对象里都有正确的名字和姓氏。 - user1711952
我假设你的userInfo对象是MyTable的实例,因为你的例子表明了这一点。那么userInfo究竟是什么类型? - James
在我的当前代码中,userInfo 的类型为:System.Collections.Generic.IEnumerable<DatabaseConnection.myTable> {System.Data.Linq.SqlClient.SqlProvider.OneTimeEnumerable<DatabaseConnection.myTable>}。抱歉给出了整个内容,我认为这可能会最有帮助 :x - user1711952

1

看起来您只是在使用LINQ to SQL,那么只需使用LINQ查询数据即可。我假设您已经创建了一个DataContext模型,并且MyTable是您的模型中包含某种用户信息的元素。

using (MyDataContext db = new MyDataContext()) {

    var results = (from m in db.MyTable
                   where m.UserId == userId
                   select m);

    // Loop over all of the rows returned by the previous query and do something with it
    foreach (var m in results) {
        // m represents a single row in the results, do something with it.
        Console.WriteLine(m.UserId);
    }

}

或者,如果您期望查询仅返回一行,则可以使用SingleSingleOrDefault将结果投影到单个对象中,而不是结果集合:

using (MyDataContext db = new MyDataContext()) {

    var singleResult = (from m in db.MyTable
                        where m.UserId == userId
                        select m).SingleOrDefault();

    // Do something with the single result
    if (singleResult != null) {
        Console.WriteLine(singleResult.UserId);
    }

}

编辑:

前面的例子介绍了如何使用LINQ查询兼容数据源的基础知识。大多数LINQ提供程序(例如您正在使用的LINQ to SQL)都支持IQueryable接口。简单来说,这个接口允许您动态构建查询,并将实际执行该查询的操作推迟到最后一刻,直到您实际尝试访问结果时才执行。由LINQ查询返回的对象(例如第一个示例中的变量results)返回一个IQueryable,其中T是您查询的对象类型。它不完全是一个集合,就像您可能想到的List<T>那样,其中元素已经存在或明确添加或删除。在第一个示例中,results实际上没有任何元素,直到foreach循环尝试迭代结果。

为了处理您的过滤方案,让我们继续构建results查询,直到它满足您的需求,然后在准备好时执行它。

首先,假设您的表单有三个字段需要进行过滤:名字、姓氏和用户ID。为了简单起见,这些字段只是文本框,并且我们知道如果相应的文本框中有任何输入,则需要按字段进行过滤。同时,假设不需要进行验证,用户将始终输入正确的数据进行过滤。
using (MyDataContext db = new MyDataContext()) {

    // Build out your base query, which would return everything from MyTable
    var results = (from m in db.MyTable
                   select m);

    // Check each textbox for a value to filter by.  Add the filter to the base
    // query where necessary.    

    if(!string.IsNullOrEmpty(txtUserId.Text)) {
        results = results.Where(x => x.UserId == (int) txtUserId.Text);
    }

    if(!string.IsNullOrEmpty(txtFirstName.Text)) {
        results = results.Where(x => x.FirstName == txtFirstName.Text);
    }

    if(!string.IsNullOrEmpty(txtLastName.Text)) {
        results = results.Where(x => x.LastName == txtLastName.Text);
    }

    // Loop over all of the rows returned by the previous query and do something with it.  
    // THIS is the actual point that the SQL would be generated from the query you've 
    // built and executed against the DB.  The elements returned by `results` 
    // should only meet all of the filters that you the user entered for one or
    // more of the fields.  Not entering any data would have resulted in all rows being
    // returned
    foreach (var m in results) {
        // m represents a single row in the results, do something with it.
        Console.WriteLine(m.UserId);
    }

}

上面的例子非常简单,但它代表了您想要将某些字段AND在一起的典型过滤场景。我相信有更优雅的方法来封装查询过滤器,但核心概念仍然是相同的。只需使用接口并将您的过滤器表达式链接到它上面即可。
这样做只允许您使用AND链接谓词,您无法执行像“Where a user has an ID of 6 OR has a first name of 'John'”这样的操作。您可以使用System.Linq.Expressions命名空间构建LINQ表达式来构建这些更复杂的表达式,但这些相对复杂,在大多数情况下都不是必需的。

我的问题是最终用户在所选内容中将扮演重要角色。基本上,最终用户将输入网络上组的名称,然后返回此组中所有用户的列表,但还有75个不同字段的列表可供最终用户选择查看关于该组中每个用户的信息(例如名字、姓氏、员工号等)。我已尝试使用Linq这种方式,除非在循环中逐个查询每个名称,否则无法成功。但我不确定这是否高效。 - user1711952
这将是一种编程效率非常低下的方式,使用LINQ或其他任何技术都是如此。幸运的是,你所谈论的过滤类型可以比那更优雅地处理。 - mclark1129
我添加了另一个示例,试图解释查询如何以延迟的方式构建和执行。 - mclark1129
谢谢Mike!这很有帮助,也很有教育意义。不幸的是,我感觉可能还是无法使用这种方法,除非我完全没有理解。我会尝试并更新进展情况! - user1711952

0

根据您的第二次更新:

您可能可以将嵌套的foreach简化为一些Linq语句。例如:

        PropertyInfo[] props = typeof(MyUserData).GetProperties();

        var results = from info in userInfo
                      from p in props
                      where listOfFields.Contains(p.Name)
                      select new
                      {
                          FieldName= p.Name,
                          UserId= info.UserId,
                          FieldValue= p.GetValue(info, null)
                      };

        foreach (var item in results)
        {
            Console.WriteLine("(userId = {0}) {1} = {2}", item.UserId, item.FieldName, item.FieldValue);
        }

0

虽然我还没有尝试过DataContext(或.NET 4.5),但在4.0中,您可以使用dynamic类直接访问字段:

IEnumerable<dynamic> userInfo = (from user in users
                                 where user.id equals userId
                                 select new { user.name, user.id /*, ...*/ }).ToList();

foreach (dynamic user in userInfo)
{
    Console.WriteLine(user.name);
    Console.WriteLine(user.id);
    //...
}

dynamic 表示一个在运行时解析的类,因此即使在编码时您看不到 intellisense 显示字段,只要它们是正确的,它就可以工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接