-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Description
Batch inserts seem to fail when SQL_Variant is being used and within the batch, different types are being used as params for sql_variant.
e.g. EF core generates something like the following:
CREATE TABLE [dbo].[Foos](Value SQL_VARIANT)
DECLARE @p1 INT = 1;
DECLARE @p2 VARCHAR = 'two';
INSERT INTO Foos
VALUES (@p2), (@p1)
This will raise an error: Conversion failed when converting the varchar value 't' to data type int.
.
When P2 and P1 are reversed, things will work but only because INT is convertible to VARCHAR. Otherwise declaring both parameters as SQL_VARIANT will also work; Casting like VALUES (CAST(@p2 AS SQL_VARIANT)....)
also works.
Steps to reproduce
// Given a model
public class Foo
{
public int Id { get; set; }
[Column(TypeName = "sql_variant")]
public object Value { get; set; }
}
// the following will throw:
context.Foos.Add(new Foo { Id = 1, Value = "one" });
context.Foos.Add(new Foo { Id = 2, Value = 2 });
causes:
Microsoft.EntityFrameworkCore.DbUpdateException: 'An error occurred while updating the entries. See the inner exception for details.'
>> SqlException: Error converting data type nvarchar to int.
Stack:
at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(DbContext _, ValueTuple`2 parameters, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IReadOnlyList`1 entriesToSave, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
Given the above code, I wonder why the insert statement does not respect the explicit TypeName when declaring the SQL parameters and instead seems to use the implicit CLR <-> SQL mapping?
Further technical details
EF Core version: 2.1
Database Provider: Microsoft.EntityFrameworkCore.SqlServer
Operating system: Windows 10 X64
IDE: (e.g. Visual Studio 2017 15.7)