Skip to content

Byte array in owned entity mapped to JSON returns different value when using projection #3566

@drapka

Description

@drapka

Hi, I encountered a problem while working with a byte array in an owned entity mapped to JSON - it looks like the materialization behaves differently when getting the whole entity vs getting only the specific property.
I would like to use this for encrypted columns, below is a simplified example.

Looks similar to dotnet/efcore#33435.

Sample Code

using Microsoft.EntityFrameworkCore;

await using var context = new BloggingContext();
await context.Database.EnsureCreatedAsync();
var blog = new Blog
{
    Posts =
    [
        new Post { Content = "This is the content of the first post."u8.ToArray() }
    ]
};


await context.Blogs.AddAsync(blog);
await context.SaveChangesAsync();

var blogs1 = await context.Blogs.ToListAsync(); 
var blogs2 = await context.Blogs
    .SelectMany(b => b.Posts.Select(p => p.Content))
    .ToListAsync();

var content1 = blogs1[0].Posts[0].Content; 
var content2 = blogs2[0];
var decodedViaSql = await context.Database
    .SqlQueryRaw<byte[]>( "SELECT decode((b.\"Posts\"->0->>'Content'), 'base64') AS \"Value\" FROM \"Blogs\" AS b LIMIT 1")
    .FirstAsync();

Console.WriteLine($"content1 len={content1.Length}, bytes={string.Join(",", content1)}"); 
Console.WriteLine($"content2 len={content2.Length}, bytes={string.Join(",", content2)}"); 
Console.WriteLine($"decodedViaSql len={decodedViaSql.Length}, bytes={string.Join(",", decodedViaSql)}"); 
Console.WriteLine("content1 equals content2: " + content1.SequenceEqual(content2)); 
Console.WriteLine("content1 equals decodedViaSql: " + content1.SequenceEqual(decodedViaSql));
Console.WriteLine("content2 equals decodedViaSql: " + content2.SequenceEqual(decodedViaSql));

public class BloggingContext : DbContext
{
    public DbSet<Blog> Blogs => Set<Blog>();

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder.UseNpgsql("Host=localhost;Port=5432;Username=postgres;Password=password;Database=demo");

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>()
            .OwnsMany<Post>(b => b.Posts, b =>
            {
                b.ToJson();
                b.WithOwner();
            });
    }
}

public class Blog
{
    public int BlogId { get; set; }
    public List<Post> Posts { get; set; } = [];
}

public class Post
{
    public required byte[] Content { get; set; }
}

Thanks for looking into this.

Versions:
.Net: 9, 10
Npgsql.EntityFrameworkCore.PostgreSQL: v9.0.4, v10.0.0

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions