5

I'm using the Mono.CSharp library to emit code. Following another question on SO (http://stackoverflow.com/questions/3407318/mono-compiler-as-a-service-mcs) I managed to get Mono.CSharp evaluating correctly on the Microsoft CLR.

To add flexibility in my app I'd like to be able to customize a query at runtime - by allowing the user to provide a LINQ query as a string that gets parsed and hits the database when executed.

Given this basic snippet of code:

IQueryable<Contact> contacts = GetContacts();
string query = "from contact in contacts
                where contact.Name == \"name\"
                select contact";
var queryableResult = Mono.CSharp.Evaluator.Evaluate(query);

How can I 'inject' the contacts variable into the Mono.CSharp.Evaluator to be evaluated as part of the query? Am I going about this the right way? In the end I either need the resulting Expression or the IQueryable from the 'query' string.

1

2 Answers 2

5

I think you have a few options:

  1. Use static or ThreadStatic variables to exchange data between the caller and you string based code:

    namespace MyNs
    {
      public class MyClass
      {
     [ThreadStatic] // thread static so the data is specific to the calling thread
     public static string MyEnumerableVariable;
    
    
     public void DoSomething() 
     {
          Evaluator.ReferenceAssembly(Assembly.GetExecutingAssembly());
          Evaluator.Run("using MyNs;")
          // run the dynamic code
          var s = @"return (from contact in MyNs.MyClass.MyEnumerableVariable where contact.Name == ""John"" select contact).ToList();";
          Evaluator.Evaluate(s);
     }
    

    } }

  2. Return a delegate from your string code:

    
     public void DoSomething() 
     {

    // run the dynamic code var s = @"return new Func<string, IQueryable<MyNs.Model.Contact>, IList>((s,q) => (from contact in q where contact.Name == s select contact).ToList());"; var func = (Func<string, IQueryable<MyNs.Model.Contact>, IList>)Evaluator.Evaluate(s); var result = func("John", myQueryableOfContactsFromNHibernate);

    }

  3. Go the full blown route.

string query = string.Format(
@"using (var dc = new DataContext()) 
{
  return (from contact in dc.Contacts where contact.Name == ""{0}"" select contact).ToList();
}", "John");

var result = Mono.CSharp.Evaluator.Evaluate(query);

Sign up to request clarification or add additional context in comments.

5 Comments

From a design point that's tightly coupling the code to the DataContext. I'm using NHibernate 3 with Ninject to get a new, clean ISession injected into my MVC controller's constructor for every request. If that's the only route then I can work around it with some static calls, just isn't ideal.
Let me think some more, and I'm sure there are better ways, but my first thought is to make a ThreadStatic variable in your calling code and reference that from your string evaluated code.
Attempting #2, <% var obj = Evaluator.Evaluate("return new Func<int> () => 3;"); %>. I'm receiving this error. If I remove the new, I get "Unexpected symbol '=>'". How do I return a Func from Evaluate into a var?
The solution to my problem turned out to be to drop the return keyword and include the actual function as the parameter to the constructor, so... <% var obj = Evaluator.Evaluate("new Func<int>(() => 3);"); %>. Hopefully that helps someone else.
I found a pretty good way to inject local variables and posted it under this question: stackoverflow.com/questions/11569177/…
2

I didn't try this, but I guess you could use Mono compiler to create a delegate vthat takes IQueryable<Contract> as argument and returns the filtered query. Something like:

IQueryable<Contact> contacts = GetContacts(); 
string query = "new Func<IQueryable<Contact>, IQueryable<Contact>>(contracts =>
                  from contact in contacts 
                  where contact.Name == \"name\" 
                  select contact)"; 
var res = Mono.CSharp.Evaluator.Evaluate(query); 

Then you just need to cast res to an appropriate Func<,> type and invoke it to get the result.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.