Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2.2 breaks our queries and generates complex SQL #72

Open
sebastianrosch opened this issue Jul 27, 2016 · 18 comments
Open

2.2 breaks our queries and generates complex SQL #72

sebastianrosch opened this issue Jul 27, 2016 · 18 comments
Assignees

Comments

@sebastianrosch
Copy link

We accidentally updated DynamicFilters to v2.2 through Nuget and all our many of our queries did no longer work. We are only applying an IsDeleted filter globally and with version 1.4, the queries only contained an additional Where clause, as was expected.

Now, with version 2.2, the queries are way longer and more complex, with several Outer Apply statements and the results are wrong compared to v1.4.

I will post specific examples once I had more time to investigate.

@sebastianrosch
Copy link
Author

Here is on example in 1.4, where the only addition done by DynamicFilters is the DeletedOn = NULL in the Where clause:

SELECT 
    [Extent1].[Id] AS [Id], 
    [Extent1].[Url] AS [Url], 
    [Extent1].[Title] AS [Title], ...
    FROM   (SELECT [Var_11].[Id] AS [Id], [Var_11].[Url] AS [Url], [Var_11].[Title] AS [Title], [Var_11].[Order] AS [Order], [Var_11].[ParentId] AS [ParentId], [Var_11].[IsActive] AS [IsActive], ..... [Var_11].[ModuleId] AS [ModuleId], [Var_11].[CreatedOn] AS [CreatedOn], [Var_11].[CreatedBy] AS [CreatedBy], [Var_11].[UpdatedOn] AS [UpdatedOn], [Var_11].[UpdatedBy] AS [UpdatedBy], [Var_11].[DeletedOn] AS [DeletedOn], [Var_11].[DeletedBy] AS [DeletedBy]
        FROM [dbo].[ConfigMenuItem] AS [Var_11]
        WHERE ([Var_11].[DeletedOn] IS NULL)  ) AS [Extent1]
    LEFT OUTER JOIN  (SELECT [Var_12].[Id] AS [Id], [Var_12].[Url] AS [Url], [Var_12].[Title] AS [Title], [Var_12].[Order] AS [Order], [Var_12].[ParentId] AS [ParentId], [Var_12].[IsActive] AS [IsActive],...... [Var_12].[ModuleId] AS [ModuleId], [Var_12].[CreatedOn] AS [CreatedOn], [Var_12].[CreatedBy] AS [CreatedBy], [Var_12].[UpdatedOn] AS [UpdatedOn], [Var_12].[UpdatedBy] AS [UpdatedBy], [Var_12].[DeletedOn] AS [DeletedOn], [Var_12].[DeletedBy] AS [DeletedBy]
        FROM [dbo].[ConfigMenuItem] AS [Var_12]
        WHERE ([Var_12].[DeletedOn] IS NULL)  ) AS [Extent2] ON [Extent1].[ParentId] = [Extent2].[Id]
    WHERE (1 = [Extent1].[IsActive]) AND (([Extent2].[Id] IS NULL) OR (([Extent2].[Id] IS NOT NULL) AND (1 = [Extent2].[IsActive])))

Here is the same query with v2.2:

SELECT 
    [Project7].[Id] AS [Id], 
    [Project7].[Url] AS [Url], 
    [Project7].[Title] AS [Title], .....
    [Element4].[Id] AS [Id1], 
    [Element4].[Url] AS [Url1], 
    [Element4].[Title] AS [Title1], ....
    FROM   (SELECT 
        [Project5].[Id] AS [Id], 
        [Project5].[Url] AS [Url], 
        [Project5].[Title] AS [Title], ....
        FROM   (SELECT 
            [Project3].[Id] AS [Id], 
            [Project3].[Url] AS [Url], 
            [Project3].[Title] AS [Title], .....
            [Project3].[Id1] AS [Id1], 
            [Element2].[Id] AS [Id2]
            FROM   (SELECT 
                [Project1].[Id] AS [Id], 
                [Project1].[Url] AS [Url], 
                [Project1].[Title] AS [Title], .....
                [Element1].[Id] AS [Id1]
                FROM   (SELECT 
                    [Extent1].[Id] AS [Id], 
                    [Extent1].[Url] AS [Url], 
                    [Extent1].[Title] AS [Title], ......
                    FROM [dbo].[ConfigMenuItem] AS [Extent1]
                    WHERE ([Extent1].[DeletedOn] IS NULL)  ) AS [Project1]
                OUTER APPLY  (SELECT TOP (1) 
                    [Extent2].[Id] AS [Id], 
                    [Extent2].[ParentId] AS [ParentId], 
                    [Extent2].[DeletedOn] AS [DeletedOn]
                    FROM [dbo].[ConfigMenuItem] AS [Extent2]
                    WHERE (([Extent2].[DeletedOn] IS NULL) ) AND ([Project1].[Id] = [Extent2].[ParentId]) ) AS [Element1] ) AS [Project3]
            OUTER APPLY  (SELECT TOP (1) 
                [Extent3].[Id] AS [Id], 
                [Extent3].[ParentId] AS [ParentId], 
                [Extent3].[DeletedOn] AS [DeletedOn]
                FROM [dbo].[ConfigMenuItem] AS [Extent3]
                WHERE (([Extent3].[DeletedOn] IS NULL) ) AND ([Project3].[Id] = [Extent3].[ParentId]) ) AS [Element2] ) AS [Project5]
        OUTER APPLY  (SELECT TOP (1) 
            [Extent4].[ParentId] AS [ParentId], 
            [Extent4].[IsActive] AS [IsActive], 
            [Extent4].[DeletedOn] AS [DeletedOn]
            FROM [dbo].[ConfigMenuItem] AS [Extent4]
            WHERE (([Extent4].[DeletedOn] IS NULL) ) AND ([Project5].[Id] = [Extent4].[ParentId]) ) AS [Element3]
        WHERE (1 = [Project5].[IsActive]) AND (([Project5].[Id1] IS NULL) OR (([Project5].[Id2] IS NOT NULL) AND (1 = [Element3].[IsActive]))) ) AS [Project7]
    OUTER APPLY  (SELECT TOP (1) 
        [Extent5].[Id] AS [Id], 
        [Extent5].[Url] AS [Url], 
        [Extent5].[Title] AS [Title], ......
        FROM [dbo].[ConfigMenuItem] AS [Extent5]
        WHERE (([Extent5].[DeletedOn] IS NULL) ) AND ([Project7].[Id] = [Extent5].[ParentId]) ) AS [Element4]

@jcachat
Copy link
Collaborator

jcachat commented Jul 27, 2016

Please post the models and filters. I can't get enough information from only the sql query. Also your queries do not show any Dynamic Filter parameters at all so from looking at it right now, I would have to conclude that Dynamic Filters is not involved. So if that is not the actual query captured by SQL Profiler, please post that as well.

@stormsjr1
Copy link

stormsjr1 commented Sep 13, 2016

We have what appears to be a similar problem with v2.3.0. In our case, we have a self-referencing entity defined as follows:

public class UserAccount : IAmSoftDeleted
{
    public string FirstName { get; set; }
    public DateTime? LastLoggedInDate { get; set; }
    public string LastName { get; set; }
    public string MiddleName { get; set; }
    public DateTime CreateDate { get; set; }
    public UserAccount CreatorUser { get; set; }
    public int? CreatorUserId { get; set; }
    public DateTime? DeleteDate { get; set; }
    public UserAccount DeleteUser { get; set; }
    public int? DeleteUserId { get; set; }
    public bool IsDeleted { get; set; }
    public DateTime? LastModifiedDate { get; set; }
    public UserAccount LastModifiedUser { get; set; }
    public int? LastModifiedUserId { get; set; }
}

The filter is as follows:

modelBuilder.Filter("SoftDelete", (IAmSoftDeleted d) => d.IsDeleted, false);

In v1.4.11, the generated SQL was...

exec sp_executesql N'SELECT 
    [Extent1].[Id] AS [Id], 
    [Extent1].[CreatorUserId] AS [CreatorUserId], 
    [Extent1].[CreateDate] AS [CreateDate], 
    [Extent1].[LastModifiedUserId] AS [LastModifiedUserId], 
    [Extent1].[LastModifiedDate] AS [LastModifiedDate], 
    [Extent1].[UserName] AS [UserName], 
    [Extent1].[LastName] AS [LastName], 
    [Extent2].[UserName] AS [UserName1], 
    [Extent1].[FirstName] AS [FirstName], 
    [Extent3].[UserName] AS [UserName2]
    FROM    (SELECT [Var_1].[Id] AS [Id], [Var_1].[FirstName] AS [FirstName], [Var_1].[LastName] AS [LastName], [Var_1].[CreateDate] AS [CreateDate], [Var_1].[CreatorUserId] AS [CreatorUserId], [Var_1].[LastModifiedDate] AS [LastModifiedDate], [Var_1].[LastModifiedUserId] AS [LastModifiedUserId], [Var_1].[UserName] AS [UserName]
        FROM [dbo].[UserAccounts] AS [Var_1]
        WHERE ([Var_1].[IsDeleted] = @DynamicFilterParam_000001)  ) AS [Extent1]
    LEFT OUTER JOIN  (SELECT [Var_2].[Id] AS [Id], [Var_2].[UserName] AS [UserName]
        FROM [dbo].[UserAccounts] AS [Var_2]
        WHERE ([Var_2].[IsDeleted] = @DynamicFilterParam_000001)  ) AS [Extent2] ON [Extent1].[LastModifiedUserId] = [Extent2].[Id]
    LEFT OUTER JOIN  (SELECT [Var_3].[Id] AS [Id], [Var_3].[UserName] AS [UserName]
        FROM [dbo].[UserAccounts] AS [Var_3]
        WHERE ([Var_3].[IsDeleted] = @DynamicFilterParam_000001)  ) AS [Extent3] ON [Extent1].[CreatorUserId] = [Extent3].[Id]',N'@DynamicFilterParam_000001 bit,@DynamicFilterParam_000002 bit',@DynamicFilterParam_000001=0,@DynamicFilterParam_000002=NULL

In 2.3.0, it is...

exec sp_executesql N'SELECT 
    [Project2].[Id] AS [Id], 
    [Project2].[CreatorUserId] AS [CreatorUserId], 
    [Project2].[CreateDate] AS [CreateDate], 
    [Project2].[LastModifiedUserId] AS [LastModifiedUserId], 
    [Project2].[LastModifiedDate] AS [LastModifiedDate], 
    [Project2].[UserName] AS [UserName], 
    [Project2].[LastName] AS [LastName], 
    [Project2].[UserName1] AS [UserName1], 
    [Project2].[FirstName] AS [FirstName], 
    [Element2].[UserName] AS [UserName2]
    FROM   (SELECT 
        [Filter1].[Id] AS [Id], 
        [Filter1].[FirstName] AS [FirstName], 
        [Filter1].[LastName] AS [LastName], 
        [Filter1].[CreateDate] AS [CreateDate], 
        [Filter1].[CreatorUserId] AS [CreatorUserId], 
        [Filter1].[LastModifiedDate] AS [LastModifiedDate], 
        [Filter1].[LastModifiedUserId] AS [LastModifiedUserId], 
        [Filter1].[UserName] AS [UserName], 
        [Element1].[UserName] AS [UserName1]
        FROM   (SELECT [Extent1].[Id] AS [Id], [Extent1].[FirstName] AS [FirstName], [Extent1].[LastName] AS [LastName], [Extent1].[CreateDate] AS [CreateDate], [Extent1].[CreatorUserId] AS [CreatorUserId], [Extent1].[LastModifiedDate] AS [LastModifiedDate], [Extent1].[LastModifiedUserId] AS [LastModifiedUserId], [Extent1].[UserName] AS [UserName]
            FROM [dbo].[UserAccounts] AS [Extent1]
            WHERE ([Extent1].[IsDeleted] = @DynamicFilterParam_000001)  ) AS [Filter1]
        OUTER APPLY  (SELECT TOP (1) 
            [Extent2].[IsDeleted] AS [IsDeleted], 
            [Extent2].[LastModifiedUserId] AS [LastModifiedUserId], 
            [Extent2].[UserName] AS [UserName]
            FROM [dbo].[UserAccounts] AS [Extent2]
            WHERE (([Extent2].[IsDeleted] = @DynamicFilterParam_000001) ) AND ([Filter1].[Id] = [Extent2].[LastModifiedUserId]) ) AS [Element1] ) AS [Project2]
    OUTER APPLY  (SELECT TOP (1) 
        [Extent3].[CreatorUserId] AS [CreatorUserId], 
        [Extent3].[IsDeleted] AS [IsDeleted], 
        [Extent3].[UserName] AS [UserName]
        FROM [dbo].[UserAccounts] AS [Extent3]
        WHERE (([Extent3].[IsDeleted] = @DynamicFilterParam_000001) ) AND ([Project2].[Id] = [Extent3].[CreatorUserId]) ) AS [Element2]',N'@DynamicFilterParam_000001 bit,@DynamicFilterParam_000002 bit',@DynamicFilterParam_000001=0,@DynamicFilterParam_000002=NULL

([Filter1].[Id] = [Extent2].[LastModifiedUserId]) should be ([Filter1].[LastModifiedUserId] = [Extent2].[Id])

and

([Project2].[Id] = [Extent3].[CreatorUserId]) should be ([Project2].[CreatorUserId] = [Extent3].[Id])

@RubenDelange
Copy link

RubenDelange commented Oct 17, 2016

I updated from v1.4.11 to v2.4.0 the other day and suddenly a (only 1) very slow SQL query popped up.

query_1.4.11.txt
query_2.4.0.txt (very slow)

In code, I use a "where Id = 'guid'" filter and I map the EF entity to a DTO using AutoMapper's IQueryable extension. Version of AutoMapper is 4.1.1 in both scenario's above.
DynamicFilters is used to add a SoftDelete filter to every query.

Target DTO in this case is rather complex due to it's nested structure and 2 properties pointing to the same SQL table, but with v1.4.11 I never had any performance issues with it.

Target dto.txt

If I compare the generated query in v2.4.0, I notice the use of "OUTER APPLY" statements. The v1.4.11 query doesn't have those. The example that @stormsjr1 posted is also an example of this behavior.

Anyway, if I revert back to v1.4.11 all is good again, so it looks like the v2.4.0 version has something to do with it.

@ScarlettCode
Copy link

filtering perf

Just want to add we seeing the same thing. From left to right. No filtering. 1.4 filtering. 2.x filtering. That's getting back a single record but you can definitely see the perf decrease from 1.4 to 2. Happy to supply more info if needed.

@ArnaudB88
Copy link

Hi,
Any updates about this issue yet?
We also have this issue in some of our projects (using this package for a soft delete filter).
I tried to disable the filter before executing the query but is has no effect, the generated sql query is extremely long (11000 line query vs 300 line query).
Because of this behaviour we are forced to move back from v2.7 to v1.4 for several big projects (missing other nice functionality).
Any update would be welcome :)

Thx!

@ArnaudB88
Copy link

Hello,
Is it possible to fix this issue please? The latest release version (2.10) still has this bug which causes complex queries. We are forced to keep using the old v1.4 version because we don't want those complex queries on our production systems.
Thanks!

@JonathanMagnan JonathanMagnan self-assigned this Sep 20, 2017
@JonathanMagnan
Copy link
Member

Hello @ArnaudB88 ,

Thank you, I will try to reproduce this issue in the next few days and better understand what in the code has been modified to causing this issue.

Best Regards,

Jonathan

@JonathanMagnan
Copy link
Member

Hello guys,

Do anyone of you could help me to reproduce this issue

Here is a code a tried to reproduce it with self-hierarchy as @stormsjr1 reported.

If I could get a working example that causes the problem, I will be able to continue the investigation about the issue

using System.Data.Entity;
using System.Linq;
using System.Windows.Forms;
using EntityFramework.DynamicFilters;
namespace Z.Lab
{
    public partial class Form_Request_FilterLongQuery : Form
    {
        public Form_Request_FilterLongQuery()
        {
            InitializeComponent();

            // CLEAR
            using (var ctx = new EntityContext())
            {
                ctx.UserAccounts.RemoveRange(ctx.UserAccounts);
                ctx.SaveChanges();
            }

            // SEED
            using (var ctx = new EntityContext())
            {
                ctx.UserAccounts.Add(new UserAccount() { ColumnInt = 1, IsDeleted = false });
                ctx.UserAccounts.Add(new UserAccount() { ColumnInt = 2, IsDeleted = true });
                ctx.SaveChanges();
            }

            // TEST
            using (var ctx = new EntityContext())
            {
                var list = ctx.UserAccounts
                    .Include(x => x.CreatorUser)
                    .Include(x => x.DeleteUser)
                    .Include(x => x.LastModifiedUser)
                    .ToList();
            }
        }

        public class EntityContext : DbContext
        {
            public EntityContext() : base("CodeFirstEntities")
            {
            }

            public DbSet<UserAccount> UserAccounts { get; set; }

            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                modelBuilder.Filter("SoftDelete", (IAmSoftDeleted d) => d.IsDeleted, false);

                modelBuilder.Types().Configure(x => x.ToTable(GetType().DeclaringType != null ? GetType().DeclaringType.FullName.Replace(".", "_") + "_" + x.ClrType.Name : ""));

                base.OnModelCreating(modelBuilder);
            }
        }



        public interface IAmSoftDeleted
        {
            bool IsDeleted { get; set; }
        }

        public class UserAccount : IAmSoftDeleted
        {
            public int ID { get; set; }
            public int ColumnInt { get; set; }
            public bool IsDeleted { get; set; }
            public int? CreatorUserID { get; set; }
            public UserAccount CreatorUser { get; set; }
            public int? DeleteUserID { get; set; }
            public UserAccount DeleteUser { get; set; }
            public int? LastModifiedUserID { get; set; }
            public UserAccount LastModifiedUser { get; set; }
        }
    }
}

Best Regards,

Jonathan

@stormsjr1
Copy link

I was able to recreate the issue in 2.3.0 with the following:

namespace App
{
    public interface IAmSoftDeleted
    {
        bool IsDeleted { get; set; }
    }

    public class EntityContext : DbContext
    {
        public EntityContext() : base("CodeFirstEntities")
        {
            Database.SetInitializer(new DropCreateDatabaseAlways<EntityContext>());
        }

        public DbSet<UserAccount> UserAccounts { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Filter("SoftDelete", (IAmSoftDeleted d) => d.IsDeleted, false);
            base.OnModelCreating(modelBuilder);
        }
    }

    public class UserAccount : IAmSoftDeleted
    {
        public DateTime CreateDate { get; set; }
        public UserAccount CreatorUser { get; set; }
        public int? CreatorUserId { get; set; }
        public DateTime? DeleteDate { get; set; }
        public UserAccount DeleteUser { get; set; }
        public int? DeleteUserId { get; set; }
        public string FirstName { get; set; }
        public int Id { get; set; }
        public bool IsDeleted { get; set; }
        public DateTime? LastLoggedInDate { get; set; }
        public DateTime? LastModifiedDate { get; set; }
        public UserAccount LastModifiedUser { get; set; }
        public int? LastModifiedUserId { get; set; }
        public string LastName { get; set; }
        public string MiddleName { get; set; }
        public string UserName { get; set; }
    }

    internal class Program
    {
        private static void Main(string[] args)
        {
            var accounts = new List<UserAccount>
            {
                new UserAccount {CreateDate = DateTime.Now, CreatorUserId = 1},
                new UserAccount {CreateDate = DateTime.Now, CreatorUserId = 2},
                new UserAccount {CreateDate = DateTime.Now, CreatorUserId = 1, LastModifiedUserId = 1}
            };

            // SEED
            using (var ctx = new EntityContext())
            {
                ctx.UserAccounts.AddRange(accounts);
                ctx.SaveChanges();
            }

            // TEST
            using (var ctx = new EntityContext())
            {
                var list = ctx.UserAccounts
                    .Include(x => x.CreatorUser)
                    .Include(x => x.LastModifiedUser)
                    .Select(x => new
                    {
                        x.Id,
                        x.CreatorUser,
                        x.CreatorUserId,
                        x.CreateDate,
                        x.LastModifiedUserId,
                        x.LastModifiedUser,
                        x.UserName,
                        x.FirstName,
                        x.LastName
                    })
                    .ToList();

                foreach (var account in list)
                {
                    if (account.LastModifiedUserId != account.LastModifiedUser?.Id)
                    {
                        Console.WriteLine($"User {account.Id} was last modified by [{account.LastModifiedUserId}] but query returned [{account.LastModifiedUser?.Id}]");
                    }
                }

                Console.ReadLine();
            }
        }
    }
}

In v2.3.0, the generated query is as follows:

exec sp_executesql N'SELECT 
    [Project3].[Id] AS [Id], 
    [Project3].[Id1] AS [Id1], 
    [Project3].[CreateDate1] AS [CreateDate], 
    [Project3].[CreatorUserId1] AS [CreatorUserId], 
    [Project3].[DeleteDate] AS [DeleteDate], 
    [Project3].[DeleteUserId] AS [DeleteUserId], 
    [Project3].[FirstName1] AS [FirstName], 
    [Project3].[IsDeleted] AS [IsDeleted], 
    [Project3].[LastLoggedInDate] AS [LastLoggedInDate], 
    [Project3].[LastModifiedDate] AS [LastModifiedDate], 
    [Project3].[LastModifiedUserId1] AS [LastModifiedUserId], 
    [Project3].[LastName1] AS [LastName], 
    [Project3].[MiddleName] AS [MiddleName], 
    [Project3].[UserName1] AS [UserName], 
    [Project3].[CreatorUserId] AS [CreatorUserId1], 
    [Project3].[CreateDate] AS [CreateDate1], 
    [Project3].[LastModifiedUserId] AS [LastModifiedUserId1], 
    [Element2].[Id] AS [Id2], 
    [Element2].[CreateDate] AS [CreateDate2], 
    [Element2].[CreatorUserId] AS [CreatorUserId2], 
    [Element2].[DeleteDate] AS [DeleteDate1], 
    [Element2].[DeleteUserId] AS [DeleteUserId1], 
    [Element2].[FirstName] AS [FirstName1], 
    [Element2].[IsDeleted] AS [IsDeleted1], 
    [Element2].[LastLoggedInDate] AS [LastLoggedInDate1], 
    [Element2].[LastModifiedDate] AS [LastModifiedDate1], 
    [Element2].[LastModifiedUserId] AS [LastModifiedUserId2], 
    [Element2].[LastName] AS [LastName1], 
    [Element2].[MiddleName] AS [MiddleName1], 
    [Element2].[UserName] AS [UserName1], 
    [Project3].[UserName] AS [UserName2], 
    [Project3].[FirstName] AS [FirstName2], 
    [Project3].[LastName] AS [LastName2]
    FROM   (SELECT 
        [Project1].[Id] AS [Id], 
        [Project1].[CreateDate] AS [CreateDate], 
        [Project1].[CreatorUserId] AS [CreatorUserId], 
        [Project1].[FirstName] AS [FirstName], 
        [Project1].[LastModifiedUserId] AS [LastModifiedUserId], 
        [Project1].[LastName] AS [LastName], 
        [Project1].[UserName] AS [UserName], 
        [Element1].[Id] AS [Id1], 
        [Element1].[CreateDate] AS [CreateDate1], 
        [Element1].[CreatorUserId] AS [CreatorUserId1], 
        [Element1].[DeleteDate] AS [DeleteDate], 
        [Element1].[DeleteUserId] AS [DeleteUserId], 
        [Element1].[FirstName] AS [FirstName1], 
        [Element1].[IsDeleted] AS [IsDeleted], 
        [Element1].[LastLoggedInDate] AS [LastLoggedInDate], 
        [Element1].[LastModifiedDate] AS [LastModifiedDate], 
        [Element1].[LastModifiedUserId] AS [LastModifiedUserId1], 
        [Element1].[LastName] AS [LastName1], 
        [Element1].[MiddleName] AS [MiddleName], 
        [Element1].[UserName] AS [UserName1]
        FROM   (SELECT 
            [Extent1].[Id] AS [Id], 
            [Extent1].[CreateDate] AS [CreateDate], 
            [Extent1].[CreatorUserId] AS [CreatorUserId], 
            [Extent1].[FirstName] AS [FirstName], 
            [Extent1].[LastModifiedUserId] AS [LastModifiedUserId], 
            [Extent1].[LastName] AS [LastName], 
            [Extent1].[UserName] AS [UserName]
            FROM [dbo].[UserAccounts] AS [Extent1]
            WHERE ([Extent1].[IsDeleted] = @DynamicFilterParam_000001)  ) AS [Project1]
        OUTER APPLY  (SELECT TOP (1) 
            [Extent2].[Id] AS [Id], 
            [Extent2].[CreateDate] AS [CreateDate], 
            [Extent2].[CreatorUserId] AS [CreatorUserId], 
            [Extent2].[DeleteDate] AS [DeleteDate], 
            [Extent2].[DeleteUserId] AS [DeleteUserId], 
            [Extent2].[FirstName] AS [FirstName], 
            [Extent2].[IsDeleted] AS [IsDeleted], 
            [Extent2].[LastLoggedInDate] AS [LastLoggedInDate], 
            [Extent2].[LastModifiedDate] AS [LastModifiedDate], 
            [Extent2].[LastModifiedUserId] AS [LastModifiedUserId], 
            [Extent2].[LastName] AS [LastName], 
            [Extent2].[MiddleName] AS [MiddleName], 
            [Extent2].[UserName] AS [UserName]
            FROM [dbo].[UserAccounts] AS [Extent2]
            WHERE (([Extent2].[IsDeleted] = @DynamicFilterParam_000001) ) AND ([Project1].[Id] = [Extent2].[CreatorUserId]) ) AS [Element1] ) AS [Project3]
    OUTER APPLY  (SELECT TOP (1) 
        [Extent3].[Id] AS [Id], 
        [Extent3].[CreateDate] AS [CreateDate], 
        [Extent3].[CreatorUserId] AS [CreatorUserId], 
        [Extent3].[DeleteDate] AS [DeleteDate], 
        [Extent3].[DeleteUserId] AS [DeleteUserId], 
        [Extent3].[FirstName] AS [FirstName], 
        [Extent3].[IsDeleted] AS [IsDeleted], 
        [Extent3].[LastLoggedInDate] AS [LastLoggedInDate], 
        [Extent3].[LastModifiedDate] AS [LastModifiedDate], 
        [Extent3].[LastModifiedUserId] AS [LastModifiedUserId], 
        [Extent3].[LastName] AS [LastName], 
        [Extent3].[MiddleName] AS [MiddleName], 
        [Extent3].[UserName] AS [UserName]
        FROM [dbo].[UserAccounts] AS [Extent3]
        WHERE (([Extent3].[IsDeleted] = @DynamicFilterParam_000001) ) AND ([Project3].[Id] = [Extent3].[LastModifiedUserId]) ) AS [Element2]',N'@DynamicFilterParam_000001 bit,@DynamicFilterParam_000002 bit',@DynamicFilterParam_000001=0,@DynamicFilterParam_000002=NULL

In 2.10, however, the dynamic SQL is correct:

exec sp_executesql N'SELECT 
    [Extent1].[Id] AS [Id], 
    [Extent2].[Id] AS [Id1], 
    [Extent2].[CreateDate] AS [CreateDate], 
    [Extent2].[CreatorUserId] AS [CreatorUserId], 
    [Extent2].[DeleteDate] AS [DeleteDate], 
    [Extent2].[DeleteUserId] AS [DeleteUserId], 
    [Extent2].[FirstName] AS [FirstName], 
    [Extent2].[IsDeleted] AS [IsDeleted], 
    [Extent2].[LastLoggedInDate] AS [LastLoggedInDate], 
    [Extent2].[LastModifiedDate] AS [LastModifiedDate], 
    [Extent2].[LastModifiedUserId] AS [LastModifiedUserId], 
    [Extent2].[LastName] AS [LastName], 
    [Extent2].[MiddleName] AS [MiddleName], 
    [Extent2].[UserName] AS [UserName], 
    [Extent1].[CreatorUserId] AS [CreatorUserId1], 
    [Extent1].[CreateDate] AS [CreateDate1], 
    [Extent1].[LastModifiedUserId] AS [LastModifiedUserId1], 
    [Extent3].[Id] AS [Id2], 
    [Extent3].[CreateDate] AS [CreateDate2], 
    [Extent3].[CreatorUserId] AS [CreatorUserId2], 
    [Extent3].[DeleteDate] AS [DeleteDate1], 
    [Extent3].[DeleteUserId] AS [DeleteUserId1], 
    [Extent3].[FirstName] AS [FirstName1], 
    [Extent3].[IsDeleted] AS [IsDeleted1], 
    [Extent3].[LastLoggedInDate] AS [LastLoggedInDate1], 
    [Extent3].[LastModifiedDate] AS [LastModifiedDate1], 
    [Extent3].[LastModifiedUserId] AS [LastModifiedUserId2], 
    [Extent3].[LastName] AS [LastName1], 
    [Extent3].[MiddleName] AS [MiddleName1], 
    [Extent3].[UserName] AS [UserName1], 
    [Extent1].[UserName] AS [UserName2], 
    [Extent1].[FirstName] AS [FirstName2], 
    [Extent1].[LastName] AS [LastName2]
    FROM   [dbo].[UserAccounts] AS [Extent1]
    LEFT OUTER JOIN [dbo].[UserAccounts] AS [Extent2] ON (([Extent2].[IsDeleted] = @DynamicFilterParam_000001) ) AND ([Extent1].[CreatorUserId] = [Extent2].[Id])
    LEFT OUTER JOIN [dbo].[UserAccounts] AS [Extent3] ON (([Extent3].[IsDeleted] = @DynamicFilterParam_000001) ) AND ([Extent1].[LastModifiedUserId] = [Extent3].[Id])
    WHERE ([Extent1].[IsDeleted] = @DynamicFilterParam_000001) ',N'@DynamicFilterParam_000001 bit,@DynamicFilterParam_000002 bit',@DynamicFilterParam_000001=0,@DynamicFilterParam_000002=NULL

So for my particular case, it appears to have been resolved somewhere between 2.3.0 and 2.10.0.

@JonathanMagnan
Copy link
Member

Hello @stormsjr1 ,

Thank you for your example, I will try it this weekend and check what I can do.

Best Regards,

Jonathan

@JonathanMagnan
Copy link
Member

Thank you @stormsjr1 ,

I tried your scenario and didn't understand why it was working until I realized you explicitly told the latest version was working correctly in this case. Your investigation may give me a hint to check when someone will be able to report a non-working code.

If someone could provide me an example that doesn't work with the latest version, I will be happy to investigate it.

Best Regards,

Jonathan

@ArnaudB88
Copy link

Hi @JonathanMagnan ,

Recently this bug became a big issue in one of our production systems.
I've send you a personal email with demo code and a database so you can reproduce this bug.
Can you pick this up again?

Thanks!
Arnaud

@JonathanMagnan
Copy link
Member

Awesome @ArnaudB88 ,

Yes, one of my developers will look to your project.

@ArnaudB88
Copy link

Hi @JonathanMagnan

Any update on this bug yet?

Thanks

@JonathanMagnan
Copy link
Member

Unfortunately from what I remember, we started to look at it but abandoned to try to fix it at this moment.

The v2.x had a lot of major changes to re-write query which is caused at the same time those kinds of performance issues.

So when we tried to fix it, we caused a lot of side impact in the unit test project and never found the perfect solution.

We are very open if someone could make a pull request with a fix but I don't think on our side we will put more time on it as we already spend several days trying to fix it and didn't make any progress.

@ArnaudB88
Copy link

Thank you for the feedback.
I find it unfortunate that no further investigation will be done. This means this repo should not be used anymore in big projects.

@JonathanMagnan
Copy link
Member

The project is open source @ArnaudB88 ,

You can do it on your side and provide a pull request if you find a solution.

You must understand that my time or my developer time is not free or unlimited. Trying to fix this issue means that issue from other developers in other projects would not be fixed. So, unfortunately, we need to make some choices.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

7 participants