是否可以在运行时动态评估包含有效LINQ的字符串?

时间:2009-01-27 14:47:31

标签: .net linq c#-3.0

基本思想是获取一个字符串并根据XML文件(或任何LINQed提供者)对其进行评估

我发现了LINQ Dynamic Query Library。只是粗略地浏览一下下载包中的单个文档。这似乎是添加扩展方法来参数化LINQ查询的各个部分。有谁知道这是否有动态评估?

有什么方法可以做(假设有一些方法可以使用XML文件中的数据为种子播种)?

ClassFromWishfulThinking.Evaluate(sLinqQuery);

3 个答案:

答案 0 :(得分:3)

一定是可能的,因为Linqpad做到了!

how it works page有点心灵弯曲,我不确定这是你想要用生产代码做什么,除非你真的无法避免它。

没有框架方法只能做你想要的东西,我可以说因为.net的工作原理而具有相当高的可信度。你必须像LinqPad一样使用csharpcodeprovider来调用编译器。

您可以更改Linq动态查询库以执行所需操作。

答案 1 :(得分:2)

我在VB中使用此代码来评估运行时字符串中包含的代码...您需要将其转换为C#,但没有理由您不能只传入包含LINQ表达式的字符串。您需要稍微修改FuncString以允许使用LINQ,因为我只引用System.Math。我在运行时使用它来评估(主要是数学)表达式,但我想它可以被修改/扩展以评估.NET框架中的任何东西......

Function Evaluate(ByVal Expression As String, ByVal Args() As Object) As Object

    If Expression.Length > 0 Then

        'Replace each parameter in the calculation expression with the correct values
        Dim MatchStr = "{(\d+)}"
        Dim oMatches = Regex.Matches(Expression, MatchStr)
        If oMatches.Count > 0 Then
            Dim DistinctCount = (From m In oMatches _
                                 Select m.Value).Distinct.Count
            If DistinctCount = Args.Length Then
                For i = 0 To Args.Length - 1
                    Expression = Expression.Replace("{" & i & "}", Args(i))
                Next
            Else
                Throw New ArgumentException("Invalid number of parameters passed")
            End If
        End If

        Dim FuncName As String = "Eval" & Guid.NewGuid.ToString("N")
        Dim FuncString As String = "Imports System.Math" & vbCrLf & _
                                   "Namespace EvaluatorLibrary" & vbCrLf & _
                                   "  Class Evaluators" & vbCrLf & _
                                   "    Public Shared Function " & FuncName & "() As Double" & vbCrLf & _
                                   "      " & Expression & vbCrLf & _
                                   "    End Function" & vbCrLf & _
                                   "  End Class" & vbCrLf & _
                                   "End Namespace"

        'Tell the compiler what language was used
        Dim CodeProvider As CodeDomProvider = CodeDomProvider.CreateProvider("VB")

        'Set up our compiler options...
        Dim CompilerOptions As New CompilerParameters()
        With CompilerOptions
            .ReferencedAssemblies.Add("System.dll")
            .GenerateInMemory = True
            .TreatWarningsAsErrors = True
        End With

        'Compile the code that is to be evaluated
        Dim Results As CompilerResults = _
            CodeProvider.CompileAssemblyFromSource(CompilerOptions, FuncString)

        'Check there were no errors...
        If Results.Errors.Count > 0 Then
        Else
            'Run the code and return the value...
            Dim dynamicType As Type = Results.CompiledAssembly.GetType("EvaluatorLibrary.Evaluators")
            Dim methodInfo As MethodInfo = dynamicType.GetMethod(FuncName)
            Return methodInfo.Invoke(Nothing, Nothing)
        End If

    Else
        Return 0

    End If

    Return 0

End Function

Sub Main()
    'In a real application, this string would be loaded from a database
    'it would be stored by some calculation administrator...
    Dim Expr As String = "  If ({0} < 20000) Then" & vbCrLf & _
                         "    Return Max(15, Min(75,0.12*{0}))" & vbCrLf & _
                         "  Else" & vbCrLf & _
                         "    Return Max(75,0.05*{0})" & vbCrLf & _
                         "  End If"

    Dim Args As New List(Of String)
    While True
        'This value would be loaded from some data interpreter for inclusion
        'in the calculation...
        Dim Val As String = Console.ReadLine
        Args.Clear()
        If IsNumeric(Val) Then
            Args.Add(Val)
            Dim dblOut As Object = Evaluate(Expr, Args.ToArray)
            Console.WriteLine(dblOut)
        Else
            Exit While
        End If
    End While

End Sub

没有理由你不能稍微修改函数定义,以便它可以作为字符串的扩展,以允许你像这样调用它:

Dim LinqString = "from c in myLinqData where c.SomeField.Equals(""somevalue"") select c"
Dim output = LinqString.Evaluate

答案 2 :(得分:0)

无论如何,大部分LinqToXml都是由字符串驱动的。例如,Elements extension method将获取一个字符串并查找具有该名称的所有子元素。

如果您的查询以字符串形式开始(而不是作为Queryable / Enumerable上的方法调用),那么它实际上不是一个linq查询 - 而是其他内容。也许XPath可以提供帮助。