动态查询Linq到xml VB.NET

时间:2011-03-16 08:13:27

标签: vb.net linq-to-xml

嘿 我想写一个查询,查询中的“where”是一个类似“

的字符串

Dim query as string =“Name = xxxx and Date> 10”

Dim t =来自doc.Descendants(“books”)中的书籍选择_         [名称] = book..value,[Date] = book..value ....         Where(查询)

我在运行时构建查询字符串

...谢谢

2 个答案:

答案 0 :(得分:0)

我不是说这是你的情况但是我从ASP经典的人那里看到了很多,我们曾经一直在构建动态SQL字符串。我们倾向于希望LINQ能够在部分代码中为我们提供更多功能,但让我们在其他地方使用字符串。不幸的是,事实并非如此。 Where采用布尔参数,没有办法解决这个问题。您可以编写自己的使用反射的解析器并最终返回一个布尔值,但是您将编写许多可能容易出错的代码。以下是你应该如何做到的:

假设这是我们的数据类:

Public Class TestObject
    Public Property Name As String
    Public Property Job As String
End Class

这是我们的测试数据:

    Dim Objects As New List(Of TestObject)
    Objects.Add(New TestObject() With {.Name = "A", .Job = "Baker"})
    Objects.Add(New TestObject() With {.Name = "B", .Job = "President"})
    Objects.Add(New TestObject() With {.Name = "C", .Job = "Bus Driver"})
    Objects.Add(New TestObject() With {.Name = "D", .Job = "Trainer"})

您要做的是创建一个表示要搜索的数据的变量:

    ''//This variable simulates our choice. Normally we would be parsing the querystring, form data, XML values, etc
    Dim RandNum = New Random().Next(0, 3)
    Dim LookForName As String = Nothing
    Select Case RandNum
        Case 0 : LookForName = "A"
        Case 1 : LookForName = "B"
        Case 2 : LookForName = "C"
    End Select

    ''//Query based on our name
    Dim Subset = (From O In Objects Select O Where (O.Name = LookForName)).ToList()

如果有时你需要在Job上搜索,有时候你不需要搜索,你可能只需要编写几个查询:

    Dim Subset As List(Of TestObject)
    Select Case RandNum
        Case 0
            Subset = (From O In Objects Select O Where (O.Name = "A" And O.Job = "Baker")).ToList()
        Case Else
            Select Case RandNum
                Case 1 : LookForName = "B"
                Case 2 : LookForName = "C"
            End Select
            Subset = (From O In Objects Select O Where (O.Name = LookForName)).ToList()
    End Select

只是为了解释编写自己的查询解析器(这是我建议你不要下去的路径),这是一个非常非常非常粗略的开始。它只支持=,只支持字符串,并且可以在多个点处中断。

Public Shared Function QueryParser(ByVal obj As Object, ByVal ParamArray queries() As String) As Boolean
    ''//Sanity check
    If obj Is Nothing Then Throw New ArgumentNullException("obj")
    If (queries Is Nothing) OrElse (queries.Count = 0) Then Throw New ArgumentNullException("queries")

    ''//Array of property/value
    Dim NameValue() As String
    ''//Loop through each query
    For Each Q In queries
        ''//Remove whitespace around equals sign
        Q = System.Text.RegularExpressions.Regex.Replace(Q, "\s+=\s+", "=")
        ''//Break the query into two parts.
        ''//NOTE: this only supports the equal sign right now
        NameValue = Q.Split("="c)
        ''//NOTE: if either part of the query also contains an equal sign then this exception will be thrown
        If NameValue.Length <> 2 Then Throw New ArgumentException("Queries must be in the format X=Y")

        ''//Grab the property by name
        Dim P = obj.GetType().GetProperty(NameValue(0))
        ''//Make sure it exists
        If P Is Nothing Then Throw New ApplicationException(String.Format("Cannot find property {0}", NameValue(0)))
        ''//We only support strings right now
        If Not P.PropertyType Is GetType(String) Then Throw New ApplicationException("Only string property types are support")

        ''//Get the value of the property for the supplied object
        Dim V = P.GetValue(obj, Nothing)
        ''//Assumming null never equals null return false for a null value
        If V Is Nothing Then Return False
        ''//Compare the two strings, return false if something doesn't match.
        ''//You could use String.Compare here, too, but this will use the current Option Compare rules
        If V.ToString() <> NameValue(1) Then Return False
    Next

    ''//The above didn't fail so return true
    Return True
End Function

此代码允许您编写:

Dim Subset = (From O In Objects Select O Where (QueryParser(O, "Name = A", "Job = Baker"))).ToList()

答案 1 :(得分:0)

不,没有什么直接就像你在寻找传递字符串的地方一样。正如他们所说,当你拥有的只是一把锤子时,一切看起来都像钉子......真正的问题是你需要了解LINQ擅长什么并将它应用到你的代码中(如果它很合适),而不是而不是尝试使用动态构建的SQL查询字符串来做它所能做的事情。

你应该做的是让那些“Where”子句强烈打字。您当前的代码很有可能爆炸并且难以调试。

你可以做的是这样的事情(对不起,使用C#,自从我触及VB.NET以来已经有一段时间了):

var query = from book in doc.Descendants("books")
            select book;

if(needsNameComparison)
{
    query = query.where(book.Name == nameToCompare);
}

if(needsDateComparison)
{
    query = query.Where(book.Date > 10);
}

List<book> bookList = query.ToList();

使用LINQ,“查询”实际上不会在“ToList()”调用之前运行。由于它使用延迟执行,因此查询是动态的,因为它正在构建,直到它实际需要运行。这类似于您之前要使用的代码,因为您提前构建了一个查询字符串,然后在特定点执行它。