dotnet / efcore

EF Core is a modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
https://docs.microsoft.com/ef/
MIT License
13.76k stars 3.18k forks source link

IEnumerable and IReadOnlyCollection navigation properties throw System.Security.VerificationException #26897

Open reinux opened 2 years ago

reinux commented 2 years ago

This is the same exception that has been discussed a few years back in several issues: https://github.com/dotnet/efcore/issues/12881 https://github.com/dotnet/efcore/issues/12558 https://github.com/dotnet/efcore/issues/12925 . It was apparently made moot by QueryBuffer being removed, but it still occurs.

It appears that the problem has returned; using IReadOnlyCollection or IEnumerable in place of ICollection causes an exception:

System.ArgumentException: GenericArguments[1], 'System.Collections.Generic.IReadOnlyCollection`1[CCServer.Data.Principals.Principal]', on 'TCollection InitializeCollection[TElement,TCollection](Int32, Microsoft.EntityFrameworkCore.Query.QueryContext, System.Data.Common.DbDataReader, Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryResultCoordinator, System.Func`3[Microsoft.EntityFrameworkCore.Query.QueryContext,System.Data.Common.DbDataReader,System.Object[]], System.Func`3[Microsoft.EntityFrameworkCore.Query.QueryContext,System.Data.Common.DbDataReader,System.Object[]], Microsoft.EntityFrameworkCore.Metadata.IClrCollectionAccessor)' violates the constraint of type 'TCollection'.
 ---> System.Security.VerificationException: Method Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor+ShaperProcessingExpressionVisitor.InitializeCollection: type argument 'System.Collections.Generic.IReadOnlyCollection`1[CCServer.Data.Principals.Principal]' violates the constraint of type parameter 'TCollection'.
   at System.RuntimeMethodHandle.GetStubIfNeeded(RuntimeMethodHandleInternal method, RuntimeType declaringType, RuntimeType[] methodInstantiation)
   at System.Reflection.RuntimeMethodInfo.MakeGenericMethod(Type[] methodInstantiation)
   --- End of inner exception stack trace ---
   at System.RuntimeType.ValidateGenericArguments(MemberInfo definition, RuntimeType[] genericArguments, Exception e)
   at System.Reflection.RuntimeMethodInfo.MakeGenericMethod(Type[] methodInstantiation)
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.VisitExtension(Expression extensionExpression)
   at System.Linq.Expressions.Expression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ProcessShaper(Expression shaperExpression, RelationalCommandCache& relationalCommandCache, LambdaExpression& relatedDataLoaders, Int32& collectionId)
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.VisitShapedQuery(ShapedQueryExpression shapedQueryExpression)
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.VisitExtension(Expression extensionExpression)
   at System.Linq.Expressions.Expression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](Expression query, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](IDatabase database, Expression query, IModel model, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass9_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute[TResult](Expression expression)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.GetEnumerator()
   at Microsoft.FSharp.Collections.SeqModule.ToList[T](IEnumerable`1 source) in D:\a\_work\1\s\src\fsharp\FSharp.Core\seq.fs:line 803
   at <StartupCode$FSI_0004>.$FSI_0004.main@()

Include provider and version information

EF Core version: 6.0.0 Database provider: (e.g. Npgsql.EntityFrameworkCore.PostgreSQL) Target framework: (e.g. .NET 6.0) Operating system: Windows 11 IDE: (e.g. Visual Studio 2022 17.0)

ajcvickers commented 2 years ago

/cc @smitpatel

smitpatel commented 2 years ago

This issue is lacking enough information for us to effectively reproduce. Please post a runnable project/solution or complete code listing that demonstrates the behavior you are seeing.

reinux commented 2 years ago

Haven't forgotten about this but I'm kind of under the weather right now... Will get back soon.

Toumash commented 2 years ago

Haven't forgotten about this but I'm kind of under the weather right now... Will get back soon.

Is this the same situation that i ve here? I've writtern 2 unit tests for that with inmemorydb, so super easy to run https://github.com/Toumash/efcore-readonlycollection-test

ajcvickers commented 2 years ago

Note for triage: passes with ICollection nav; fails with IReadOnlyCollection nav, with 6.0 on in-memory and SQL Server.

    public class RootEntity
    {
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int Id { get; set; }
        public IReadOnlyCollection<ChildEntity> ChildEntites { get; set; }
    }
var obj = _context.RootEntites
    .Select(t => new { Values = t.ChildEntites.Filter() })
    .First();
public static class DatabaseQueryExtensions
{
    public static IEnumerable<ChildEntity> Filter(this IEnumerable<ChildEntity> source) =>
        source.Where(c => c.Value != "first");
}
System.ArgumentException
GenericArguments[1], 'System.Collections.Generic.IReadOnlyCollection`1[efcore.readonlycollection.tests.ChildEntity]', on 'TCollection InitializeCollection[TElement,TCollection](Int32, Microsoft.EntityFrameworkCore.Query.QueryContext, System.Data.Common.DbDataReader, Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryResultCoordinator, System.Func`3[Microsoft.EntityFrameworkCore.Query.QueryContext,System.Data.Common.DbDataReader,System.Object[]], System.Func`3[Microsoft.EntityFrameworkCore.Query.QueryContext,System.Data.Common.DbDataReader,System.Object[]], Microsoft.EntityFrameworkCore.Metadata.IClrCollectionAccessor)' violates the constraint of type 'TCollection'.
   at System.RuntimeType.ValidateGenericArguments(MemberInfo definition, RuntimeType[] genericArguments, Exception e)
   at System.Reflection.RuntimeMethodInfo.MakeGenericMethod(Type[] methodInstantiation)
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.VisitExtension(Expression extensionExpression)
   at System.Linq.Expressions.Expression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at System.Dynamic.Utils.ExpressionVisitorUtils.VisitArguments(ExpressionVisitor visitor, IArgumentProvider nodes)
   at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node)
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at System.Dynamic.Utils.ExpressionVisitorUtils.VisitArguments(ExpressionVisitor visitor, IArgumentProvider nodes)
   at System.Linq.Expressions.ExpressionVisitor.VisitNew(NewExpression node)
   at System.Linq.Expressions.NewExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.ShaperProcessingExpressionVisitor.ProcessShaper(Expression shaperExpression, RelationalCommandCache& relationalCommandCache, LambdaExpression& relatedDataLoaders, Int32& collectionId)
   at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.VisitShapedQuery(ShapedQueryExpression shapedQueryExpression)
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.VisitExtension(Expression extensionExpression)
   at System.Linq.Expressions.Expression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](Expression query, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](IDatabase database, Expression query, IModel model, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass9_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute[TResult](Expression expression)
   at System.Linq.Queryable.First[TSource](IQueryable`1 source)
   at efcore.readonlycollection.tests.ReadonlyCollectionTests.WhereWithIReadonlyColection_by_extensionMethod_should_work_but_will_fail_anyway() in C:\local\code\repros\efcore-readonlycollection-test-master\efcore-readonlycollection-test-master\UnitTest1.cs:line 47

System.Security.VerificationException
Method Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor+ShaperProcessingExpressionVisitor.InitializeCollection: type argument 'System.Collections.Generic.IReadOnlyCollection`1[efcore.readonlycollection.tests.ChildEntity]' violates the constraint of type parameter 'TCollection'.
   at System.RuntimeMethodHandle.GetStubIfNeeded(RuntimeMethodHandleInternal method, RuntimeType declaringType, RuntimeType[] methodInstantiation)
   at System.Reflection.RuntimeMethodInfo.MakeGenericMethod(Type[] methodInstantiation)
ajcvickers commented 2 years ago

Note for triage: not a regression from 5.0.

Copy/paste repro:

public class RootEntity
{
    public int Id { get; set; }
    public IReadOnlyCollection<ChildEntity> ChildEntites { get; set; }
}

public class ChildEntity
{
    public int Id { get; set; }
    public RootEntity RootEntity { get; set; }
    public string Value { get; set; }
}

public class SomeDbContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder
            .UseSqlServer(Your.ConnectionString)
            .LogTo(Console.WriteLine, LogLevel.Information)
            .EnableSensitiveDataLogging();

    public DbSet<RootEntity> RootEntities { get; set; }
}

public class Program
{
    public static void Main()
    {
        using (var context = new SomeDbContext())
        {
            context.Database.EnsureDeleted();
            context.Database.EnsureCreated();

            var obj = context.RootEntities
                .Select(t => new { Values = t.ChildEntites.Filter() })
                .First();
        }
    }
}

public static class DatabaseQueryExtensions
{
    public static IEnumerable<ChildEntity> Filter(this IEnumerable<ChildEntity> source)
        => source.Where(c => c.Value != "first");
}
ajcvickers commented 2 years ago

Note from triage: consider improving functionality of collection accessor.

Helius01 commented 2 years ago

I have same issue . whats the solution?

Toumash commented 2 years ago

I have same issue . whats the solution?

For me it was to not use the query extensions, and write the where expression linq directly in the query itself. Repro for me and solution was: https://github.com/Toumash/efcore-readonlycollection-test

rofenix2 commented 1 year ago

See this issue please, it might be related: https://github.com/dotnet/efcore/issues/30195

colespencer1453 commented 1 year ago

@ajcvickers @smitpatel Has the root cause of this issue been identified? I'm running into this issue on a rather complicated query with an Expression that works fine by itself, but throws this exception when adding statements to the query. The query works fine when replacing the abstracted functions with the code directly in the Select lamdas. I can easily revert to the working code, but was just curious on conjectures for why this exception occurs.