DynamicExpresso icon indicating copy to clipboard operation
DynamicExpresso copied to clipboard

Anonymous object creation

Open holden888 opened this issue 6 years ago • 5 comments

how to get from:

string strExpression = "x => new { x.Ido, x.OtdName }";

such expression:

Expression<Func<T, object>> result = x => new {x.Ido, x.OtdName };

I need this to select specific fields in Generic Repository:

public async Task<List<object>> GetList1(Expression<Func<T, object>> selectFields)
        {
            IQueryable<T> query = _dbSet;
            IQueryable<object> result;
         
            result = query.Select(selectFields);

            return await result.AsNoTracking().ToListAsync();
        }

holden888 avatar Mar 11 '18 05:03 holden888

For the same field works like this:

string strExpression = "x.Ido";
var interpreter = new Interpreter();
Expression<Func<T, object>> expression = interpreter.ParseAsExpression<Func<T, object>>(whereExpression, "x");

How to do this for multiple fields?:

string strExpression = "x.Ido, x.OtdName";

For:

public async Task<List<object>> GetList1(Expression<Func<T, object>> selectFields)
        {
            IQueryable<T> query = _dbSet;
            IQueryable<object> result;
         
            result = query.Select(selectFields);

            return await result.AsNoTracking().ToListAsync();
        }

holden888 avatar Mar 11 '18 07:03 holden888

You are right, unfortunately for now anonymous object are not supported.

I will think about this for a future release...any help is appreciated 😃

davideicardi avatar Mar 13 '18 18:03 davideicardi

I had a look, and it seems quite difficult: the anonymous type has to be defined at runtime, and to do that, you need to add a custom dynamic assembly to the current app domain, and then add the new anonymous type to that custom assembly. Also, that custom assembly must be unloaded when it's no longer needed, to avoid memory bloat. Also, the anonymous type must define fields, properties, a default constructor, and possibly a ToString() method, otherwise it might not be understood by Entity Framework.

Creating a dynamic assembly doesn't seem possible with netstandard 2.0, only with netstandard 2.1.

I found a project that does most of it, except that it doesn't unload the dynamic assembly, and it's also not compatible with netstandard.

https://github.com/dotlattice/LatticeUtils/blob/master/LatticeUtils/AnonymousTypeUtils.cs

metoule avatar Jun 08 '21 09:06 metoule

Thank you @metoule for the interesting analysis! I agree with your conclusion. For now let's keep the issue open.

davideicardi avatar Jun 11 '21 13:06 davideicardi

Another possibility, depending on the outcome desired for this, is to return an ExpandoObject. This would allow consuming code to be able to reference it as dynamic, or be passed to other interpreters since there is already some support for

I do have a decent amount of experience in building classes at run time, especially for the simple classes that are created as anonymous types. Looking into how those are actually compiled out

var o = new { Prop1 = int.MaxValue, Prop2 = string.Empty };

ends up getting compiled out to a class as follows

public class <>f__AnonymousType0`2<Prop1, Prop2>
{
   private readonly Prop1<Prop1>i__Field;
   private readonly Prop2 <Prop2>i__Field;

    public <>f__AnonymousType0`2(Prop1 Prop1, Prop2 Prop2)
    {
        <Prop1>i__Field = Prop1;
        <Prop2>i__Field = Prop2;
    }
    
    public int Prop1 { get => <Prop1>i__Field; }
    public string Prop2 { get => <Prop2>i__Field; }

    public override string ToString()
    {
//Technically calls ToString() with a null check, but this shows the string format.
          return $"{{ Prop1: {Prop1}, Prop2: {Prop2} }}"
    }

    //Had to use ILDisassembler to get here.
    public override bool Equals(object o)
    {
        return o is <>f__AnonymousType0`2<Prop1, Prop2> a && EqualityComparer<Prop1>.Default.Equals(a.Prop1, Prop1) && EqualityComparer<Prop2>.Default.Equals(a.;
    }
    public override int GetHashCode()
    {
        //This is pretty ugly.  Having a hard time decompiling from MSIL in my head at the moment.
//Long story short, combines hash codes with the results from EqualityComparer<T>.Default
    }
}

holdenmai avatar Sep 19 '22 03:09 holdenmai