tags:

views:

675

answers:

3

The basic idea is to take a string and evaluate it against an XML File (or any LINQed provider)

I found this LINQ Dynamic Query Library. Just had a cursory glance at the documentation which is a single page in the download bundle. This seems to be adding extension methods to parameterize parts of the LINQ Query. Does anyone know if this does dynamic evaluation?

Is there any way to just do (assuming there is some way to seed the class with the Data from the XML File) ?

ClassFromWishfulThinking.Evaluate(sLinqQuery);
A: 

Much of LinqToXml is driven by strings anyway. For example, the Elements extension method will take a string and find all the child elements with that name.

If your query starts as a string (and not as method calls on Queryable/Enumerable), then it isn't really a linq query - but something else. Maybe XPath can help.

David B
+3  A: 

It must be possible because Linqpad does it!

Looking at the how it works page is a bit mind bending, and I'm not sure this is something you would want to do with production code unless you really couldn't avoid it.

There is no framework method that just does what you want, I can say that with a fairly high degree of confidence because of how .net works. You have to call the compiler using the csharpcodeprovider as LinqPad does.

You might be able to change the Linq Dynamic Query Library to do what you need.

Christopher Edwards
It's not production code.. rather trying to do a simple version of Linqpad.. but against XML instead of SQL.
Gishu
A: 

I've got this code in VB to evaluate code contained in a string at runtime... you'll need to translate it to C#, but there's no reason you couldn't just pass in a string containing a LINQ expression. You'd need to modify FuncString slightly to allow for the use of LINQ as I only reference System.Math. I use it for evaluation of (mostly mathematical) expressions at runtime, but I imagine it could be modified/expanded to evaluate pretty much anything in the .NET framework...

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

There's no reason you couldn't modify the function definition slightly so that it could function as an extension of string to allow you to call it like so:

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