linq2db / linq2db.EntityFrameworkCore

Bring power of Linq To DB to Entity Framework Core projects
MIT License
462 stars 38 forks source link

PostgreSQL Unnest not working? #217

Closed Evengard closed 2 years ago

Evengard commented 2 years ago

Let's assume a table in PostgreSQL defined with EF Core:

public class Fact
{
        public Guid FactId { get; set; }
        public string[] Tags { get; set; }
}

And now let's try to do any query with unnest in it...

var l2c = ctx.CreateLinqToDbContext();
var query = await (from f in ctx.Facts
                 from t in l2c.Unnest(f.Tags)
                 select t).ToArrayAsyncLinqToDB();

The exception is:

System.InvalidOperationException
variable 'f' of type 'L2DbBugRepro.Fact' referenced from scope '', but it is not defined
   at System.Linq.Expressions.Compiler.VariableBinder.Reference(ParameterExpression node, VariableStorageKind storage)
   at System.Linq.Expressions.Compiler.VariableBinder.VisitParameter(ParameterExpression node)
   at System.Linq.Expressions.ParameterExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.Compiler.VariableBinder.Visit(Expression node)
   at System.Linq.Expressions.ExpressionVisitor.Visit(ReadOnlyCollection`1 nodes)
   at System.Linq.Expressions.Compiler.VariableBinder.VisitLambda[T](Expression`1 node)
   at System.Linq.Expressions.Expression`1.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.Compiler.VariableBinder.Visit(Expression node)
   at System.Linq.Expressions.Compiler.LambdaCompiler.Compile(LambdaExpression lambda)
   at System.Linq.Expressions.LambdaExpression.Compile()
   at LinqToDB.EntityFrameworkCore.LinqToDBForEFToolsImplDefault.EvaluateExpression(Expression expr)
   at LinqToDB.EntityFrameworkCore.LinqToDBForEFToolsImplDefault.EvaluateExpression(Expression expr)
   at System.Linq.Enumerable.SelectIListIterator`2.ToArray()
   at LinqToDB.EntityFrameworkCore.LinqToDBForEFToolsImplDefault.<>c__DisplayClass39_0.<TransformExpression>g__LocalTransform|0(Expression e)
   at LinqToDB.EntityFrameworkCore.LinqToDBForEFToolsImplDefault.<>c__DisplayClass39_0.<TransformExpression>b__1(Expression e)
   at LinqToDB.Expressions.Extensions.<>c.<Transform>b__16_0(Func`2 f, Expression e)
   at LinqToDB.Expressions.TransformInfoVisitor`1.Transform(Expression expr)
   at LinqToDB.Expressions.TransformInfoVisitor`1.Transform(Expression expr)
   at LinqToDB.Expressions.TransformInfoVisitor`1.Transform(Expression expr)
   at LinqToDB.Expressions.TransformInfoVisitor`1.Transform[T](IList`1 source)
   at LinqToDB.Expressions.TransformInfoVisitor`1.Transform(Expression expr)
   at LinqToDB.Expressions.Extensions.Transform(Expression expr, Func`2 func)
   at LinqToDB.EntityFrameworkCore.LinqToDBForEFToolsImplDefault.TransformExpression(Expression expression, IDataContext dc, DbContext ctx, IModel model)
   at LinqToDB.EntityFrameworkCore.LinqToDBForEFTools.TransformExpression(Expression expression, IDataContext dc, DbContext ctx, IModel model)
   at LinqToDB.EntityFrameworkCore.LinqToDBForEFToolsDataConnection.ProcessExpression(Expression expression)
   at LinqToDB.Linq.Query`1.GetQuery(IDataContext dataContext, Expression& expr, Boolean& dependsOnParameters)
   at LinqToDB.Linq.ExpressionQuery`1.GetQuery(Expression& expression, Boolean cache, Boolean& dependsOnParameters)
   at LinqToDB.Linq.ExpressionQuery`1.<GetForEachAsync>d__19.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter.GetResult()
   at LinqToDB.AsyncExtensions.<ToArrayAsync>d__9`1.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()

I thought something is wrong because I'm using a different l2d context, so I tried this:


var l2c = ctx.CreateLinqToDbContext();
var query = await (from f in l2c.GetTable<Fact>()
                 from t in l2c.Unnest(f.Tags)
                 select t).ToArrayAsyncLinqToDB();

But no, the error was the same.

sdanyliv commented 2 years ago

Thanks for reporting will check tomorrow. Anyway show your configuration for Model.

Evengard commented 2 years ago

The full repro as follows:

Repro ```csharp using LinqToDB; using LinqToDB.DataProvider.PostgreSQL; using LinqToDB.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using System.ComponentModel.DataAnnotations; namespace L2DbBugRepro { class Program { static List storage = new List(); static async Task Main(string[] args) { LinqToDBForEFTools.Initialize(); await using (var ctx = new BlogContext()) { await ctx.Database.EnsureDeletedAsync(); await ctx.Database.EnsureCreatedAsync(); ctx.Facts.Add(new Fact { FactId = Guid.NewGuid(), Tags = new[]{ "one", "two" }, }); await ctx.SaveChangesAsync(); } await using (var ctx = new BlogContext()) { var l2c = ctx.CreateLinqToDbContext(); var query = await (from f in l2c.GetTable() from t in l2c.Unnest(f.Tags) select t).ToArrayAsyncLinqToDB(); } } } public class BlogContext : DbContext { public DbSet Facts { get; set; } static ILoggerFactory ContextLoggerFactory => LoggerFactory.Create(b => b.AddConsole().AddFilter("", LogLevel.Information)); protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder .UseNpgsql(@"Host=localhost;Username=root;Password=rootpass;Database=Testing") .EnableSensitiveDataLogging() .UseLoggerFactory(ContextLoggerFactory); protected override void OnModelCreating(ModelBuilder modelBuilder) { } } public class Fact { [Key] public Guid FactId { get; set; } public string[] Tags { get; set; } } } ```
sdanyliv commented 2 years ago

Sorry for delay, confirmed bug. Probably will release fix today.

Evengard commented 2 years ago

Thanks, will wait for the release.

Evengard commented 2 years ago

@sdanyliv I'm terribly sorry to bother you again, but I tried to compile the latest master branch... Now on CreateLinqToDbContext() I'm getting this exception:

System.MissingMethodException: 'Method not found: 'System.Type Microsoft.EntityFrameworkCore.Metadata.IPropertyBase.get_ClrType()'.'
   at LinqToDB.EntityFrameworkCore.LinqToDBForEFToolsImplDefault.<>c.<DefineConvertors>b__14_2(IProperty p)
   at System.Linq.Enumerable.SelectEnumerableIterator`2.MoveNext()
   at System.Linq.Enumerable.SelectManySingleSelectorIterator`2.MoveNext()
   at System.Collections.Generic.HashSet`1.UnionWith(IEnumerable`1 other)
   at System.Collections.Generic.HashSet`1..ctor(IEnumerable`1 collection, IEqualityComparer`1 comparer)
   at System.Linq.Enumerable.DistinctIterator`1.ToArray()
   at LinqToDB.EntityFrameworkCore.LinqToDBForEFToolsImplDefault.DefineConvertors(MappingSchema mappingSchema, IModel model, IValueConverterSelector convertorSelector)
   at LinqToDB.EntityFrameworkCore.LinqToDBForEFToolsImplDefault.CreateMappingSchema(IModel model, IMetadataReader metadataReader, IValueConverterSelector convertorSelector)
   at LinqToDB.EntityFrameworkCore.LinqToDBForEFToolsImplDefault.<>c__DisplayClass17_0.<GetMappingSchema>b__0(ICacheEntry e)
   at Microsoft.Extensions.Caching.Memory.CacheExtensions.GetOrCreate[TItem](IMemoryCache cache, Object key, Func`2 factory)
   at LinqToDB.EntityFrameworkCore.LinqToDBForEFToolsImplDefault.GetMappingSchema(IModel model, IMetadataReader metadataReader, IValueConverterSelector convertorSelector)
   at LinqToDB.EntityFrameworkCore.LinqToDBForEFTools.GetMappingSchema(IModel model, IInfrastructure`1 accessor)
   at LinqToDB.EntityFrameworkCore.LinqToDBForEFTools.CreateLinqToDbContext(DbContext context, IDbContextTransaction transaction)
   at L2DbBugRepro.Program.<Main>d__1.MoveNext() in I:\dev\repro\repro\Program.cs:line 33
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at L2DbBugRepro.Program.<Main>d__1.MoveNext() in I:\dev\repro\repro\Program.cs:line 37

I've used the same repro as posted above, updated Linq2Db to the latest Nuget (non-preview) version, still having this problem.

Evengard commented 2 years ago

Never mind, after digging into the project structure a little bit got it working. Thanks, seems to work pretty well!