在以前ado.net時候,我們使用存儲過程返回一個列表,可以將結果集放在DataTable中,如果我們需要將結果集放在一個強類型集合(如List<T>)中我們該怎么做呢?之前在網上看到過一種解決方法,忘記出處了,請諒解。大概思路是: 在用DataReader讀取一行記錄時,將該行創建為一個對象,然后添加到列表中 。
我在EF3.5中使用存儲過程,需要在edmx(領域模型中)文件中做函數導入(Function Import),并且返回值類型必須是數據庫中已存在的實體。
這樣做的缺憾(不夠靈活)如下:
1、必須要函數導入,如果后來修改或更新實體模型,要維護該函數。(可以接受)
2、存儲過程返回的對象必須是數據庫已存在的實體,不然無法選擇返回值類型。(目前沒找到合理方法,要在數據庫中建一個空表,無法接受,但在EF4.0中返回類型哪里可以新建ComplexType)。
所以在項目中少許的存儲過程就用ado.net了。現有一個示例場景,從northwind的Products表中,獲取一部分數據,得到產品編號,產品名稱,單價這三列。那么存儲過程中的sql語句:select ProductID,ProductName,UnitPrice from Products。
第一個版本的實現:
1、返回的數據實體如:
View Code
1
public
class
MyModel
2
{
3
public
int
ProductId {
get
;
set
; }
4
public
string
ProductName {
get
;
set
; }
5
public
decimal
UnitPrice {
get
;
set
; }
6
7
///
<summary>
8
///
創建該對象
9
///
</summary>
10
///
<param name="record"></param>
11
///
<returns></returns>
12
public
static
MyModel Create(IDataRecord record)
13
{
14
return
new
MyModel()
15
{
16
ProductId = Field<
int
>(record,
"
ProductID
"
),
17
ProductName = Field<
string
>(record,
"
ProductName
"
),
18
UnitPrice = Field<
decimal
>(record,
"
UnitPrice
"
)
19
};
20
}
21
///
<summary>
22
///
獲取某個字段的值
23
///
</summary>
24
///
<typeparam name="T"></typeparam>
25
///
<param name="record"></param>
26
///
<param name="fieldName"></param>
27
///
<returns></returns>
28
public
static
T Field<T>(IDataRecord record,
string
fieldName)
29
{
30
T fieldValue =
default
(T);
31
32
if
(record[fieldName] != DBNull.Value)
33
{
34
fieldValue = (T)record[fieldName];
35
}
36
37
return
fieldValue;
38
}
39
}
2、在數據訪問層(DAL)中,編寫一個泛型方法。如:
View Code
1
///
<summary>
2
///
執行存儲過程得到集合列表
3
///
</summary>
4
///
<typeparam name="TResult"></typeparam>
5
///
<param name="procedureName">
存儲過程名稱
</param>
6
///
<param name="creater">
創建對象的委托
</param>
7
///
<param name="parameters"></param>
8
///
<returns></returns>
9
public
List<TResult> ExecuteProcedureList<TResult>(
string
procedureName,Func<IDataRecord,TResult> creater,
params
SqlParameter[] parameters)
10
{
11
List<TResult> result =
new
List<TResult>();
12
13
//
get sqlconnection string
14
string
connString = (ObjectContext.Connection
as
EntityConnection).StoreConnection.ConnectionString;
15
16
SqlConnection conn =
new
SqlConnection(connString);
17
SqlCommand cmd = conn.CreateCommand();
18
cmd.CommandType = CommandType.StoredProcedure;
19
cmd.Parameters.AddRange(parameters);
20
cmd.CommandText = procedureName;
21
22
try
23
{
24
if
(conn.State != ConnectionState.Open)
25
{
26
conn.Open();
27
}
28
using
(DbDataReader reader = cmd.ExecuteReader())
29
{
30
while
(reader.Read())
31
{
32
TResult model = creater(reader);
33
result.Add(model);
34
}
35
}
36
}
37
catch
{
throw
; }
38
finally
39
{
40
cmd.Dispose();
41
conn.Close();
42
}
43
return
result;
44
}
3、調用代碼:
List<MyModel>
resultList= bll.ExecuteProcedureList<MyModel>(
"
proc_getlist
"
, MyModel.Create);
上面的代碼可能存在不足,比如:
1、MyModel中的Create方法是每個model必須自己實現(是否可以約束類型必須存在Create方法)。
2、MyModel中的Field泛型方法是用來獲取字段的值,它的功能獨立于該Model,可以抽離出來,寫在其他地方。
3、調用時候還必須傳遞Model中的創建對象的方法 等,是否我們可以進一步的封裝呢?
?
我的設計(第二版)是: 將Field方法和Create的聲明寫在一個抽象基類中,Field方法作為工具使用static,Create作為抽象方法,讓實現者必須實現類型的創建。
1
///
<summary>
2
///
作為 EF中使用存儲過程返回集合 對象的基類
3
///
</summary>
4
///
<typeparam name="TResult"></typeparam>
5
public
abstract
class
ProcedureModel
6
{
7
///
<summary>
8
///
獲取record中字段的值,如果不存在則返回默認值
9
///
</summary>
10
///
<typeparam name="T"></typeparam>
11
///
<param name="record"></param>
12
///
<param name="fieldName"></param>
13
///
<returns></returns>
14
public
static
T Field<T>(IDataRecord record,
string
fieldName)
15
{
16
T fieldValue =
default
(T);
17
18
if
(record[fieldName] != DBNull.Value)
19
{
20
fieldValue = (T)record[fieldName];
21
}
22
23
return
fieldValue;
24
}
25
26
///
<summary>
27
///
創建該對象
28
///
</summary>
29
///
<param name="record"></param>
30
///
<returns></returns>
31
public
abstract
ProcedureModel Create(IDataRecord record);
32
}
1
public
class
MyModel : ProcedureModel
2
{
3
public
int
ProductId {
get
;
set
; }
4
public
string
ProductName {
get
;
set
; }
5
public
decimal
UnitPrice {
get
;
set
; }
6
7
8
9
10
#region
創建實體的方法
11
12
public
override
ProcedureModel Create(IDataRecord record)
13
{
14
return
new
MyModel()
15
{
16
ProductId = Field<
int
>(record,
"
ProductID
"
),
17
ProductName = Field<
string
>(record,
"
ProductName
"
),
18
UnitPrice = Field<
string
>(record,
"
UnitPrice
"
)
19
};
20
}
21
22
#endregion
23
}
數據訪問層中:
1
///
<summary>
2
///
執行存儲過程返回集合
3
///
</summary>
4
///
<typeparam name="TResult">
實體類型
</typeparam>
5
///
<param name="procedureName">
儲存過程名稱
</param>
6
///
<param name="createTResult">
創建實體的方法
</param>
7
///
<param name="parameters">
存儲過程的參數
</param>
8
///
<returns></returns>
9
public
List<TResult> ExecuteProcedureList<TResult>(
string
procedureName,
params
SqlParameter[] parameters)
10
where
TResult : ProcedureModel,
new
()
11
{
12
List<TResult> result =
new
List<TResult>();
13
14
//
get sqlconnection string
15
string
connString = (ObjectContext.Connection
as
EntityConnection).StoreConnection.ConnectionString;
16
17
SqlConnection conn =
new
SqlConnection(connString);
18
SqlCommand cmd = conn.CreateCommand();
19
cmd.CommandType = CommandType.StoredProcedure;
20
cmd.Parameters.AddRange(parameters);
21
cmd.CommandText = procedureName;
22
23
24
try
25
{
26
if
(conn.State != ConnectionState.Open)
27
{
28
conn.Open();
29
}
30
31
Func<IDataRecord, ProcedureModel> creater = (
new
TResult()
as
ProcedureModel).Create;
32
33
using
(DbDataReader reader = cmd.ExecuteReader())
34
{
35
while
(reader.Read())
36
{
37
TResult model = (TResult)creater(reader);
38
result.Add(model);
39
}
40
}
41
}
42
catch
{
throw
; }
43
finally
44
{
45
cmd.Dispose();
46
conn.Close();
47
}
48
return
result;
49
}
調用:
List<MyModel>
resultList= bll.ExecuteProcedureList<MyModel>(
"
proc_getlist
"
);
?
通過以上可以看出,在第二個版本中,調用時去掉了對象創建方法的傳遞,如果其他開發者使用ExecuteProcedureList,則強制繼承ProdureModel抽象類,這樣的約束。
當然上面存儲過程沒有做分頁(數據量大的情況,會影響性能),根據需要可以自己實現。第二個版本的實現請在需要的地方合理使用。
對于第二個實現,我有一些問題想請教高手們。
1、對于創建對象的方法Create,我覺得實現為static比較好,這樣每個實例都可以共用一個Create方法,無需每個對象都有Create方法,如何做?
2、對于Create方法,返回值問題。現在是返回的基類型,會設計到類型轉換的問題,是否有性能影響(這里不涉及裝箱拆箱)。如果返回具體類型,該如何做?
3、對于在DAL的ExecuteProcedureList中,如何獲取創建對象的方法問題。目前是創建一個對象,通過多肽得到子類的實現方法,如果static如何做,(反射)?
4、總之,有沒有更好的設計呢?期待著大牛的指點,先謝謝啦
?
上面算是拋磚引玉吧,期待大家的回復和討論,我們共同進步,謝謝大家!
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

