zzzprojects / EntityFramework-Plus

Entity Framework Plus extends your DbContext with must-haves features: Include Filter, Auditing, Caching, Query Future, Batch Delete, Batch Update, and more
https://entityframework-plus.net/
MIT License
2.27k stars 318 forks source link

IncludeOptimized does not work with private set #769

Closed SpaceOgre closed 1 year ago

SpaceOgre commented 1 year ago

1. Description

If an Entity class have a property with private set then IncludeOptimized does not work.

2. Exception

System.ArgumentException: Property set method not found.
   at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)
   at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, Object[] index)
   at Z.EntityFramework.Plus.PropertyOrFieldAccessor.SetValue(Object obj, Object value)
   at Z.EntityFramework.Plus.QueryIncludeOptimizedNullCollection.CheckNullRecursive(Object currentItem, List`1 paths, Int32 index)
   at Z.EntityFramework.Plus.QueryIncludeOptimizedNullCollection.NullCollectionToEmpty(Object item, List`1 childs)
   at Z.EntityFramework.Plus.QueryIncludeOptimizedParentQueryable`1.CreateEnumerable()
   at Z.EntityFramework.Plus.QueryIncludeOptimizedParentQueryable`1.GetEnumerator()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)

3. Fiddle or Project

public class Entity
{
    public Collection<Child> Children { get; private set; }
}

public class Child{}

4. Any further technical details

I think it might be that you get the properties from the Entity Framework wrapper class type instead of the type from the actual class, since if you get it from the actual class then SetValue from PropertyInfo should work just fine for private setters.

Does not work:

using System;
using System.Reflection;
using System.Reflection.Emit;

public class BaseClass
{
    public virtual String Prop
    {
        get;
        private set;
    }
}

public class ImpClass : BaseClass
{
}

public class TypeMain
{
    public static void Main() 
    {
        Type myType =(typeof(ImpClass));
    var obj = new ImpClass();
        PropertyInfo[] myPropertyInfo = myType.GetProperties(BindingFlags.Public|BindingFlags.Instance);
    myPropertyInfo[0].SetValue(obj, "test", null);
    obj.Dump();
    }
}

But this does:

using System;
using System.Reflection;
using System.Reflection.Emit;

public class BaseClass
{
    public virtual String Prop
    {
        get;
        private set;
    }
}

public class ImpClass : BaseClass
{
}

public class TypeMain
{
    public static void Main() 
    {
        Type myType =(typeof(BaseClass));
        var obj = new ImpClass();
        PropertyInfo[] myPropertyInfo = myType.GetProperties(BindingFlags.Public|BindingFlags.Instance);
    myPropertyInfo[0].SetValue(obj, "test", null);
    obj.Dump();
    }
}
JonathanMagnan commented 1 year ago

Hello @SpaceOgre ,

Do you think you could create a Fiddle with the issue?

My developer tried to reproduce it, but he tells me everything is working: https://dotnetfiddle.net/2ab36j

Best Regards,

Jon


Sponsorship Help us improve this library

Performance Libraries context.BulkInsert(list, options => options.BatchSize = 1000); Entity Framework ExtensionsDapper Plus

Runtime Evaluation Eval.Execute("x + y", new {x = 1, y = 2}); // return 3 C# Eval Function

SpaceOgre commented 1 year ago

@JonathanMagnan will see if I can manage that.

But now i see that I totally forgot to mention that I saw the problem on Net Framework 4.8, sorry about that.

JonathanMagnan commented 1 year ago

I'm pretty sure my developer tried to reproduce it on EF Core, that could explain why ;)

Could you confirm that you are using EF6?

SpaceOgre commented 1 year ago

@JonathanMagnan yes I'm using Ef6. Sorry about not puttning that in the report straight away... Working in projects with both EF6 and EF Core atm so mix it up sometimes.

JonathanMagnan commented 1 year ago

Hello @SpaceOgre ,

Look like my developer cannot reproduce it either with EF6: https://dotnetfiddle.net/dsnsPF

Could you check what he did wrong on the Fiddle and provide one with the issue?

Best Regards,

Jon

SpaceOgre commented 1 year ago

@JonathanMagnan I finally tracked down the problem...

My first report was not correct, the problem arises when include a sub-child in the child include with at select:

context.Set<Parent>().IncludedOptimized(x => x.Children.Select(y => y.Toy))

See this fiddle: https://dotnetfiddle.net/5q2VuO

JonathanMagnan commented 1 year ago

Hello @SpaceOgre ,

Unfortunately, we will have to abandon this one.

While we can successfully reproduce this issue, we have not found any easy way to fix it.

The proxy type created by Entity Framework makes handling this scenario harder.

So at this moment, we will treat this scenario as unsupported.

Best Regards,

Jon


Are you finding this library useful? If so, please consider supporting its continued development by becoming a sponsor.

Additionally, if you think your enterprise would benefit from this library, we encourage you to suggest they become a sponsor as well. Your support will help ensure this library remains alive and well-supported.

SpaceOgre commented 1 year ago

@JonathanMagnan no problem, totalt get that it might be hard to fix. And the use of private setter in these cases is probably pretty rare. An easy fix is to use protected instead of private and you get more or less the same protection as private.