npgsql / efcore.pg

Entity Framework Core provider for PostgreSQL
PostgreSQL License
1.53k stars 223 forks source link

Fully support multi-dimensional arrays #314

Open roji opened 6 years ago

roji commented 6 years ago

Single-dimensional PostgreSQL arrays are well-supported, but not multi-dimensional ones. This issue is meant to see how many people actually want this - please upvote if you'd benefit from it.

Things that need to be implemented:

alexzaytsev-newsroomly commented 6 years ago

@roji The documentation here: http://www.npgsql.org/efcore/mapping-and-translation.html says However, operations such as indexing the array, searching for elements in it, etc. aren't yet translated to SQL and will be evaluated client-side. This will probably be fixed in 1.2. Since the latest version is over 2.0, i wanted to check - is something like

context.MyEntities.FirstOrDefault(x => x.ArrayProperty.Any(i => i == 42)) expected to be evaluated client side or actually sent to the backend?

(i noticed that Contains() is listed as supported on the same page)

roji commented 6 years ago

Will fix the docs, thanks. Yes, array operation translation was added to 2.0, which means server-evaluation (see #120). The translation list on the docs page is correct, I'll remove the old sentence you spotted. If you run into any issues or client evaluation occurs, please open a new issue.

Note, however, multidimensional arrays (this issue) aren't supported at all as well as single-dimensional ones.

krzlabrdx commented 1 year ago

@roji It seems that multidimensional arrays are essentially a dead feature but is it possible to somehow work this missing feature around for very limited use cases?

I'm using EF Core just to prepare db migration, read the data with no LINQ expressions and I store them with no EF Core at all using COPY operation:

[Table("abcs")]
class Abc
{
  // ...

  // fails to generate migration, maybe we'd have to use some kind of custom converter?
  [Column("my_col", TypeName="bigint[][]")]
  public long[][] MyCol { get; set; } // or ideally List<List<long>>
}

// reading
var someData = await _dbContext.Abcs.FromSqlInterpolated("...").ToListAsync();

// writing
var _conn = (NpgsqlConnection)dbContext.Database.GetDbConnection()

await using var writer = await _connection.BeginBinaryImportAsync("COPY abcs (..., ..., my_col, ..) FROM STDIN (FORMAT BINARY)", token);

foreach (...) {
  await writer.StartRowAsync(token);
  await writer.WriteAsync(..., NpgsqlDbType.Bigint, token);
  await writer.WriteAsync(abc.MyCol.ToArray(), NpgsqlDbType.Array | NpgsqlDbType.Bigint, token); // how to specify array of arrays?
}

await writer.CompleteAsync(token);
roji commented 1 year ago

It seems that multidimensional arrays are essentially a dead feature

Multidimensional array support in the EF provider isn't dead - it's just not fully supported, and not enough people have asked for improvements to it, so I haven't prioritized working on it.

Your above question has nothing to with EF - the COPY API works at the (non-EF) ADO.NET layer, where multidimensional arrays should be fully supported. The NpgsqlDbType for a multidimensional array is the same as for a regular array, since in PostgreSQL there's just one array type, regardless of dimensions.

So the code above looks like it should work; if it doesn't, please open an issue in https://github.com/npgsql/npgsql with a full code repro so we can investigate.

Rincho-M commented 1 year ago

Are there any plans to implement this feature, or is it the same as it was five years ago?

roji commented 1 year ago

@Rincho-M so far this hasn't received enough user feedback to warrant spending the time on it. What exact capability are you missing here?

Rincho-M commented 1 year ago

@roji I want to write and read a model with a two-dimensional array property. It seems to save correctly, but it gives an index-out-of-range exception when I'm trying to read it

roji commented 1 year ago

@Rincho-M saving and loading is generally supposed to work: see the following minimal code sample which works for me with 7.0; if you're seeing something different, please open a new issue with a minimal repro. Anything beyond that may not work though.

Code sample ```c# await ctx.Database.EnsureDeletedAsync(); await ctx.Database.EnsureCreatedAsync(); ctx.Blogs.Add(new() { Matrix = new[,] { { 1, 2 }, { 3, 4 } } }); await ctx.SaveChangesAsync(); ctx.ChangeTracker.Clear(); var blog = await ctx.Blogs.SingleAsync(); foreach (var i in blog.Matrix) { Console.WriteLine(i); } public class BlogContext : DbContext { public DbSet Blogs { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder .UseNpgsql("Host=localhost;Username=test;Password=test") .LogTo(Console.WriteLine, LogLevel.Information) .EnableSensitiveDataLogging(); } public class Blog { public int Id { get; set; } public string? Name { get; set; } public int[,] Matrix { get; set; } } ```
Rincho-M commented 1 year ago

@roji so I did some testing and it appears that it doesnt work only with char arrays. And not only with multidimensional arrays, but with one-dimentional ones too. I didnt find opened issue with this error so I will try to open one on this weekend

MattParkerDev commented 1 week ago

This appears to work with int[,] and double[,], at least with Postgres, but not enums, e.g. MyEnum[,]. Given that enums are just integers, is there an easy way to support this?

roji commented 1 week ago

Enums need to be value-converted internally, and the value converter that takes care of arrays doesn't support multidimensional ones yet.