dotnet / ef6

This is the codebase for Entity Framework 6 (previously maintained at https://entityframework.codeplex.com). Entity Framework Core is maintained at https://github.com/dotnet/efcore.
https://docs.microsoft.com/ef/ef6
MIT License
1.42k stars 544 forks source link

Issue running EF6 “SqlQuery<T>()” in the context of .NET Core 3.x #1871

Closed joelmdev closed 2 years ago

joelmdev commented 3 years ago

Note that this issue is largely duplicated from this SO question. Since this also deals with Azure Functions and .NET Core, please let me know if i need to repost this issue elsewhere.

We have an Azure Function v3 running on .NET Core 3.1. The function references .NET Framework 4.8 class library that leverages EF 6.4. Accessing DbSets and rehydrating entities, change tracking, lazy loading- most operations work as expected. However, calling DbContext.Database.SqlQuery<TElement>(String, Object[]) results in an exception with the following message:

The SqlParameterCollection only accepts non-null SqlParameter type objects, not SqlParameter objects.

The stack trace sans our custom code is as follows:

at System.Data.SqlClient.SqlParameterCollection.ValidateType(Object value)
   at System.Data.SqlClient.SqlParameterCollection.AddRange(Array values)
   at System.Data.Entity.Core.Objects.ObjectContext.CreateStoreCommand(String commandText, Object[] parameters)
   at System.Data.Entity.Core.Objects.ObjectContext.ExecuteStoreQueryInternal[TElement](String commandText, String entitySetName, ExecutionOptions executionOptions, Object[] parameters)
   at System.Data.Entity.Core.Objects.ObjectContext.<>c__DisplayClass186_0`1.<ExecuteStoreQueryReliably>b__1()
   at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func`1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)
   at System.Data.Entity.Core.Objects.ObjectContext.<>c__DisplayClass186_0`1.<ExecuteStoreQueryReliably>b__0()
   at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1 operation)
   at System.Data.Entity.Core.Objects.ObjectContext.ExecuteStoreQueryReliably[TElement](String commandText, String entitySetName, ExecutionOptions executionOptions, Object[] parameters)
   at System.Data.Entity.Core.Objects.ObjectContext.ExecuteStoreQuery[TElement](String commandText, ExecutionOptions executionOptions, Object[] parameters)
   at System.Data.Entity.Internal.InternalContext.<>c__DisplayClass111_0`1.<ExecuteSqlQuery>b__0()
   at System.Data.Entity.Internal.LazyEnumerator`1.MoveNext()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   <<a bunch of our custom stuff>>
   at Microsoft.Azure.WebJobs.Host.Executors.VoidMethodInvoker`2.InvokeAsync(TReflected instance, Object[] arguments) in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\VoidMethodInvoker.cs:line 21
   at Microsoft.Azure.WebJobs.Host.Executors.FunctionInvoker`2.<InvokeAsync>d__10.MoveNext() in C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Executors\FunctionInvoker.cs:line 52

I believe this is related to this issue, however EF6 is not compatible with the new Microsoft.Data.SqlClient provider so we cant switch from System.Data.SqlClient to it. There may be an explanation that escapes me, but it's clear from he stack trace that the error is happening in System.Data.SqlClient.SqlParameterCollection and we are most definitely using System.Data.SqlClient.SqlParameter so I'm not sure where this is breaking down but it seems to be out of our hands.

Please let me know if I can provide any further information to assist in resolving this issue.

Further technical details

EF version: 6.4 Database Provider: EntityFramework.SqlServer Operating system: N/A, Azure Function v3 IDE: VS2019

ErikEJ commented 3 years ago

Please share a code snippet or a full repro.

joelmdev commented 3 years ago

Here's the slimmed down version to get all of our custom code out of the way. If we need a compiling and running repro that's going to take me some time.

In the .NetCore 3.1 Function (v3):

using Microsoft.Azure.ServiceBus;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
using MyProject.Services;

namespace MyProject.Functions
{
    public class MyFunction
    {
        private readonly MyDbContext _ctx;

        public MyFunction(MyDbContext ctx)
        {
            _ctx = ctx;
        }

        [FunctionName("MyFunction")]
        public override void Run([ServiceBusTrigger("%QueueName%", Connection = "%ConnectionString%")]
            Message msg,
            ILogger log)
        {
            var service = new MyService();

            service.DoStuff();
        }
    }
}

In the .NET 4.8 class library:

using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;

namespace MyProject.Services
{
    public class MyService
    {
        private readonly MyDbContext _context;

        public MyService(MyDbContext ctx)
        {
            _context = ctx;
        }

        public void DoStuff()
        {
            //this works
            var lineItems1 = _context.LineItems.Where(li => li.Description == "something arbitrary").ToList();

            //this gives the exception and stack trace from the original post
            var lineItems2 = _context.Database.SqlQuery<LineItem>(
                "SELECT * FROM LineItems WHERE Description = @Description",
                new[] { new SqlParameter("@Description", "something arbitrary") }).ToList();
        }
    }
}

My workaround for this has been to de-parameterize our queries and used SQL escaped strings instead, which is really not awesome.

ErikEJ commented 3 years ago

So you are calling a .NET Framework class library from a .NET Core 3.1 app? Sounds dangerous.

Can you share a runnable repro, I suspect that you have a indirect reference to M.D.S. somewhere.

You could also try to change to:

new[] { new System.Data.SqlClient.SqlParameter("@Description", "something arbitrary") }).ToList();
joelmdev commented 3 years ago

EF6 is supposedly supported as of .NET Core 3.0. Most things seem to work well. I've already searched solution wide for Microsoft.Data.SqlClient and tried the fully qualified name as you mentioned above. No references and no dice, respectively. I don't understand the exact inner workings that cause this to happen, but my money is still on this being related to the issue I linked to in the original post. EF Core users were running into this same problem and migrating from System.Data.SqlClient to Microsoft.Data.SqlClient was the fix. I think the problem applies here as well, but there's no upgrade path for EF6 at this time.

I'll have to find time to put together a runnable repro, but I think there's a pretty clear path to investigate without one.

ErikEJ commented 3 years ago

EF core is not EF6. A proper repro is needed.

joelmdev commented 3 years ago

Thanks for that bit of insight. Seeing that you commented on the linked issue some time back, I would imagine that you can see the similarities. As mentioned, when time allows I will look into putting together a repro.

ajcvickers commented 2 years ago

This issue has been closed because EF6 is no longer being actively developed. We are instead focusing on stability of the codebase, which means we will only make changes to address security issues. See the repo README for more information.