zzzprojects / Eval-Expression.NET

C# Eval Expression | Evaluate, Compile, and Execute C# code and expression at runtime.
https://eval-expression.net/
Other
449 stars 86 forks source link

IQueryable EF Syntax error with select #62

Closed shapemetrics closed 4 years ago

shapemetrics commented 4 years ago

The below statement is invalid when attempting to select against a sub-item in the query. The object exists using Include(p => p.Contacts) which should allow this syntax in static code.

n.Select(
q => new {
q.Contacts.AddressLine1
}
).ToList();

However, here is the output error I am getting from the above statement. The entity looks like so

Case -> Contacts, where contacts are a related table.

The EF query is

Context.Cases.Include(p => p.Contacts);

'Oops! No applicable member has been found for the expression. The error occurred for expression "." at position 1 near ".Select( q => new { q.C".'

JonathanMagnan commented 4 years ago

Hello @shapemetrics ,

Thank you for reporting.

My developer will try to reproduce your scenario.

Best Regards,

Jonathan

JonathanMagnan commented 4 years ago

My developer looked at it but he was not able to reproduce the issue.

However, he finds your code provided a little bit strange such as the AddressLine1 property on the list of contact Contacts. He believes that some information is missing to allow him to reproduce it.

Is it possible to provide a runnable example with the issue?

Here is one of the examples he made:

using System.Data.Entity;
using System.Linq;
using System.Windows.Forms;

namespace Z.Expressions.Lab
{
    public partial class Form_Request_EF_Include_EVAL : Form
    {
        public Form_Request_EF_Include_EVAL()
        {
            InitializeComponent(); 

            // CLEAN  
            using (var context = new EntityContext())
            {
                context.EntitySimples.RemoveRange(context.EntitySimples);

                context.SaveChanges();
            }

            // SEED  
            using (var context = new EntityContext())
            {
                for (var i = 0; i < 3; i++)
                {
                    context.EntitySimples.Add(new EntitySimple {ColumnInt = i, EntitySimplChild = new EntitySimpleChild { ColumnString = i.ToString()}});
                }

                context.SaveChanges();
            }

            // SEED  
            using (var context = new EntityContext())
            {
                var example1 = context.EntitySimples.Include(x => x.EntitySimplChild).SelectDynamic(q => @"new { q.EntitySimplChild.ColumnString }  ").ToList();

                // Required to add the "Include" extension method from Entity Framework
                EvalManager.DefaultContext.RegisterAssembly(typeof(DbContext).Assembly);

                var example2 = Eval.Execute("return EntitySimples.Include(x => x.EntitySimplChild).Select(q => new { q.EntitySimplChild.ColumnString } ).ToList()", context);
            }
        }

        public class EntityContext : DbContext
        {
            public EntityContext() : base(My.ConnectionString)
            {
            }

            public DbSet<EntitySimple> EntitySimples { get; set; }
            public DbSet<EntitySimpleChild> EntitySimpleChilds { get; set; }

            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                base.OnModelCreating(modelBuilder);
            }
        }

        public class EntitySimple
        {
            public int ID { get; set; }
            public int ColumnInt { get; set; }
            public string ColumnString { get; set; }
            public EntitySimpleChild EntitySimplChild { get; set; }
        }

        public class EntitySimpleChild
        {
            public int ID { get; set; }
            public int ColumnInt { get; set; }
            public string ColumnString { get; set; }
        }
    }
}
shapemetrics commented 4 years ago

I am using collections on a foreign key.


public class EntitySimple
        {
            public int ID { get; set; }
            public int ColumnInt { get; set; }
            public string ColumnString { get; set; }
[ForeignKey("ForiegnKey")]
            public virtual ICollection<EntitySimpleChild> Children{ get; set; }
        }

        public class EntitySimpleChild
        {
            public int ID { get; set; }
public int ForiegnKey{get;set;}
            public int ColumnInt { get; set; }
            public string ColumnString { get; set; }
        }
JonathanMagnan commented 4 years ago

Hello @shapemetrics ,

Here is a .NET Fiddle with your code: https://dotnetfiddle.net/74mbTs

We are not sure yet of your full expression, so if you could provide it, we will be able to improve our online example.

Best Regards,

Jonathan

shapemetrics commented 4 years ago
    var filtered = context.EntitySimples;

    dynamic list = Eval.Execute("n.Select(q => new {q.Children.ColumnString}", new { n = filtered });
JonathanMagnan commented 4 years ago

Hello @shapemetrics ,

Here is what my developer provided me:

dynamic list = Eval.Execute("n.SelectMany(q => q.Children.Select(q2 => q2.ColumnString)).ToList()", new {n = filtered});

The child is a collection in our example, so you need to select the property, you cannot access it directly through the collection.

Let me know if that solves this issue.

shapemetrics commented 4 years ago

It looks like I will need to create multiple queries based on the collection(s) and join with the original to produce a new anonymous type. Thanks for the heads up.