﻿# 解決 SQL Server 檢視表 (Views) 無法匯入 EDMX 的問題

前陣子在教學的過程中，有位學員問了一個 Entity Framework 的問題，他在 SQL Server 資料庫中建立了一個報表用的檢視表 (Views)，然後想要匯入到 Entity Framework 的 EDMX 實體資料模型中，但卻怎樣都匯不進去，主要原因就出在 Entity Framework 無法自動推斷該檢視表的主索引鍵為何，所以才會無法匯入，而本篇文章將說明匯入 SQL Server 檢視表 (Views) 到 Entity Framework 的注意事項。

我們先看一下，我們已經先建立好一個 **Fabrics.edmx** 實體資料模型檔，然後接著建立一個檢視表名為 **vw\_TableCount**，而這個檢視表在定義的時候，最明顯的特徵就是沒有 **FROM** 子句。

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/a805a0041aed0b68410b-image_2312.png)

```
CREATE VIEW [dbo].[vw_TableCount]AS SELECT    (SELECT COUNT(*) FROM dbo.Client)         AS ClientNum,   (SELECT COUNT(*) FROM dbo.Product)        AS ProductNum,   (SELECT COUNT(*) FROM dbo.[Order])         AS OrderNum
```

接著在 ADO.NET 實體資料模型的設計工具中，在空白處按下滑鼠右鍵，選擇【**從資料庫更新模型**】

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/7c639acc442236c157f2-image_2313.png)

將這個 View 給勾選起來，並按下【完成】

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/5af053c4c65d21e47f7f-image_2314.png)

此時你會發現在 EDMX 設計工具中，完全沒有出現這個被新增的 **vw\_TableCount** 檢視表。

但如果你改以 XML 編輯器開啟 Fabrics.edmx 檔案，可以看出這個無法匯入的原因：

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/f91007898238ba1c0d12-image_2315.png)

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/ce66b670fcdc1a82f0ef-image_2316.png)

錯誤訊息如下：

> <!--產生期間發現錯誤:  
> 警告 6013: 資料表/檢視 'G:\\PROJECTS\\xxx\\APP\_DATA\\FABRICS.MDF.dbo.vw\_TableCount' **未定義主索引鍵**，也**無法推斷有效的主索引鍵**。此資料表/檢視**已被排除**。如果要使用此實體，您必須檢閱您的結構描述，加入正確的索引鍵，並將它取消註解。  
> <EntityType Name="vw\_TableCount">  
> <Property Name="ClientNum" Type="int" />  
> <Property Name="ProductNum" Type="int" />  
> <Property Name="OrderNum" Type="int" />  
> </EntityType>-->

由於 Entity Framework 的運作，必須要有主索引建 (Primary Key) 才能正常運作，當 Entity Framework 無法推斷主索引件是誰時，那就會完全無法運作了。

在我們這個檢視表的輸出結果，你其實很清楚，這張檢視表最多只會有一筆資料輸出，不會有資料重複的問題。所以，我們現在可以有兩個方法解決此問題：

1.  修改 EDMX 的 XML 內容 ( SSDL, CSDL, MSL )，並自行指定 SSDL 中主索引鍵是哪個欄位。
2.  修改 SQL 檢視表的語法，**明確提示** Entity Framework，告訴它哪個欄位才是 Primary Key

這裡我們會以「第二種」方法為主，因為第一種修正方式很有可能 EDMX 會被改壞或下次忘記這樣改。

在 EDMX 匯入 SQL Server 檢視表的時候，有兩種明確提示的方法：

1.  在 **SELECT** 子句中，如果有欄位使用 [**ISNULL** 函式](http://msdn.microsoft.com/zh-tw/library/ms184325.aspx)，那麼該欄位**會自動成為** EF 的**主索引鍵**。
2.  在 **SELECT** 子句中，如果有欄位使用 [**NULLIF** 函式](http://msdn.microsoft.com/zh-tw/library/ms177562.aspx)，那麼該欄位**一定不會成為** EF 的**主索引鍵**。

在我們這個例子裡，因為錯誤訊息顯示「未定義主索引鍵，也無法推斷有效的主索引鍵」，所以我們需要的是上述第 1 種明確提示方法，也就是在 **SELECT** 子句中採用 [**ISNULL** 函式](http://msdn.microsoft.com/zh-tw/library/ms184325.aspx)即可！

現在我們將 View 修改如下：

```
CREATE VIEW [dbo].[vw_TableCount]AS SELECT    ISNULL(NULL, 1) AS ID,   (SELECT COUNT(*) FROM dbo.Client)         AS ClientNum,   (SELECT COUNT(*) FROM dbo.Product)        AS ProductNum,   (SELECT COUNT(*) FROM dbo.[Order])         AS OrderNum
```

我們將該**檢視表**額外新增一個 ID 欄位，並且固定輸出一個數字 1 即可。接著在 ADO.NET 實體資料模型的設計工具中，在空白處按下滑鼠右鍵，選擇【**從資料庫更新模型**】，並將這個**檢視表**給勾選起來，按下【完成】，該檢視表就會正確被匯入 **Fabrics.edmx** 實體資料模型檔中！

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/eb8acc62153a6dc9f0fe-image_2317.png)

即便如此，你還是可以再次打開 EDMX 檔案，只是現在的錯誤訊息變成如下：

> <!--產生期間發現錯誤:  
> 警告 6002: 資料表/檢視 'G:\\PROJECTS\\xxx\\APP\_DATA\\FABRICS.MDF.dbo.vw\_TableCount' 未定義主索引鍵。**已推斷此索引鍵**，並將定義建立成唯讀的資料表/檢視。-->

由於 Visual Studio 的 ADO.NET 實體資料模型的設計工具已經幫你自動「**推斷此索引鍵**」，所以 **vw\_TableCount** 這個實體 ( Entity ) 已經可以正常使用了！

當然，如果你的 Entity Framework 遇到其他類似的問題，有些則是可以匯入檢視表，但執行結果會有問題，都可以利用這個小技巧來解決。

**備註**：本篇文章的範例程式已經放上 GitHub：[https://github.com/doggy8088/EFViewPrimaryKey](https://github.com/doggy8088/EFViewPrimaryKey)

**重點摘要**

在 EDMX 匯入 SQL Server 檢視表的時候，有兩種明確提示的方法：

1.  在 **SELECT** 子句中，如果有欄位使用 [**ISNULL** 函式](http://msdn.microsoft.com/zh-tw/library/ms184325.aspx)，那麼該欄位**會自動成為** EF 的**主索引鍵**。
2.  在 **SELECT** 子句中，如果有欄位使用 [**NULLIF** 函式](http://msdn.microsoft.com/zh-tw/library/ms177562.aspx)，那麼該欄位**一定不會成為** EF 的**主索引鍵**。

其他可能的小技巧：

-   若檢視表中有用到 **UNION** 語法，請注意不要讓**主索引鍵**的**值**重複了!
-   如果你確信你的檢視表要回傳完整的結果，也可以考慮用以下語法輸出 ID 欄位的值：
    
    ```
    ISNULL(newid(), '')
    ```
    
-   如果會用到 **UNION** 語法串接多個結果集，有部分結果集沒有主索引鍵的話，也可以用上一點的技巧，不過記得要做適當轉型，確保不同結果集的主索引鍵欄位型別一致，例如：
    
    ```
    ISNULL(CONVERT(VARCHAR(36), newid()),'')
    ```
    

**相關連結**

-   [ISNULL (Transact-SQL)](http://msdn.microsoft.com/zh-tw/library/ms184325.aspx)
-   [NULLIF (Transact-SQL)](http://msdn.microsoft.com/zh-tw/library/ms177562.aspx)
-   [Entity Framework and SQL Server View - Stack Overflow](http://stackoverflow.com/questions/1013333/entity-framework-and-sql-server-view)
-   [Entity Framework and Setting Primary Keys on Views](https://web.archive.org/web/20191226043400/http://girlfromoutofthisworld.com:80/entity-framework-and-setting-primary-keys-on-views/)
-   [Improving Performance with SQL Server 2008 Indexed Views](http://msdn.microsoft.com/en-us/library/dd171921\(SQL.100\).aspx)
-   [解開 Entity Framework 資料重複之謎 - 黑暗執行緒](http://blog.darkthread.net/post-2012-06-29-ef-duplicate-data-when-incorrect-pk.aspx)
-   [Entity Framework 的 View Primary Key 錯置疑雲 - 黑暗執行緒](http://blog.darkthread.net/post-2012-06-28-ef-view-pk-issue.aspx)
-   [EDM QueryViews vs Defining Queries (and read-only views)](http://thedatafarm.com/blog/data-access/edm-queryviews-vs-defining-queries-and-read-only-views/)
-   [Defining Queries in the EF Designer](http://msdn.microsoft.com/nl-nl/data/jj730307) (MSDN)
-   [Past Versions of Entity Framework](http://msdn.microsoft.com/nl-nl/data/hh913624)
