DataObjects-NET / dataobjects-net

https://dataobjects.net
MIT License
61 stars 25 forks source link

Unable to translate exception on query grouped by LinqExtension field #362

Open letarak opened 9 months ago

letarak commented 9 months ago

DO 7.0.3

Fail to execute query that contains > 1 aggregate value by field with link access inside lambda expression

Code sample

using System.Linq.Expressions;
using DoTest;
using Microsoft.Data.SqlClient;
using Xtensive.Orm;
using Xtensive.Orm.Configuration;

internal class Program
{
    private static async Task Main(string[] args)
    {
        try
        {
            DbHelper.ExecuteNonQuery("DROP DATABASE [DO-Tests]");
        }
        catch (Exception)
        {
        }

        DbHelper.ExecuteNonQuery("CREATE DATABASE [DO-Tests]");

        var dc = new DomainConfiguration("sqlserver", new SqlConnectionStringBuilder(DbHelper.ConnectionString()).ToString());

        dc.Sessions.Add(new SessionConfiguration(WellKnown.Sessions.Default) { BatchSize = 25 });

        dc.Types.Register(typeof(TestEntity));
        dc.Types.Register(typeof(LinkEntity));

        Expression<Func<TestEntity, decimal?>> lambdaExpression = it => it.Link.Value;

        dc.LinqExtensions.Register(typeof(TestEntity).GetProperty(nameof(TestEntity.CalculatedValue)), lambdaExpression);

        dc.UpgradeMode = DomainUpgradeMode.Recreate;

        await using var d = await Domain.BuildAsync(dc);

        await using var s = await d.OpenSessionAsync();
        await using var t = await s.OpenTransactionAsync();

        // OK
        _ = s.Query.All<TestEntity>()
            .GroupBy(it => new
            {
                Name = it.Name == null
                    ? string.Empty
                    : it.Name
            }).Select(it => new
            {
                GroupCount = it.Count(),
                it.Key,
                ValueCalculated_Min = it.Min(e => e.CalculatedValue)
            }).Select(it => new
            {
                it.Key.Name,
                Id = Guid.NewGuid(),
                _Count = it.GroupCount,
                it.ValueCalculated_Min
            })
            .ToArray();

        // OK
        _ = s.Query.All<TestEntity>()
            .GroupBy(it => new
            {
                Name = it.Name == null
                    ? string.Empty
                    : it.Name
            }).Select(it => new
            {
                GroupCount = it.Count(),
                it.Key,
                ValueCalculated_Min = it.Max(e => e.CalculatedValue)
            }).Select(it => new
            {
                it.Key.Name,
                Id = Guid.NewGuid(),
                _Count = it.GroupCount,
                it.ValueCalculated_Min
            })
            .ToArray();

        // OK
        _ = s.Query.All<TestEntity>()
            .GroupBy(it => new
            {
                Name = it.Name == null
                    ? string.Empty
                    : it.Name
            }).Select(it => new
            {
                GroupCount = it.Count(),
                it.Key,
                ValueCalculated_Min = it.Min(e => e.CalculatedValue),
                ValueCalculated_Max = it.Max(e => e.CalculatedValue),
                Count = it.Count(e => e.Link != null)

            }).Select(it => new
            {
                it.Key.Name,
                Id = Guid.NewGuid(),
                _Count = it.GroupCount,
                it.ValueCalculated_Min,
                it.ValueCalculated_Max,
                it.Count
            })
            .ToArray();

        // OK
        _ = s.Query.All<TestEntity>()
            .GroupBy(it => new
            {
                Name = it.Name == null
                    ? string.Empty
                    : it.Name
            }).Select(it => new
            {
                GroupCount = it.Count(),
                it.Key,
                ValueCalculated_Min = it.Min(e => e.Link.Value - e.Value),
                ValueCalculated_Max = it.Max(e => e.Link.Value - e.Value)
            }).Select(it => new
            {
                it.Key.Name,
                Id = Guid.NewGuid(),
                _Count = it.GroupCount,
                it.ValueCalculated_Min,
                it.ValueCalculated_Max
            })
            .ToArray();

        // FAIL
        _ = s.Query.All<TestEntity>()
            .GroupBy(it => new
            {
                Name = it.Name == null
                    ? string.Empty
                    : it.Name
            }).Select(it => new
            {
                GroupCount = it.Count(),
                it.Key,
                ValueCalculated_Min = it.Min(e => e.CalculatedValue),
                ValueCalculated_Max = it.Max(e => e.CalculatedValue)
            }).Select(it => new
            {
                it.Key.Name,
                Id = Guid.NewGuid(),
                _Count = it.GroupCount,
                it.ValueCalculated_Min,
                it.ValueCalculated_Max
            })
            .ToArray();
    }

    [HierarchyRoot]
    public class LinkEntity : Entity
    {
        public LinkEntity(Session session) : base(session)
        {
        }

        [Key] [Field(Nullable = false)] public int Id { get; set; }

        [Field] public decimal? Value { get; set; }
    }

    [HierarchyRoot]
    public class TestEntity : Entity
    {
        public TestEntity(Session session) : base(session)
        {
        }

        [Key] [Field(Nullable = false)] public int Id { get; set; }

        [Field] public string Name { get; set; }

        [Field] public LinkEntity Link { get; set; }

        [Field(Nullable = true)] public decimal? Value { get; set; }

        public decimal? CalculatedValue { get; set; }
    }
}

Exception

Unhandled exception. Xtensive.Orm.QueryTranslationException: Unable to translate 'Query.All().GroupBy(it => new @<Name>((it.Name == null)
? .Empty
: it.Name)).Select(it => new @<GroupCount, Key, ValueCalculated_Min, ValueCalculated_Max>(
it.Count(),
it.Key,
it.Min(e => e.CalculatedValue),
it.Max(e => e.CalculatedValue)
)).Select(it => new @<Name, Id, _Count, ValueCalculated_Min, ValueCalculated_Max>(
it.Key.Name,
Guid.NewGuid(),
it.GroupCount,
it.ValueCalculated_Min,
it.ValueCalculated_Max
))' expression. See inner exception for details.
---> System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')
at System.Collections.Generic.List`1.get_Item(Int32 index)
at Xtensive.Orm.Rse.Providers.AggregateProvider..ctor(CompilableProvider source, Int32[] groupIndexes, AggregateColumnDescriptor[] columnDescriptors)
at Xtensive.Orm.Linq.Translator.VisitAggregate(Expression source, MethodInfo method, LambdaExpression argument, Boolean isRoot, MethodCallExpression expressionPart)
at Xtensive.Orm.Linq.Translator.VisitQueryableMethod(MethodCallExpression mc, QueryableMethodKind methodKind)
at Xtensive.Linq.QueryableVisitor.VisitMethodCall(MethodCallExpression mc)
at Xtensive.Orm.Linq.Translator.VisitMethodCall(MethodCallExpression mc)
at Xtensive.Linq.ExpressionVisitor`1.Visit(Expression e)
at Xtensive.Orm.Linq.Translator.Visit(Expression e)
at Xtensive.Orm.Linq.Translator.VisitNewExpressionArguments(NewExpression n)
at Xtensive.Orm.Linq.Translator.VisitNew(NewExpression newExpression)
at Xtensive.Linq.ExpressionVisitor`1.Visit(Expression e)
at Xtensive.Orm.Linq.Translator.Visit(Expression e)
at Xtensive.Orm.Linq.Translator.VisitLambda(LambdaExpression le)
at Xtensive.Orm.Linq.Translator.BuildProjection(LambdaExpression le)
at Xtensive.Orm.Linq.Translator.VisitSelect(Expression expression, LambdaExpression le)
at Xtensive.Orm.Linq.Translator.VisitQueryableMethod(MethodCallExpression mc, QueryableMethodKind methodKind)
at Xtensive.Linq.QueryableVisitor.VisitMethodCall(MethodCallExpression mc)
at Xtensive.Orm.Linq.Translator.VisitMethodCall(MethodCallExpression mc)
at Xtensive.Linq.ExpressionVisitor`1.Visit(Expression e)
at Xtensive.Orm.Linq.Translator.Visit(Expression e)
at Xtensive.Orm.Linq.Translator.VisitSequence(Expression sequenceExpression, Expression expressionPart)
at Xtensive.Orm.Linq.Translator.VisitSequence(Expression sequenceExpression)
at Xtensive.Orm.Linq.Translator.VisitSelect(Expression expression, LambdaExpression le)
at Xtensive.Orm.Linq.Translator.VisitQueryableMethod(MethodCallExpression mc, QueryableMethodKind methodKind)
at Xtensive.Linq.QueryableVisitor.VisitMethodCall(MethodCallExpression mc)
at Xtensive.Orm.Linq.Translator.VisitMethodCall(MethodCallExpression mc)
at Xtensive.Linq.ExpressionVisitor`1.Visit(Expression e)
at Xtensive.Orm.Linq.Translator.Visit(Expression e)
at Xtensive.Orm.Linq.Translator.Translate()
at Xtensive.Orm.Linq.QueryProvider.Translate(Expression expression, CompilerConfiguration compilerConfiguration)
--- End of inner exception stack trace ---
at Xtensive.Orm.Linq.QueryProvider.Translate(Expression expression, CompilerConfiguration compilerConfiguration)
at Xtensive.Orm.Linq.QueryProvider.Translate(Expression expression)
at Xtensive.Orm.Linq.QueryProvider.Execute[TResult](Expression expression, Func`4 runQuery)
at Xtensive.Orm.Linq.QueryProvider.ExecuteSequence[T](Expression expression)
at Xtensive.Orm.Linq.Queryable`1.GetEnumerator()
at System.Collections.Generic.LargeArrayBuilder`1.AddRange(IEnumerable`1 items)
at System.Collections.Generic.EnumerableHelpers.ToArray[T](IEnumerable`1 source)
at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
at Program.Main(String[] args) in /Users/anton.guschin/RiderProjects/DoTest/DoTest/Program.cs:line 108
at Program.Main(String[] args) in /Users/anton.guschin/RiderProjects/DoTest/DoTest/Program.cs:line 108
at Program.Main(String[] args) in /Users/anton.guschin/RiderProjects/DoTest/DoTest/Program.cs:line 108
at Program.Main(String[] args) in /Users/anton.guschin/RiderProjects/DoTest/DoTest/Program.cs:line 108
at Program.<Main>(String[] args)