前幾篇文章都圍繞著 Entity Framework 與 Stored Procedure 打轉,而上一篇則是討論到回傳多資料結果時在 Entity Framework 的處理方式,最後我還是回歸到使用 ADO.NET 的方式來處理,雖然最後是有提到有一篇國外文章有說明可以去修改 EDMX 檔案內容讓 Entity Framework 可以直接處理 SP 所回傳的多資料結果,但去修改 EDMX 內容實在有太大的風險,而且算是一種非正式的處理方式,所以我選擇不採用。
而在網路上找尋相關解決方案的時候,我看到了 Telerik OpenAccess ORM 可以處理這種 SP 回傳多資料結果的方法,再加上 Telerik OpenAccess ORM 是免費的,而且設定與使用方式跟 Entity Framework 大致相同,在使用過 Telerik OpenAccess ORM 來解決這個處理後覺得有些案子是可以考慮使用它。
這一篇就從一開始在專案中加入使用 Telerik OpenAccess ORM,然後加入 Model 與 Stored Procedure,最後再使用 Telerik OpenAccess ORM 所提供方法來完成這個回傳多資料結果的處理。
Telerik OpenAccess ORM
http://www.telerik.com/products/orm.aspx
http://www.telerik.com/products/orm/getting-started.aspx
一定會有人問,那這個 Telerik OpenAccess 跟 Entity Framework 的比較呢?
7 Reasons to Choose OpenAccess ORM over Entity Framework
http://www.telerik.com/products/orm/getting-started/openaccess-vs-entity-framework.aspx
其實哪個產品不都是說自己一定比別人好,至於是不是真的那樣呢?這唯有讓各位自己實際去體會。
我覺得 Telerik OpenAccess 的線上資源提供地相當完整,從入門學習、範例、文章、影片、論壇、KB 等,都相當豐富,尤其是「Online Help」,將安裝與使用文件以及各種專案的應用都有詳細的說明。
Telerik OpenAccess ORM Online Help
http://www.telerik.com/help/openaccess-orm/openaccess-orm-introduction.html
另外還有一點就是 Telerik OpenAccess ORM 標榜可以支援多種資料庫,而且是在不必另外安裝元件的情況下,舉凡非 MS SQL Server 的 Oracle, MySQL, Sybase, PostgreSQL, SQLite 等都有支援,
http://www.telerik.com/products/orm/features/database-support.aspx
因為官方所提供的說明文件已經相當完整了,所以我這邊就不再多廢話,直接進入主題,讓我們看看怎麼處理回傳多資料結果的狀況。
Step.1 在以建立的專案加入 telerik OpenAccess Domain Model
開啟加入新項目,選擇「資料」類別,要加入的是「Telerik OpenAccess Domain Model」
我這邊是使用 Database First,所以選擇「Populate from database」,接著再選擇使用的是哪一種資料庫,然後決定 Model 名稱以及命名空間名稱,
加入資料庫連接屬性,
加入資料庫連接屬性的操作如同在 EF 的設定一樣,
完成加入,
選擇要加入 model 的內容,這邊我們加入所有的 Table 以及「ReturnTwoResult」這個 SP,
接著的 Define Naming Rules,我都是使用預設選項,所以就直接 Next,
最後的 Code Generation Settings,這邊也是使用預設選項,到了這邊就結束 telerik OpenAccess Model 的設定,
完成加入
Step.2 在 Controller 中加入程式
專案加入 Telerik OpenAccess Domain Model 之後,就可以在 ASP.NET MVC 專案的 Controller 裡加入程式,而「ReturnTwoResult」所傳回的資料為 Order 與 OrderDetails,所以這邊我建立 OrderController,並且加入 Index 與 Details 兩個 Action 方法,Index 為顯示 Order 列表,Details 為顯示 Order 與 OrderDetails 資料,
public class OrderController : Controller
{
private EntitiesModel1 db = new EntitiesModel1();
public ActionResult Index()
{
var result = db.Orders.OrderBy(x => x.OrderID).ToList();
return View(result);
}
public ActionResult Details(int id = 0)
{
//TODO: use [ReturnTwoList]
return View();
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
}
Details 這個 Action 方法的程式內容,參考 telerik OpenAccess ORM Online Help 的範例「How to: Execute Stored Procedures Returning Multiple Result Sets」,不過我這邊稍微做了修改,
public ActionResult Details(int id = 0)
{
if (id == null)
{
return RedirectToAction("Index");
}
else
{
// 1. Create a new instance of the OpenAccessContext.
// 2. Retrieve the OAConnection instance.
// 3. Create a new instance of the OACommand class.
using (EntitiesModel1 dbContext = new EntitiesModel1())
using (IDbConnection oaConnection = dbContext.Connection)
using (IDbCommand oaCommand = oaConnection.CreateCommand())
{
// 4. Set the CommandType property.
oaCommand.CommandType = CommandType.StoredProcedure;
// 5. Set the CommandText property.
oaCommand.CommandText = "ReturnTwoResult";
IDbDataParameter param = oaCommand.CreateParameter();
param.ParameterName = "OrderID";
param.Value = id;
oaCommand.Parameters.Add(param);
// 6. Execute the command and materialize the Order entities
using (IDataReader dataReader = oaCommand.ExecuteReader())
{
IRowMapper<Order> orderMapper = MapBuilder<Order>.MapAllProperties()
.DoNotMap(x => x.Customer)
.DoNotMap(x => x.Employee)
.DoNotMap(x => x.Shipper)
.Build();
Order order = new Order();
if (dataReader.Read())
{
order = orderMapper.MapRow(dataReader);
}
// 7. Advance to the next result sets
dataReader.NextResult();
List<OrderDetail> orderDetails = db.Translate<OrderDetail>(dataReader as DbDataReader).ToList();
OrderOrderDetailsViewModel viewModel = new OrderOrderDetailsViewModel()
{
Master = order,
Details = orderDetails
};
return View(viewModel);
}
}
}
}
在 Details() 程式中還是有使用 EntLib DAAB 的 RowMapper,因為 OpenAccess 所提供的 Translate<T>() 方法只能對多筆資料有作用,而對單筆資料就不行,所以這邊就還是使用 EntLib DAAB 的 RowMapper,另外傳到 View 的 Model 則是另外建立一個 ViewModel 類別,如下:
ViewModel - OrderOrderDetailsViewModel
public class OrderOrderDetailsViewModel
{
public Order Master
{
get;
set;
}
public List<OrderDetail> Details
{
get;
set;
}
}
Step.3
當上面的設定與程式完成後,並且也加入了檢視(View)就可以執行,就在按下「F5」後卻發現出現了錯誤,
這是 Web.Config 的 system.web 的 compilation 裡少了設定,
我們需要加上組件參考,如下:
不過組件資訊中的 Version 內容會隨著版本更新而有所改變,所以要注意專案是使用那一個版本,
<system.web>
<compilation debug="true" targetFramework="4.5">
<assemblies>
<add assembly="Telerik.OpenAccess, Version=2013.1.219.3, Culture=neutral, PublicKeyToken=7ce17eeaf1d59342" />
</assemblies>
</compilation>
Step.4
正當以為上面的問題解決之後就可以正常執行,但在進入 Details 頁面時再度出現錯誤,而這次的錯誤就讓人摸不著頭緒,
這次的問題直指 OrderDetail 類別中的 UnitPrice 屬性的問題,
關於這個問題卻讓我找不到直接的解決方式,最後我就另外建立一個 DTO 類別來替代,
public class OrderDetailDTO
{
public int OrderID { get; set; }
public int ProductID { get; set; }
public decimal UnitPrice { get; set; }
public short Quantity { get; set; }
public float Discount { get; set; }
}
連帶的也需要將 OrderOrderDetailsViewModel 做修改,
public class OrderOrderDetailsViewModel
{
public Order Master
{
get;
set;
}
public List<OrderDetailDTO> Details
{
get;
set;
}
}
再來也修改 Details Action 方法內的程式,
...
...
...
// 7. Advance to the next result sets
dataReader.NextResult();
List<OrderDetailDTO> orderDetails = db.Translate<OrderDetailDTO>(dataReader as DbDataReader).ToList();
OrderOrderDetailsViewModel viewModel = new OrderOrderDetailsViewModel()
{
Master = order,
Details = orderDetails
};
return View(viewModel);
完成最後的修改之後,總算可以看到執行結果了,
這次使用 Telerik OpenAccess ORM 取代 Entity Framework 來執行 Stored Procedure 傳回多資料結果的處理,不過到最後卻還是回歸使用傳統 ADO.NET 執行的方式,所以在這種傳回多資料結果的處理就不要使用 ORM Solution 來完成,直接使用傳統 ADO.NET 的方式。
以上
沒有留言:
張貼留言