Skip to content

Batch insert with SQL_Variant fails when different variant types are present #12482

@koenbeuk

Description

@koenbeuk

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)

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions