Skip to content

In-Memory concurrency check is not doing a sequence check when using a byte array type #12214

Closed
@tsangste

Description

@tsangste

Describe what is not working as expected.

I am using a byte array for my concurrency check and when using automapper to go from my dto to my ef entity object it causes the following error when saving with the in-memory provider for my tests:

Exception thrown: 'Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException' in ConcurrencyTest.dll
An unhandled exception of type 'Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException' occurred in ConcurrencyTest.dll
Conflicts were detected for instance of entity type 'Customer' on the concurrency token properties {'RowVersion'}. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting values.

The error has started occurring when upgrading my project to 2.1. I narrowed it to #10158 where concurrency token checks were enabled for the in-memory provider. After reviewing the code I notice it is doing a equals check and possibly might require a equal sequence check if its a byte array?

Steps to reproduce

using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using Microsoft.EntityFrameworkCore;

namespace ConcurrencyTest
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var customer = new Customer
            {
                FirstName = "John",
                LastName = "Smith",
                RowVersion = Convert.FromBase64String("AAAAAAAQexM=")
            };

            using (var context = new DatabaseContext())
            {
                context.Customers.Add(customer);
                context.SaveChanges();
            }

            using (var context = new DatabaseContext())
            {
                try
                {
                    var currentCustomer = context.Customers.Single(c => c.Id == customer.Id);

                    currentCustomer.FirstName = "Dr";
                    currentCustomer.LastName = "Who";

                    // Set to the same sequence byte[] value to simulate originalvalue being set from another object i.e. if a dto mapping occured
                    context.Entry(currentCustomer).Property("RowVersion").OriginalValue = Convert.FromBase64String("AAAAAAAQexM=");

                    context.SaveChanges();
                }
                catch (DbUpdateConcurrencyException e)
                {
                    Console.WriteLine(e);
                    throw;
                }
            }
        }
    }

    public class DatabaseContext : DbContext
    {
        public DbSet<Customer> Customers { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder
                .UseInMemoryDatabase("ConcurrencyTest");
        }
    }

    public class Customer
    {
        [Key]
        public int Id { get; set; }

        public string FirstName { get; set; }

        public string LastName { get; set; }

        [ConcurrencyCheck]
        public byte[] RowVersion { get; set; }
    }
}

Further technical details

EF Core version: 2.1
Database Provider: Microsoft.EntityFrameworkCore.InMemory
Operating system: Windows 10
IDE: Visual Studio 2017 15.7.3

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions