EntityFramework SQL 2005 not support datatype ‘datetime2’

因為在測試環境都是用 SQL 2008 的版本在做測試開發,到了正式環境要發佈的時候卻發生 not support datatype ‘datetime2’ 的錯誤。

Server Error in '/' Application.
--------------------------------------------------------------------------------

The version of SQL Server in use does not support datatype 'datetime2'.
Description: An unhandled exception occurred during the execution of the current web request.
Please review the stack trace for more information about
the error and where it originated in the code.

Exception Details: System.ArgumentException:
The version of SQL Server in use does not support datatype 'datetime2'.

  • 開發測試環境是 SQL 2008
  • 正式環境 SQL 2005
  • EntityFramework 4.3
  • Database First

問題發生原因

這邊有 datetime 跟 datetime2 的比較
在 SQL 2008 之後版本,微軟將資料庫的 datetime 欄位改為 datetime2,而當使用 EntityFramework Database First 的時候,就會根據資料庫的版本去產生 edmx。再利用 edmx 對應去產生物件做操作。所以如果使用的是 SQL 2008 產生出來的物件,在 Datetime 要存進資料庫的時候就會幫你轉成 SQL Datetime2 格式。當然這樣在 SQL 2008 下是沒有問題的,可是如果要拿同一份程式到 SQL 2005 執行就會造成這個錯誤。

解決方法

其實解決方法很簡單,只要將 edmx 用 XML Editor 開啟之後。找到資料庫對應版本的設定。

<!-- SSDL content -->
<edmx:StorageModels>
<Schema Namespace="Test.Store"
Alias="Self"
Provider="System.Data.SqlClient"
ProviderManifestToken="2008"
xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator"
xmlns="http://schemas.microsoft.com/ado/2009/02/edm/ssdl">
<EntityContainer Name="LiteonAward2012ModelStoreContainer">

將上面的第 6 行 2008 改為 2005 之後將檔案存檔,並且重新產生 POCO 或是 DBContext 物件編譯過後就可以了。


Using SQL Server 2008 and SQL Server 2005 and date time

EntityFramework 在比較 null 跟 int 的差異性

如果說今天有一個欄位在資料庫中被設計成可為 null 的 int ,在使用 Linq 語法上會變得比較麻煩。

  • EntityFramework 4.3.1

資料庫的設計

dbo.category

Id Name ParentId
1 Computer NULL
2 TV NULL
3 Laptop 1
4 Desktop 1

在這邊的需求是當參數 ParentId 傳進來的的時候,要能夠找到對應的資料。

利用 == 比較子

public ViewResult Index(int? parentId)
{
var categories = db.Categories
.Where(it => it.ParentId == parentId);
return View(categories);
}

在這邊比較的時候會產生一個問題,EntityFramework 在解析 Linq 語法成為 T-SQl 的時候會將 == 解析為 = ,所以當傳入的 parentId 為 null 的時候,這邊比較的語法會是

WHERE ParentId = NULL

null 的比較這樣是錯誤的,要找到 null 的資料應該是利用 is null 來當條件。

  • 傳入資料是 null > 沒有找到 ParentId = null 資料
  • 傳入資料不是 null > 可以正確找到資料

利用 Equals 比較子

public ViewResult Index(int? parentId)
{
var categories = db.Categories
.Where(it => it.ParentId.Equals(parentId));
return View(categories);
}

如果是傳入為 null 的時候也要能比較出 null 的資料,這時候用 Equals 當作運算子,則在解析成為 T-SQL 的時候成為

WHERE ParentId IS NULL

這樣就可以比較 null 的資料了,但是問題並非這麼單純,因為當傳入參數不為 null 的時候,會產生一個錯誤

Unable to create a constant value of type 'System.Object'. Only primitive 
types or enumeration types are supported in this context.

  • 傳入資料是 null > 可以正確找到資料
  • 傳入資料不是 null > 產生 Unable to create a constant value of type ‘System.Object’ Exception

整合兩邊的方法

在這個情況之下這個查詢勢必需要先判斷再送進 SQL 查詢的動作了

public ViewResult Index(int? parentId)
{
if (parentId.HasValue)
{
var categories = db.Categories
.Where(it => it.ParentId == parentId)
.ToList();
}
else
{
var categories = db.Categories
.Where(it => it.ParentId.Equals(parentId))
.ToList();
}
return View(categories);
}

先判斷傳入參數是否為 null 再根據不同情況做查詢

另一種選擇的解決方法

或著是不想要寫兩段 Linq 的也可以考慮

public ViewResult Index(int? parentId = 0)
{
var categories = db.Categories
.Where(it => (it.ParentId.HasValue ? it.ParentId : 0) == parentId)
.ToList();

return View(categories);
}

在這邊的三元替換轉換成 T-SQL 會是

WHERE (CASE WHEN (ParentId IS NOT NULL) THEN ParentId ELSE 0 END) = @p__linq__0


differences between int32.Equals int? == int? for linq

ASP.NET MVC 3 Tools Update – Scaffolding

Introduction

在ASP.NET MVC 3 Tools Update中內建了Scaffolding這個功能,這個功能可以幹嘛呢,基本上它是 T4 Template 的延伸應用。POCO 工具也是 T4 的應用,能讓你根據某種規範去產生出程式碼,xml 或是 class 或是自己定義的規則。Scaffolding這個犯規的地方在於說他根據 Model 的 Class 幫你把整個網站基本功能全部架好了,如果只是後台應用幾乎很大一部份可以直接使用,修改也一定比從頭 Coding 到尾還要簡單多。

Requirement

Visual Studio 2010

Entity Framework 4.1

ASP.NET MVC 3 Tools Update

NuGet(Optional)

MvcScaffolding(Optional)

image

▲利用Scaffolding查詢就有數款工具可以參考

Implement

先準備好 Model 還有 Entity ,不論是 Database First ,Model First,Code First 都可以。

image

▲在MVC專案的Controllers資料夾新增Controller

image

▲如果沒有安裝MvcScaffolding則只會有上面的選項

image

▲把相關資料填完,這邊如果Model 跟 Context 在不同專案當中的話要加入參考,Advanced Options 可以設定 Master Page 的選項,按下OK之後就會開始產生相關的程式碼。

image

▲需要一段處理時間,輸出這邊可以看到產生的部分。

*如果選擇 using repository 的部分的話,需要一併建立相關聯的部分,不然參考的 interface 沒有建立起來會有錯誤。

 

image

▲產生的 List 畫面 ,包括 Edit,Create,Delete 基本功能

image

▲也加入了驗證機制

Summary

實際在執行的時候也並不是每種狀況下都可以100%沒有問題,包括像自訂類別的Complex。不過還是能夠幫助很快的先建立基本的架構,要是覺得預設的工具或是找到的功能都不夠完整的話,T4 Template 也可以不用別人的工具自己實作。之後會不會出現很多公司內部自己研發一整套 T4 ,邊跟客戶開會就邊設計 Model ,等到需求確定網站就完成了,速度快到嚇死人。難怪有人說,重點不是在寫了多少 code ,而是少寫了多少 code

Reference

Steven Sanderson – Scaffold your ASP.NET MVC 3 project with the MvcScaffolding package (NuGet MvcScaffolding package 作者的網誌)

http://blog.stevensanderson.com/2011/01/13/scaffold-your-aspnet-mvc-3-project-with-the-mvcscaffolding-package/

ASP.NET MVC 3 Tools Update

http://www.microsoft.com/download/en/details.aspx?id=1491

黑大的介紹文 ASP.NET MVC 3 Tools Update

http://blog.darkthread.net/post-2011-05-18-mvc-3-tools-update.aspx

Code First # 2 – Initializer

在 Code First 中,很神奇的資料庫可以根據設定的 model class 建立起來,不過他真的有這麼厲害嗎。如果 model 修改的時候,他也可以自動跟據修改過的去修改原本的 schema 嗎。

事情總是沒有想像中這麼美好, model class 修改的時候他的確會一起修改 table schema ,但是他的做法卻是 – 把整個資料庫 drop 掉,再重新 create 起來。當然這邊設定的帳號需要有 drop 資料庫的權限。那這樣的話我全部的資料不就不見了,事實上的確是這樣,不過這個並不是預設的動作,預設的動作是跳 Exception 給你

System.InvalidOperationException: 
The model backing the 'PlaceContext' context has changed since the database was created.
Either manually delete/update the database, or call Database.SetInitializer with an IDatabaseInitializer instance.
For example, the DropCreateDatabaseIfModelChanges strategy will automatically delete and recreate the database, and optionally seed it with new data.

那如何開啟這個功能呢

Rquirement

1. 你必須對資料庫擁有 drop 的權限。

image

在 Entity framework 4.1 中看到提供這接些方法

CreateDatabaseIfNotExists(預設是這個)

DropCreateDatabaseAlways

DropCreateDatabaseIfModelChanges

或是是自訂實作 IDatabaseInitializer 介面

image

設置 Database 的 Initializer 這個動作必須在 DbContext instance is initialized 之前。The database initialization strategy is called when DbContext instance is initialized from a DbCompiledModel. – MSDN

這樣就可以 DropCreate 你的 model 確定每次的 Table Schema 都是正確無誤的

另外有看到一些實作 IDatabaseInitializer 的範例

Entity Framework CTP 4 – Code First Custom Database Initializer

Code First # 1 – ADO.NET Entity Framework 4.1

Code First 是ADO.NET Entity Framework 4.1 新推出的功能,原有的 Model First , Database First 又多了一種新的 ORM 解決方案。

Code First 和其他兩種方式比較不一樣的是在這個方案中不用到 .edmx 檔案工具來建置 model 。在其他兩個解決方案中,是利用 .edmx 來當作兩邊的中介,在這邊設定資料庫與 model 間的對應,在設定對應完之後再利用工具或是手動去產生程式碼。但是 在 Code First 是直接利用程式碼去產生資料庫,變成說資料庫反而是動態被產生的。

Required

Visual Studio 2010

ADO.NET Entity Framework 4.1

我用的是 Sql express

image

一開始先開啟一個空白的 Class Library 在這邊參考了 Entity Framework 這個元件

image

Entity Framework 這邊就有包含 System.Data.Entity ,但是注意的是這邊的只是增加的功能,所以如果只有單純 Reference 這個的話功能會不完全喔。所以這邊也可以用NuGet 這個 plugin 工具,可以省下一些時間。

接下來直接在 Domain.Model 設計整個 Model

image

接下來是設置DbContext

image

如圖中所示,我設立了一個 PlaceContext 繼承了 DbContext 這個類別,而其中有四個DataSet 的屬性。之後程式在執行的會將資料庫自動建立起來(預設是SQLEXPRESS)。

image

資料庫的名稱就是DbContext 的 Class 名稱,不過 Table 名稱就不是我設定的,系統根據 Model ClassName 加上複數型態,如果不要加上複數直接用 class name

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}

References

Code-First Development with Entity Framework 4

http://weblogs.asp.net/scottgu/archive/2010/07/16/code-first-development-with-entity-framework-4.aspx

ADO.NET POCO Entity Generator With Entity Framework

 

我們利用 Visual Studio 2010 建立一個多階層的 Appication,在這邊利用POCO 工具來對 Entity Data Model 產生 POCO 的物件。

假設我們已經建立好 Data Entity edmx 的部分,預設他也會建立一個 Designer.cs

image

打開 Designer.cs 裡面應該有類似於下面的程式碼

namespace Eoa.Infrastructure.Entity
{
#region Contexts

public partial class EoaModelEntities : ObjectContext
{

}

#endregion

#region Entities

public partial class Articals : EntityObject
{

}

#endregion
}

可以看到 vs2010 幫我們建立了兩個部分,ObjectContext 跟 EntityObject

ObjectContext : 提供以物件形式查詢和使用實體資料的機能。

EntityObject : Entity Data Model 工具所產生之實體類型的基底類別。

這邊是MSDN上面的解釋,其實就是說繼承 EntityObject 部分是 Model 工具根據 edmx 裡面的設計去建立的物件。而 ObjectContext 則是控制這些東西要怎麼去跟資料庫實體去互動。這樣子已經算是完整的把資料庫物件化了,整個資料庫是一個物件,而裡面的 Table 也有個別的物件去控制。

那為什麼要用 POCO ,因為這邊的 EntityObject 用起來太麻煩了,而且也不夠直覺,重點又回到了關注點分離的部分,當我 new 一個物件起來的時候,我還不想管資料庫要怎麼去處理他。如果買車的時候,銷售員硬要你買車庫,也是一件很不可理喻的事情吧。

image

回到 edmx 的屬性頁面,這時候把 Custom Tool 清除

image

可以看到這邊的 Designer.cs 已經被移除掉了,接下來加入 ADO.NET C# POCO Entity Generator

image

image

image

看到這邊 ADO.NET C# POCO Entity Generator 一樣幫我們產生了 ObjectContext 跟 Object 兩個部分,只是這邊的 Object 不再是 EntityObject 而是 CLR Object 。

image

接下來把 CLR Object 移到我們想要他去的地方,並且在 string inputFile  的部分指向原本專案的edmx,這邊的相對路徑是根據實際資料夾中的相對路徑,不是方案中的虛擬資料夾設定,這邊是因為我實際資料夾設定就是方案資料夾的設定。

image

在 T4 Template 的使用上,當你 save .tt 檔案的時候,他才會自動去更新產生的 .cs 檔案。例如說在 EoaModel.edmx 的介面中修改了屬性或是增加了欄位,這時候我需要回到 EoaModel.tt 重新儲存一次,這時候他會根據 inputFile 這個屬性去重新參考檔案,並且重新建立 EoaModel.tt 底下全部的檔案,所以說手動修改 EoaModel.tt 裡面的檔案是不建議的做法。

移至新專案下之後 EoaModel.tt 重新儲存的時候,他也會修改 namespace 的部分。

記得原本的元件要加入新元件的參考,並且在 ObjectContext 加入參考。

image

就如同剛剛所提到的,如果直接在 EoaModel.Context.tt > EoaModel.Context.cs 去做修改,那之後在每一次重新建立的時候,都會被覆蓋掉,所以我這邊用的方法是到EoaModel.Context.tt 檔案中去修改 template 。

image

這樣子在重新建立的時候也能夠每一次都將 Eoa.Domain.Model 加入 using

Reference

POCO Tool

http://visualstudiogallery.msdn.microsoft.com/23df0450-5677-4926-96cc-173d02752313

POCO in the Entity Framework: Part 1 – The Experience

http://blogs.msdn.com/b/adonet/archive/2009/05/21/poco-in-the-entity-framework-part-1-the-experience.aspx

POCO in the Entity Framework : Part 2 – Complex Types, Deferred Loading and Explicit Loading

http://blogs.msdn.com/b/adonet/archive/2009/05/28/poco-in-the-entity-framework-part-2-complex-types-deferred-loading-and-explicit-loading.aspx

Sneak Peek – Using Code Generation Templates with the Entity Framework 4.0

http://blogs.msdn.com/b/adonet/archive/2009/05/19/sneak-peek-using-code-generation-templates-with-the-entity-framework-4-0.aspx

Where to place the entity framework in our solution?

http://stackoverflow.com/questions/5558996/where-to-place-the-entity-framework-in-our-solution/5559032#5559032

*Entity Framework POCO Repository using Visual Studio 2010 .Net 4.0 Beta 1

http://blog.keithpatton.com/2009/05/30/Entity+Framework+POCO+Repository+Using+Visual+Studio+2010+Net+40+Beta+1.aspx

Model first with Entity Framework

 

試著用 Entity Framework 中預設的 Model first 方法來建立資料庫。假設我們要建立一個類似部落格或是討論區的專案。

image

先建立一個空的專案,還有空的 Class Library

image

接下來建立 Entity Data Model,在這邊選擇 Empty Model 來產生一個空白的 Model,在這邊設計物件中的相關邏輯。

其實我覺得整個 Entity Framework 最重要的部分就是這個畫面了,EF 要解決的不外乎就是物件對於資料層面的概念不夠直接,在前端呈現的部分幾乎也都可以用物件的觀念去呈現了(ex. Json),在資料層微軟的 Linq to SQL 到 Entity Framework 都是想解決這部分的問題,使用物件直接操作資料。

image

在這邊可以看到,我建立了幾個物件

1. Person – 全部的人們

2.Member – 會員必須經過申請認證,並且在取得身分之前要先登入

3.Guest – 訪客沒有經過申請,或是沒有登入

4.Artical – 文章必須是會員才有發表的資格

5.Response – 只要來到這個地方就可以針對文章做回應,不論是有沒有登入,所以這邊的關係是對 Person 不是 Member

接下來利用資料庫產生工具來建立資料庫指令碼,這邊我們先到資料庫中開啟一個空白的資料庫再來執行下面的動作。

image

整個 Wizard 會產生一份 .sql 的 Sql Command 檔案,到資料庫下去執行

image

之後就會根據 Model 裡面設計的規則,去產生 Table 對應。

Reference

[VS2010] ADO.NET Entity Framework: 建立多對多關聯模型

http://www.dotblogs.com.tw/regionbbs/archive/2010/02/03/netfx.4.0.new.feature.ado.net.entity.framework.2.0.implement.mtom.relationship.aspx