代码之家  ›  专栏  ›  技术社区  ›  Gishu

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

  •  6
  • Gishu  · 技术社区  · 15 年前

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

    我发现了这个 LINQ Dynamic Query Library .只是粗略地浏览了一下下载包中的一页文档。这似乎是在向LINQ查询的参数化部分添加扩展方法。有人知道这是否能进行动态评估吗?

    是否有任何方法可以这样做(假设有某种方法可以用XML文件中的数据为类种子)?

    ClassFromWishfulThinking.Evaluate(sLinqQuery);
    
    3 回复  |  直到 15 年前
        1
  •  3
  •   Christopher Edwards    15 年前

    一定有可能,因为 Linqpad 干吧!

    看着 how it works page 这有点让人心烦意乱,我不确定这是否是您想要用生产代码做的事情,除非您真的无法避免它。

    没有一种框架方法可以满足您的需求,我可以说,由于.NET的工作方式,它具有相当高的信心。您必须使用 csharpcodeprovider 就像LinqPad一样。

    您可能能够更改LINQ动态查询库以执行所需的操作。

        2
  •  2
  •   BenAlabaster    15 年前

    我用VB编写了这段代码来计算运行时字符串中包含的代码…您需要将其转换为C,但没有理由不能只传入包含LINQ表达式的字符串。您需要稍微修改functring,以允许使用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
    
        3
  •  0
  •   Amy B    15 年前

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

    如果您的查询以字符串开始(而不是以可查询/可枚举的方法调用开始),那么它实际上不是一个LINQ查询,而是其他查询。也许吧 XPath 可以帮忙。