کوئری ذیل را در نظر بگیرید:
var productsList1 = ctx.Products.Where(product => product.Id > 1) .Include(product => product.Category) .Include(product => product.User) .Where( product => product.Category.Title.Contains("t") && product.Category.Id > 1 && product.Price > 100) .OrderBy(product => product.Price) .ToList();
احتمالا شاید عنوان کنید که به ازای هر Include یک join خواهیم داشت. بنابراین دو جوین به جداول کاربران و گروههای محصولها ایجاد میشود.
اما ... در واقعیت این کوئری را تولید میکند:
SELECT [Extent1].[Id] AS [Id], [Extent1].[Name] AS [Name], [Extent1].[Price] AS [Price], [Extent1].[CategoryId] AS [CategoryId], [Extent1].[UserId] AS [UserId], [Extent3].[Id] AS [Id1], [Extent3].[Name] AS [Name1], [Extent3].[Title] AS [Title], [Extent3].[UserId] AS [UserId1], [Extent4].[Id] AS [Id2], [Extent4].[Name] AS [Name2] FROM [dbo].[Products] AS [Extent1] INNER JOIN [dbo].[Categories] AS [Extent2] ON [Extent1].[CategoryId] = [Extent2].[Id] LEFT OUTER JOIN [dbo].[Categories] AS [Extent3] ON [Extent1].[CategoryId] = [Extent3].[Id] LEFT OUTER JOIN [dbo].[Users] AS [Extent4] ON [Extent1].[UserId] = [Extent4].[Id] WHERE ([Extent1].[Id] > 1) AND ([Extent2].[Title] LIKE N'%t%') AND ([Extent1].[CategoryId] > 1) AND ([Extent1].[Price] > 100) ORDER BY [Extent1].[Price] ASC
این دو جوین حاصل یکبار Include جدول Categories و یکبار استفاده از navigation property آن در قسمت where است.
این باگ در اینجا گزارش شده، ولی به نظر هنوز برطرف نشدهاست یا مجددا ظاهر شدهاست.
برای رفع آن در حال حاضر بهترین راه حل استفاده از روش ذیل است:
var query2 = from product in ctx.Products let category = product.Category where product.Id > 1 where category.Title.Contains("t") && category.Id > 1 && product.Price > 100 select new { product, category }; var productsList2 = query2.ToList();
SELECT [Extent1].[Id] AS [Id], [Extent1].[Name] AS [Name], [Extent1].[Price] AS [Price], [Extent1].[CategoryId] AS [CategoryId], [Extent1].[UserId] AS [UserId], [Extent2].[Id] AS [Id1], [Extent2].[Name] AS [Name1], [Extent2].[Title] AS [Title], [Extent2].[UserId] AS [UserId1] FROM [dbo].[Products] AS [Extent1] INNER JOIN [dbo].[Categories] AS [Extent2] ON [Extent1].[CategoryId] = [Extent2].[Id] WHERE ([Extent1].[Id] > 1) AND ([Extent2].[Title] LIKE N'%t%') AND ([Extent2].[Id] > 1) AND ([Extent1].[Price] > 100)
چند نکته:
-در کوئری let دار، اگر در قسمت select نهایی فقط product ذکر شود، هرچند جوین به جدول گروهها تشکیل میشود اما فیلدهای این جدول انتخاب نخواهند شد.
-معادل کوئری LINQ نوشته شده را اگر بخواهیم توسط متدهای الحاقی بازنویسی کنیم، به کوئری ذیل خواهیم رسید:
var query2ChainedVersion = ctx.Products .Select(product => new { product, category = product.Category }) .Where(@t => @t.product.Id > 1) .Where(@t => @t.category.Title.Contains("t") && @t.category.Id > 1 && @t.product.Price > 100) .Select(@t => new { @t.product , @t.category });
اگر علاقمند به آزمایش این باگ هستید، کدهای کامل آنرا از اینجا میتوانید دریافت کنید:
Sample38.zip