DO 5.0.17

namespace Sample
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    using NUnit.Framework;
    using Xtensive.Orm;
    using Xtensive.Orm.Configuration;

    internal class Program
    {
        private static void Main(string[] args)
        {
            var dc = new DomainConfiguration("sqlserver",
                "Data Source=.; Initial Catalog=DO40-Tests; Integrated Security=True;");

            dc.Types.Register(typeof(TestEntity));
            dc.Types.Register(typeof(TestEntity2));
            dc.Types.Register(typeof(ListEntity));

            dc.UpgradeMode = DomainUpgradeMode.Recreate;

            Expression<Func<TestEntity, TestEntity2>> exp = e => e.List.FirstOrDefault().Link; 
            Expression<Func<TestEntity, TestEntity2>> exp2 = e => e.LinkOnList.Link;

            dc.LinqExtensions.Register(typeof(TestEntity).GetProperty(nameof(TestEntity.VirtualList)), exp);
            dc.LinqExtensions.Register(typeof(TestEntity).GetProperty(nameof(TestEntity.VirtualLink)), exp2);

            using (var d = Domain.Build(dc))
            {
                using (var s = d.OpenSession())
                using (s.Activate())
                using (var t = s.OpenTransaction())
                {
                    var item = new TestEntity(s) { String = "test" };
                    var item2 = new TestEntity2(s) { String = "test" };
                    var list = new ListEntity(s) { String = "test", Owner = item, Link = item2 };
                    item.LinkOnList = list;

                    new TestEntity(s) { String = "test2" };

                    t.Complete();
                }

                using (var s = d.OpenSession())
                using (s.Activate())
                using (var t = s.OpenTransaction())
                {
                    var ids = new List<Guid> { Guid.NewGuid() };

                    // OK
                    Query.All<TestEntity>()
                        .Where(e => e.Link != null && e.Link.Id != Guid.NewGuid())
                        .OrderBy(e => e.Link)
                        .ToArray();

                    // OK
                    Query.All<TestEntity>()
                        .Where(e => e.VirtualLink != null && e.VirtualLink.Id != Guid.NewGuid())
                        .OrderBy(e => e.VirtualLink)
                        .ToArray();

                    // OK
                    Query.All<TestEntity>()
                        .Where(e => e.VirtualList != null && e.VirtualList.Id != Guid.NewGuid())
                        .OrderBy(e => e.VirtualList)
                        .ToArray();

                    // OK
                    Query.All<TestEntity>()
                        .Where(e => e.Link != null && !ids.Contains(e.Link.Id))
                        .OrderBy(e => e.Link)
                        .ToArray();

                    // OK
                    Query.All<TestEntity>()
                        .Where(e => e.VirtualLink != null && !ids.Contains(e.VirtualLink.Id))
                        .OrderBy(e => e.VirtualLink)
                        .ToArray();

                    // OK
                    Query.All<TestEntity>()
                        .Where(e => e.VirtualList != null && !ids.Contains(e.VirtualList.Id))
                        .OrderBy(e => e.VirtualList)
                        .ToArray();

                    // OK
                    Query.All<TestEntity>()
                        .Where(e => e.Link.Id != Guid.NewGuid())
                        .OrderBy(e => e.Link)
                        .ToArray();

                    // OK
                    Query.All<TestEntity>()
                        .Where(e => e.VirtualLink.Id != Guid.NewGuid())
                        .OrderBy(e => e.VirtualLink)
                        .ToArray();

                    // OK
                    Query.All<TestEntity>()
                        .Where(e => e.VirtualList.Id != Guid.NewGuid())
                        .OrderBy(e => e.VirtualList)
                        .ToArray();

                    // OK
                    Query.All<TestEntity>()
                        .Where(e => !ids.Contains(e.Link.Id))
                        .OrderBy(e => e.Link)
                        .ToArray();

                    // OK
                    Query.All<TestEntity>()
                        .Where(e => !ids.Contains(e.VirtualLink.Id))
                        .OrderBy(e => e.VirtualLink)
                        .ToArray();

                    // FAIL
                    // Generate INNER JOIN on [Program.TestEntity2], when 'List' empty, VirtualList = null and row excluded from select
                    Assert.AreEqual(2, Query.All<TestEntity>()
                        .Select(e => new { e.VirtualList.String, e.VirtualList.Id })
                        .Count());

                    // FAIL
                    // Unable to translate
                    Query.All<TestEntity>()
                        .Where(e => !ids.Contains(e.VirtualList.Id))
                        .OrderBy(e => e.VirtualList)
                        .ToArray();

                    t.Complete();
                }
            }
        }

        [HierarchyRoot]
        public class TestEntity : Entity
        {
            /// <summary>Initializes a new instance of this class.</summary>
            /// <param name="session">The session.</param>
            public TestEntity(Session session) : base(session)
            {
            }

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

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

            [Field]
            public ListEntity LinkOnList { get; set; }

            [Field]
            [Association(PairTo = "Owner")]
            public EntitySet<ListEntity> List { get; set; }

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

            public TestEntity2 VirtualList { get; set; }

            public TestEntity2 VirtualLink { get; set; }
        }

        [HierarchyRoot]
        public class ListEntity : Entity
        {
            /// <summary>Initializes a new instance of this class.</summary>
            /// <param name="session">The session.</param>
            public ListEntity(Session session) : base(session)
            {
            }

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

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

            [Field(Nullable = false)]
            public TestEntity Owner { get; set; }

            [Field(Nullable = false)]
            public TestEntity2 Link { get; set; }
        }

        [HierarchyRoot]
        public class TestEntity2 : Entity
        {
            /// <summary>Initializes a new instance of this class.</summary>
            /// <param name="session">The session.</param>
            public TestEntity2(Session session) : base(session)
            {
            }

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

            [Field(Nullable = false)]
            public string String { get; set; }
        }
    }
}

asked Sep 12 '18 at 07:53

Gushchin%20Anton's gravatar image

Gushchin Anton
9121921


One Answer:

Hello Anton,

Thank you for the report, we will figure out what is the problem

answered Sep 20 '18 at 04:03

Alexey%20Kulakov's gravatar image

Alexey Kulakov
73215

Anton, Could you explain what you expect from the case when INNER JOIN is generated?

(Oct 17 '18 at 05:29) Alexey Kulakov Alexey%20Kulakov's gravatar image
  1. Expect that the row would not be excluded from result, because filters has not been applied
  2. For selected fields expect default value (this is current behaviour for empty links and I think should be default behaviour for empty list)

When I execute something like this

e.Link.SomeLink.Id

and when Link or SomeLink = null, return value would be Guid.Empty

(Dec 06 '18 at 23:30) Gushchin Anton Gushchin%20Anton's gravatar image

By the way We do not want missing InnerJoin for required links in other circumstances Because, link cannot be null :)

(Dec 06 '18 at 23:33) Gushchin Anton Gushchin%20Anton's gravatar image
Your answer
Please start posting your answer anonymously - your answer will be saved within the current session and published after you log in or create a new account. Please try to give a substantial answer, for discussions, please use comments and please do remember to vote (after you log in)!
toggle preview

powered by OSQA