EntityFramework 7 Left Join Where Select 奇怪問題


這篇博文紀錄一下:使用 EF7,當 Linq 查詢中使用 "Left Join" 語法(DefaultIfEmpty),Where Select 不同條件語法實現,出現的不同問題。

為了正確反應測試 EF7 所出現的問題,我同時也用 EF6 進行測試,測試代碼都是一樣的,然后使用 SQL Server Profiler 抓取 EF6、EF7 所生成的 SQL 代碼。

測試環境說明:

  • EF6 版本:6.1.2-beta1
  • EF7 版本:7.0.0-beta1
  • 開發環境:VS2015 Preview
  • SQL Server Profiler 對應版本:SQL Server 2014

四種測試場景(b、c 表示對應關聯實體。):

  1. where b select b
  2. no where select b
  3. where c select b
  4. no where select c

BloggingContext 配置代碼:

using Microsoft.Data.Entity;
using Microsoft.Data.Entity.Metadata;
using System.Collections.Generic;

namespace EF7
{
    public class BloggingContext : DbContext
    {
        public DbSet<Blog> Blogs { get; set; }
        public DbSet<BlogCate> BlogCates { get; set; }

        protected override void OnConfiguring(DbContextOptions builder)
        {
            builder.UseSqlServer(@"Server=.;Database=Blogging;Trusted_Connection=True;");
        }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            builder.Entity<Blog>()
                .Key(b => b.BlogId);
            builder.Entity<BlogCate>()
                .Key(b => b.CateId);
        }
    }

    public class Blog
    {
        public int BlogId { get; set; }
        public string Url { get; set; }
        public int BlogCateId { get; set; }
    }
    public class BlogCate
    {
        public int CateId { get; set; }
        public string CateName { get; set; }
    }
}

OnModelCreating 中的映射配置代碼並沒有對 Blog 和 BlogCate 進行 OneToMany 關聯配置,這樣方便我們使用 Linq 進行 Left Join 語法編寫,EF6 測試項目我使用的是 Model First,因為 EF7 測試項目中的模型更改,在 EF6 測試項目中,只要一個“Update Model from Database”命令就可以了,這樣方便進行測試,當然項目中建議不要使用 Model First,EF7 的測試項目使用的是 Xunit,EF6 的測試項目直接是控制台應用程序,因為在 VS2015 中,對於非 ASP.NET 5 Class Library 項目,使用 Xunit 暫無法實現測試。下面貼一下,針對 Where Select 不同條件語法實現,EF6、EF7 所出現的具體問題?

1. where b select b

測試代碼:

[Fact]
public void TestWithLeftJoin()
{
    using (var context = new BloggingContext())
    {
        var query = from b in context.Blogs
                    join c in context.BlogCates on b.BlogCateId equals c.CateId into left
                    from c in left.DefaultIfEmpty()
                    where b.Url == "http://www.cnblogs.com/"
                    select b.BlogId;
        var result = query.ToList();
    }
}

EF6 測試結果:

EF6 生成 SQL 代碼:

SELECT 
    [Extent1].[BlogId] AS [BlogId]
    FROM  [dbo].[Blog] AS [Extent1]
    LEFT OUTER JOIN [dbo].[BlogCate] AS [Extent2] ON [Extent1].[BlogCateId] = [Extent2].[CateId]
    WHERE N'http://www.cnblogs.com/' = [Extent1].[Url]

EF7 測試結果:

詳細異常信息:

The multi-part identifier "b.Url" could not be bound.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action1 wrapCloseInAction)
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action1 wrapCloseInAction)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
at System.Data.SqlClient.SqlDataReader.TryConsumeMetaData()
at System.Data.SqlClient.SqlDataReader.get_MetaData()
at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, SqlDataReader ds)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource1 completion, Int32 timeout, Task& task, Boolean asyncWrite)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method)
at System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior)
at System.Data.Common.DbCommand.ExecuteReader()
at Microsoft.Data.Entity.Relational.Query.QueryingEnumerable1.Enumerator.MoveNext()
at System.Linq.Lookup2.CreateForJoin(IEnumerable1 source, Func2 keySelector, IEqualityComparer1 comparer)
at System.Linq.Enumerable. d__6a4.MoveNext()
at System.Linq.Enumerable. d__142.MoveNext()
at System.Linq.Enumerable.WhereSelectEnumerableIterator2.MoveNext()
at Microsoft.Data.Entity.Query.EntityQueryExecutor.EnumerableExceptionInterceptor1.EnumeratorExceptionInterceptor.MoveNext()
at System.Collections.Generic.List1..ctor(IEnumerable1 collection)
at System.Linq.Enumerable.ToList [TSource] (IEnumerable1 source)
at EF7.Tests.EF7_Test.TestWithLeftJoin() in C:\Users\yuezhongxin\Desktop\EF7\src\EF7.Tests\EF7_Test.cs:line 47

2. no where select b

測試代碼:

[Fact]
public void TestWithLeftJoin()
{
    using (var context = new BloggingContext())
    {
        var query = from b in context.Blogs
                    join c in context.BlogCates on b.BlogCateId equals c.CateId into left
                    from c in left.DefaultIfEmpty()
                    select b.BlogId;
        var result = query.ToList();
    }
}

EF6 測試結果:

EF6 生成 SQL 代碼:

SELECT 
    [Extent1].[BlogId] AS [BlogId]
    FROM  [dbo].[Blog] AS [Extent1]
    LEFT OUTER JOIN [dbo].[BlogCate] AS [Extent2] ON [Extent1].[BlogCateId] = [Extent2].[CateId]

EF7 測試結果:

EF7 生成 SQL 代碼:

SELECT [b].[BlogCateId], [b].[BlogId]
FROM [Blog] AS [b]

3. where c select b

測試代碼:

[Fact]
public void TestWithLeftJoin()
{
    using (var context = new BloggingContext())
    {
        var query = from b in context.Blogs
                    join c in context.BlogCates on b.BlogCateId equals c.CateId into left
                    from c in left.DefaultIfEmpty()
                    where c.CateName == "EF7"
                    select b.BlogId;
        var result = query.ToList();
    }
}

EF6 測試結果:

EF6 生成 SQL 代碼:

SELECT 
    [Extent1].[BlogId] AS [BlogId]
    FROM  [dbo].[Blog] AS [Extent1]
    INNER JOIN [dbo].[BlogCate] AS [Extent2] ON [Extent1].[BlogCateId] = [Extent2].[CateId]
    WHERE N'EF7' = [Extent2].[CateName]

EF7 測試結果:

詳細異常信息:

Object reference not set to an instance of an object.
Anonymously Hosted DynamicMethods Assembly
at lambda_method(Closure , QuerySourceScope )
at System.Linq.Enumerable.WhereSelectEnumerableIterator2.MoveNext()
at Microsoft.Data.Entity.Query.EntityQueryExecutor.EnumerableExceptionInterceptor1.EnumeratorExceptionInterceptor.MoveNext()
at System.Collections.Generic.List1..ctor(IEnumerable1 collection)
at System.Linq.Enumerable.ToList[TSource] (IEnumerable1 source)
at EF7.Tests.EF7_Test.TestWithLeftJoinExption() in C:\Users\yuezhongxin\Desktop\EF7\src\EF7.Tests\EF7_Test.cs:line 63

4. no where select c

測試代碼:

[Fact]
public void TestWithLeftJoin()
{
    using (var context = new BloggingContext())
    {
        var query = from b in context.Blogs
                    join c in context.BlogCates on b.BlogCateId equals c.CateId into left
                    from c in left.DefaultIfEmpty()
                    select c;
        var result = query.ToList();
    }
}

EF6 測試結果:

EF6 生成 SQL 代碼:

SELECT 
    [Extent2].[CateId] AS [CateId], 
    [Extent2].[CateName] AS [CateName]
    FROM  [dbo].[Blog] AS [Extent1]
    LEFT OUTER JOIN [dbo].[BlogCate] AS [Extent2] ON [Extent1].[BlogCateId] = [Extent2].[CateId]

EF7 測試結果:

EF7 生成 SQL 代碼:

SELECT [c].[CateId], [c].[CateName]
FROM [BlogCate] AS [c]
SELECT [b].[BlogCateId]
FROM [Blog] AS [b]

總結:

  1. where b select b(EF6 √,EF7 X)
  2. no where select b(EF6 √,EF7 √)
  3. where c select b(EF6 √,EF7 X)
  4. no where select c(EF6 √,EF7 √)

除了這幾種場景,當然還有其他的比如 where b c select b 等等,但都和上面比較類似,這邊就不紀錄了。


已提交至 EntityFramework 7 issues:Use EF7, Linq Left Join Where Select is error.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM