Issue running EF6 "SqlQuery<T>()" in the context of .NET Core 3.x

Viewed 166

I have a .NET Framework 4.x class library that leverages Entity Framework 6. Up until recently it has been leveraged in another 4.x based application but more recently it's been used in a .NET Core 3.x Azure function. Nothing has been changed in the class library and most aspects seem to work as expected with the exception of methods that accept SqlParameters, in particular DbContext.Database.SqlQuery<T>().

The problem that I'm running into seems to be very similar to what happens when using EF Core with .NET Core 3.x without updating from System.Data.SqlClient to Microsoft.Data.SqlClient, except it flips the problem on its head. The exception message is:

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

However, all documentation that I've come across states that EF 6 is compatible with .NET Core 3.x but is not compatible with Microsoft.Data.SqlClient. I'm most certainly using System.Data.SqlClient.SqlParameter and I can't see anywhere where the app is dipping into Microsoft.Data.SqlClient, as illustrated by this partial stack trace:

   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

EDIT: Here's the pared down code from the solution...

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();
    }
}

}

0 Answers
Related