I'm using CompilerContainer on static method in static class and have StackOverflowException.

Have this static class with 1 static method:

public static class StringUtility
{
    public static bool StringEqual(string str1, string str2)
    {
        return (string.Compare(str1, str2, true) == 0);
    }
}

and defined CompilerContainer(using linq expressions):

[CompilerContainer(typeof(Expression))]
public static class LinqExtensions
{
    [Compiler(typeof(StringUtility), "StringEqual", TargetKind.Method | TargetKind.Static)]
    public static Expression StringEqual(Expression string1Expression, Expression string2Expression)
    {
        // sourceExpression -> Identity
        Expression<Func<string, string, bool>> expression = (string1, string2) => StringUtility.StringEqual(string1, string2);
        return expression.BindParameters(string1Expression, string2Expression);
    }
}

And when i use this in linq query like this:

Query.All<Country>().Where(country => StringUtility.StringEqual(country.Code2, "uk"))

Then it will stops on never-ending recursion on calling LinqExtensions.StringEqual method and throws StackOverflowException. I know that this never-ending recursion is caused by

=> StringUtility.StringEqual(string1, string2) defined in expression, but i dont know how to solve such problem.

Can you fix this CompilerContainer to work out?


Updated at 02.03.2010 8:16:36

Your sample will work for me, but i want to explain how to use CompilerContainer more generally.

What i have is static class called StringUtility which has more than this one StringEqual method, more complex, like this:

public static string RemoveDiacritics(string text)
{
  // complex code here
  ...
}

and i want to use this method (and other complex methods defined in StringUtility) in linq queries like:

var noteWithoutDiacritics = Query.All<PlayerNote>().Where(...).Select(note => StringUtility.RemoveDiacritics(note.Description));

I have about 10 methods (can be more in future) in class StringUtility and i want to define CompilerContainer methods to use those methods from StringUtility, but i dont want to rewrite each StringUtility method body into LinqExtensions (CompilerContainer) class mapped methods. This is what i want. Is this possible or not?


Updated at 02.03.2010 19:33:26

If you don't want to maintain two versions of the same code, there is a good option: [CompilerContainer(typeof(Expression))] pubic static class MathUtils { private static Expression<func<...>> addExpression = (a,b) => a + b; // The only instance of this code private static Func<...> addDelegate = addExpression.Compile(); public static bool Add(int a, int b) { return addDelegate.Invoke(a,b); } [Compiler(typeof(MathUtils), "Add", TargetKind.Method | TargetKind.Static)] public static Expression AddCompiler(Expression a, Expression b) { return addExpression.BindParameters(a, b); } }

Thanks for hint, i will try it, this will look good for what i want.

This thread was imported from our support forum. The original discussion may contain more detailed answer.

asked Mar 02 '10 at 07:37

Peter%20%C5%A0ulek's gravatar image

Peter Šulek
492313236


One Answer:

There is nothing to fix: you're compiling call to StringUtility.StringEqual to StringUtility.Equals, so a new expression is the same as previous. Such a case must lead to either infnite recursion or infinite loop in compiler dependently on its design. In our case this leads to recursion.


The idea of such compilers is to replace the expression our compiler doesn't know to the one it knows. If this won't happen, it tries to apply the same rule (= your compilers) to a new expression until it will be able to translate the rest by its own.

So LINQ compiler allow you to use simpler expressions instead of more complex ones in your program.

There are also SQL compilers - they act nearly the same, but on SQL DOM level (AFAIK, there is an example in manual as well).


This must solve the problem:

[CompilerContainer(typeof(Expression))]
public static class LinqExtensions
{
    [Compiler(typeof(StringUtility), "StringEqual", TargetKind.Method | TargetKind.Static)]
    public static Expression StringEqual(Expression string1Expression, Expression string2Expression)
    {
        // sourceExpression -> Identity
        Expression<Func<string, string, bool>> expression = (string1, string2) => string1.ToLower()==string2.ToLower()
        return expression.BindParameters(string1Expression, string2Expression);
    }
}

> This is what i want. Is this possible or not?

No, this is not possible: we need an expression to compile, but method body doesn't provide it. In fact, it's just IL. We could try to de-compile it, of course, but this is much more complex case (AFAIK, no one does this, this is too exotic).

On another hand, note that probably you don't need method body at all: if the method is used just in LINQ queries, you can simply throw NotSupportedException("This method can be used only in LINQ queries!") from it, and, implying there is a rewriter for it, everything will work smoothly.

So the issue is more related to platform we run code on, but not to DO itself. E.g. if there is an infrastructure allowing to get any method body in form of expression, we'd definitely use it. But so far this doesn't work on .NET (and likely, won't - in observable future).


If you don't want to maintain two versions of the same code, there is a good option:

[CompilerContainer(typeof(Expression))]
pubic static class MathUtils
{
  private static Expression<Func<...>> addExpression = (a,b) => a + b; // The only instance of this code
  private static Func<...> addDelegate = addExpression.Compile();

  public static bool Add(int a, int b)
  {
    return addDelegate.Invoke(a,b);
  }

  [Compiler(typeof(MathUtils), "Add", TargetKind.Method | TargetKind.Static)]
  public static Expression AddCompiler(Expression a, Expression b)
  {
    return addExpression.BindParameters(a, b);
  }
}

answered Mar 02 '10 at 07:48

Alex%20Yakunin's gravatar image

Alex Yakunin
29714412

Ah, ok - it's clear, you'd like to perform a comparison ignoring the case?

(Mar 02 '10 at 07:48) Alex Yakunin Alex%20Yakunin's gravatar image

Ok, thanks for explanation, must find another way to do that.

(Mar 02 '10 at 07:48) Peter Šulek Peter%20%C5%A0ulek's gravatar image

You're welcome :)

(Mar 02 '10 at 07:48) Alex Yakunin Alex%20Yakunin's gravatar image
Your answer
Please start posting your answer anonymously - your answer will be saved within the current session and published after you log in or create a new account. Please try to give a substantial answer, for discussions, please use comments and please do remember to vote (after you log in)!
toggle preview

Subscription:

Once you sign in you will be able to subscribe for any updates here

Tags:

×573

Asked: Mar 02 '10 at 07:37

Seen: 4,044 times

Last updated: Mar 02 '10 at 07:37

powered by OSQA