1. 程式人生 > >Entity Framework 4.1 之八:繞過 EF 查詢對映

Entity Framework 4.1 之八:繞過 EF 查詢對映

原文名稱:Entity Framework 4.1: Bypassing EF query mapping (8)

看到 Entity Framework 4.1 推薦英文教程,為了幫大家看起來方便一些,簡單翻譯一下。這是一個系列,共有 8 篇,這是第 8 篇。

這是這了系列的最後一篇,我將討論如何繞過 EF 的查詢對映。

像所有優秀的框架一樣,EF 知道它並不能優秀到覆蓋所有的角落,通過允許直接訪問資料庫,EF 支援開放底層的 ADO.NET 框架。

有三個 API 支援:

  • DbContext.Database.ExecuteSqlCommand
  • DbContext.Database.SqlQuery
  • DbSet.SqlQuery

第一個沒有什麼特別,就像典型的 ADO.NET 中的 SqlCommand。

publicint ExecuteSqlCommand(string sql, paramsobject[] parameters);

第二個有點意思。

public IEnumerable<TElement> SqlQuery<TElement>(string sql, paramsobject[] parameters);

我們可以使用這個方法直接將 SQL 命令傳送到資料庫,不管是儲存過程,還是臨時的 SQL。與 ADO.NET 的區別在於它能夠將查詢結果的 DataReader 中的資料直接轉換為實體物件。

TElement 可以是任何類。重要的是 EF 不會跟蹤返回的物件,即使他們真的是實體型別的物件。這與第三個 DbSet 不同,第三種方式會跟蹤返回的物件。

讓我們試一下 DbContext.Database.SqlQuery:

public IEnumerable<SprocReport> GetEntityList()
{
return Database.SqlQuery<SprocReport>("SELECT LegalEntityBaseID, EntityName FROM dbo.LegalEntity");
}

一個最佳實踐就是在 DbContext 的派生類中封裝這些呼叫。下面是我們使用的 SprocReport 類的定義。

publicclass SprocReport
{
publicint LegalEntityBaseID { get; set; }
publicstring EntityName { get; set; }
}

這個類不是實體,而且屬性被直接對映:不能控制對映。即使你使用複雜型別,並且覆蓋了對映,這些覆蓋也不會起作用。

現在看 DbSet.SqlQuery,這個方法返回的實體將會被 EF 跟蹤修改,所以,如果你在這些返回的實體上做了修改,當 DbContext.SaveChanges 被呼叫的時候,將會被處理。從另一個方面來說,也不能覆蓋列的對映。

另外一個旁路 EF 對映管理的方法是使用 Entity SQL,記住 EF 將實體模型對映到物理的模型,在轉換到本地底層的資料儲存(例如 TSQL) 查詢之前,先將  LINQ 查詢被轉化到實體模型上(通過 eSQL 語法)。

舉例來說,我們可以建立實體集而不需要在 DbContex 中定義:

protectedoverridevoid OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);

modelBuilder.Entity
<SimpleEntry>().HasEntitySetName("MyEntry");
modelBuilder.Entity
<SimpleEntry>().ToTable("MyEntry", "man");
modelBuilder.Entity
<SimpleEntry>()
.Property(s
=> s.ID)
.HasColumnName(
"SimpleEntryID");
modelBuilder.Entity
<SimpleEntry>()
.Property(s
=> s.Name)
.HasColumnName(
"SimpleEntryName");

}

然後,我們可以暴露出查詢:

public IEnumerable<SimpleEntry> GetSimpleEntries()
{
IObjectContextAdapter adapter
=this;
var entries
= adapter.ObjectContext.CreateQuery<SimpleEntry>("SELECT VALUE MyEntry FROM MyEntry");

return entries;
}

這裡我們使用底層的 ObjectContext 以便查詢。這種方式比直接將 SQL 傳送到資料庫的優勢在於,我們可以使用 LINQ 在其上進行查詢,最終傳送到資料庫的 SQL 是合併得到的。因此,我們可以通過從一個返回任何結果的簡單查詢開始,然後在其上應用 LINQ來得到有效的查詢,而不需要在使用方查詢整個表。

為了說服我們自己,我剛剛說的是真的,讓我們試一下。

public IEnumerable<SimpleEntry> GetSimpleEntries()
{
IObjectContextAdapter adapter
=this;
var entries
= adapter.ObjectContext.CreateQuery<SimpleEntry>("SELECT VALUE MyEntry FROM MyEntry");
var final
= from e in entries
where e.Name == "Mark"
select e;
var f
= (System.Data.Objects.ObjectQuery<SimpleEntry>)final;
var s
= f.ToTraceString();

return entries;
}

如果輸出 s 的值,可以看到:

SELECT[Extent1].[SimpleEntryID]AS[SimpleEntryID],
[Extent1].[SimpleEntryName]AS[SimpleEntryName]FROM[man].[MyEntry]AS[Extent1]WHERE N’Mark’ = [Extent1].[SimpleEntryName]

這是 EF 生成的典型的 TSQL, 你會注意到 LINQ 過濾條件被應用到了 SQL 語句中。

現在,如果你希望能夠截獲實體的 Insert, Update, 和 Delete 操作,就要靠你自己了。你需要重寫 DbContext.SaveChanges ,獲取特定狀態的實體,實現自己的資料操作邏輯來儲存修改,然後在呼叫 base.SaveChanges 之前將這些實體的狀態切換到 Unmodified 。這可以用,但這是一種特殊的技巧。