-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Open
Milestone
Description
Some operations do not respect the HasConversion configuration when used with in-memory DB
See the following code where the same entity is retrieved from DB using 2 methods:
a) fetching all table data into array and using Linq-to-Objects over it
b) using EF query
One of the approaches is able to correctly locate the data while the other one is not. Removing the .HasConversion
part causes both approaches to behave correctly the same way
Code to reproduce
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
await using var ctx = new TodoDb();
ctx.Todos.Add(new Todo { Id = TodoDb.DefaultId });
await ctx.SaveChangesAsync();
var todos = await ctx.Todos.ToArrayAsync();
var todoFromArray = todos.FirstOrDefault(t => t.Id == TodoDb.DefaultId);
Console.WriteLine(todoFromArray is not null); // true
var todoFromDb = ctx.Todos.FirstOrDefault(t => t.Id == TodoDb.DefaultId);
Console.WriteLine(todoFromDb is not null); // false
class TodoDb : DbContext
{
public static Guid DefaultId = Guid.Parse("2965109F-B866-4B41-A600-AF570AC1E4C7");
public DbSet<Todo> Todos => Set<Todo>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new TodoConfiguration());
base.OnModelCreating(modelBuilder);
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseInMemoryDatabase("TodoList");
}
private static class DbGuidConverter
{
public static byte[] GuidToBinary(Guid guid)
{
var bytes = guid.ToByteArray();
Rotate(bytes);
return bytes;
}
public static Guid BinaryToGuid(byte[] binary)
{
Rotate(binary);
return new Guid(binary);
}
static void Rotate(Span<byte> bytes)
{
Swap(bytes,0,3);
Swap(bytes,1,2);
Swap(bytes,4,5);
Swap(bytes,6,7);
}
static void Swap(Span<byte> source, int a, int b) => (source[a], source[b]) = (source[b], source[a]);
}
internal class TodoConfiguration : IEntityTypeConfiguration<Todo>
{
public void Configure(EntityTypeBuilder<Todo> builder)
{
builder.HasKey(x => x.Id);
builder
.Property(x => x.Id)
.HasConversion(id => DbGuidConverter.GuidToBinary(id), blob => DbGuidConverter.BinaryToGuid(blob));
}
}
}
public class Todo
{
public Guid Id { get; set; }
}
Complete csproj file:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="7.0.5" />
</ItemGroup>
</Project>
Metadata
Metadata
Assignees
Type
Projects
Milestone
Relationships
Development
Select code repository
Activity
ajcvickers commentedon May 2, 2023
@oleggolovkov Have you tried with the latest EF8 preview? We already fixed some issues in this area.
oleggolovkov commentedon May 2, 2023
@ajcvickers I can see that behavior is the same using
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.0-preview.3.23174.2" />
ajcvickers commentedon May 2, 2023
Thanks. We will investigate.
ajcvickers commentedon May 3, 2023
Note for triage: looks like the in-memory database is not doing deep comparisons of the bytes after the conversion.