1

I've created a LINQ expression that projects a sequence into new sequence of anonymous types as I've found here LINQ expression tree with anonymous types.

Here is my expression I'would like to execute:

Document doc = ....;
doc.AllStatements.Select(s => new { field = s.Amount });

This is runtime representation of selector inside brackets:

{t => new field;Decimal;() {field = t.Amount}}

And this is runtime representation of whole expression.

{System.Linq.Enumerable+<UnionIterator>d__88`1[SISTEM.Models.Statement].Select(t => new field;Decimal;() {field = t.Amount})}

When I try to enumerate it during Debug I get just this:

") expected"

If I try with multiple field anonymous type like this:

doc.AllStatements.Select(s => new { field = s.Amount, field2 = s.Account });

I get this:

{System.Linq.Enumerable+<UnionIterator>d__88`1[SISTEM.Models.Statement].Select(t => new field;Decimal;field1;Nullable`1;() {field = t.Amount, field1 = t.Account})}

and then runtime error is:

"Unexpected character '`'"

Can somebody help me decode this?

UPDATE:

This is my actual call:

var res = Expressions.DoExpression(typeof(Document), doc, "AllStatements.Select(new field=Amount, field1=Account)");

But, there is a lot of parsing and LINQ expressions behind DoExpressions function (500 lines of code or so)

UPDATE 2:

First of all here is snippet of code:

Dokument doc = db.Dokumenti.First(); // Proper document, entity object;
var res = Expressions.DoExpression(
    typeof(Dokument), 
    doc, 
    "SviFinStavovi.Select(new id=Iznos, dsc=Opis)"
);

SviFinStavovi is Navigational Property of Dokument, and Iznos and Opis are properties of underlying type of SviFinStavovi.

These 2 lines of code, though, don't throw exception. Exception will be thrown only when I try to enumerate res. You have those above.

If I had put SviFinStavovi.Select(Iznos), that would have worked OK.

This is DoExpression:

public static object DoExpression(Type t, object obj, string expression){
ParameterExpression pe = Expression.Parameter(obj.GetType(), "objekat");
Expression SelectExpr = Expressions.ResolveCompleteExpression(pe, expression.Prepare());
return Expression.Lambda(SelectExpr, pe).Compile().DynamicInvoke(obj);}

ResolveCompleteExpression parses the whole thing.

Now, this is function that I got from this site, link above, which I added, and which causes trouble:

    public static Expression SelectDynamic(Expression expr, IEnumerable<string> fieldNames)
    {
        Type source = expr.Type.GetGenericArguments()[0];
        Dictionary<string, PropertyInfo> sourceProperties = new Dictionary<string, PropertyInfo>();
        foreach (string arg in fieldNames) sourceProperties.Add(arg.Split('=')[0].Trim(), source.GetProperty(arg.Split('=')[1].Trim()));                       
        Type dynamicType = LinqRuntimeTypeBuilder.GetDynamicTypeWrapper(sourceProperties);

        ParameterExpression sourceItem = Expression.Parameter(source, "t");
        IEnumerable<MemberBinding> bindings = dynamicType.GetFields().Select(p => Expression.Bind(p, Expression.Property(sourceItem, sourceProperties[p.Name]))).OfType<MemberBinding>();
        Expression selector = Expression.Lambda(Expression.MemberInit(
            Expression.New(dynamicType.GetConstructor(Type.EmptyTypes)), bindings), sourceItem);
        return Expression.Call(typeof(Queryable), "Select", new Type[] { source, dynamicType }, expr, selector);
    }

At this point expr would represent (correctly) doc.SviFinStavovi, and field names would be ["id=Iznos"] ["dsc=Opis"].

Now, this is not my code, i just adjusted it to suit me little bit. This is effectively last executed line of code of the snippet above. Only to unwind stack and do the compile.

Sorry if this maybe doesn't make any sense. If you need any clarifications, please ask.

Community
  • 1
  • 1
Milos Mijatovic
  • 955
  • 1
  • 17
  • 34
  • Where exactly do you get these errors? In the "Watch" windows of VS? – Spontifixus Sep 17 '12 at 15:15
  • yeah, my result is of var type. Then I put a breakpoint after that, and then inspect it (VS says 'Expanding the Results View will enumerate the IEnumerable'). Then I get those errors – Milos Mijatovic Sep 17 '12 at 15:18
  • The strings inside Watch are not C# code, so you can't execute it that way. – svick Sep 17 '12 at 15:21
  • I maybe didn't say it all. This is my call: var res = Expressions.DoExpression(typeof(Document), doc, "AllStatements.Select(new field1=Amount, field2=Account)"); But, there is like 500 lines of code behind this (parsing and creating LINQ expresions). Res is final result of everything, and i get those errors mentioned above – Milos Mijatovic Sep 17 '12 at 15:22
  • 1
    Try setting break on all exceptions. Unfortunately, I suspect your error is somewhere in those 500 lines. – Jim Wooley Sep 17 '12 at 15:37
  • Well, maybe... Though, code works perfectly for projecting single properties inside Select like "AllStatements.Select(Amount)" (s => s.Amount). I parse this into LINQ Expression, compile it, and get IEnumerable of some type. Then I can further Sum() or Count() it, or whatever. But when I introduce anonymous projections, i get into trouble. Parsing code into LINQ expressions doesn't throw any exceptions, i only get runtime exceptions. – Milos Mijatovic Sep 17 '12 at 15:49
  • I parse "new field=Amount, field1=Account" which actually means (s => new { s.Amount, s.Account })) into runtime: "t => new field;Decimal;field1;Nullable`1;() {field = t.Amount, field1 = t.Account}". Would you say that it is ok? – Milos Mijatovic Sep 17 '12 at 15:50
  • @MilosMijatovic: I suspect it's going to be impossible to help you without more information about your parsing. Clearly *something* is going wrong in the expression you're creating. I suggest you come up with a short but *complete* program demonstrating the problem - remove the parsing part, and just have a program which takes the same actions as your parsing code in a particular failure case. It would help if you could paste the full exception, too... it *sounds* like the problem is within your parsing... – Jon Skeet Sep 17 '12 at 16:13
  • It's also unclear what you mean by "into runtime" - giving string representations of expression trees isn't terribly helpful, to be honest. – Jon Skeet Sep 17 '12 at 16:14
  • Sure, I was afraid I'll be too broad. Give me 15 mins. Thanks for taking interest. – Milos Mijatovic Sep 17 '12 at 16:18

2 Answers2

0

I think the select needs parenthesis around the fields after new.

 Select("new(<property1>,<property2>,...)");


var res = Expressions.DoExpression(typeof(Document), doc, "AllStatements.Select(new (field=Amount, field1=Account))");
jrob
  • 532
  • 1
  • 6
  • 16
0

Well I couldn't make anonymous projection, but I crated small class and project into that:

public class LookupModel
{
    public LookupModel(int id, string dsc)
    {
        ID = id;
        DSC = dsc;
    }

    public int ID { get; set; }
    public string DSC { get; set; }
}

So using this:

function LINQSelectNew(Expression expr, string[] args)
{
Type type = expr.Type.GetGenericArguments()[0];
ParameterExpression parameter = Expression.Parameter(type, type.Name);

List<Expression> lista = new List<Expression>();                    
foreach (string s in args) lista.Add(Expressions.ResolveCompleteExpression(parameter, s.Prepare()));

Expression New = Expression.New(typeof(LookupModel).GetConstructor(lista.Select(e => e.Type).ToArray()), lista.ToArray());

return Expression.Call(
    typeof(Queryable),
    "Select",
    new Type[] { type, New.Type },
    expr,
    Expression.Lambda(New, new ParameterExpression[] { parameter }));
}

I can do this:

var res = Expressions.DoExpression(
    typeof(Dokument), 
    doc, 
    "SviFinStavovi.Select(new LookupModel(amount, amount.ToString()))"
);

Hope this helps somebody...

Milos Mijatovic
  • 955
  • 1
  • 17
  • 34