在以前ado.net時(shí)候,我們使用存儲(chǔ)過程返回一個(gè)列表,可以將結(jié)果集放在DataTable中,如果我們需要將結(jié)果集放在一個(gè)強(qiáng)類型集合(如List<T>)中我們?cè)撛趺醋瞿??之前在網(wǎng)上看到過一種解決方法,忘記出處了,請(qǐng)諒解。大概思路是: 在用DataReader讀取一行記錄時(shí),將該行創(chuàng)建為一個(gè)對(duì)象,然后添加到列表中 。
我在EF3.5中使用存儲(chǔ)過程,需要在edmx(領(lǐng)域模型中)文件中做函數(shù)導(dǎo)入(Function Import),并且返回值類型必須是數(shù)據(jù)庫中已存在的實(shí)體。
這樣做的缺憾(不夠靈活)如下:
1、必須要函數(shù)導(dǎo)入,如果后來修改或更新實(shí)體模型,要維護(hù)該函數(shù)。(可以接受)
2、存儲(chǔ)過程返回的對(duì)象必須是數(shù)據(jù)庫已存在的實(shí)體,不然無法選擇返回值類型。(目前沒找到合理方法,要在數(shù)據(jù)庫中建一個(gè)空表,無法接受,但在EF4.0中返回類型哪里可以新建ComplexType)。
所以在項(xiàng)目中少許的存儲(chǔ)過程就用ado.net了?,F(xiàn)有一個(gè)示例場(chǎng)景,從northwind的Products表中,獲取一部分?jǐn)?shù)據(jù),得到產(chǎn)品編號(hào),產(chǎn)品名稱,單價(jià)這三列。那么存儲(chǔ)過程中的sql語句:select ProductID,ProductName,UnitPrice from Products。
第一個(gè)版本的實(shí)現(xiàn):
1、返回的數(shù)據(jù)實(shí)體如:

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 /// 創(chuàng)建該對(duì)象
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 /// 獲取某個(gè)字段的值
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、在數(shù)據(jù)訪問層(DAL)中,編寫一個(gè)泛型方法。如:

1 /// <summary>
2 /// 執(zhí)行存儲(chǔ)過程得到集合列表
3 /// </summary>
4 /// <typeparam name="TResult"></typeparam>
5 /// <param name="procedureName"> 存儲(chǔ)過程名稱 </param>
6 /// <param name="creater"> 創(chuàng)建對(duì)象的委托 </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、調(diào)用代碼:
List<MyModel> resultList= bll.ExecuteProcedureList<MyModel>( " proc_getlist " , MyModel.Create);
上面的代碼可能存在不足,比如:
1、MyModel中的Create方法是每個(gè)model必須自己實(shí)現(xiàn)(是否可以約束類型必須存在Create方法)。
2、MyModel中的Field泛型方法是用來獲取字段的值,它的功能獨(dú)立于該Model,可以抽離出來,寫在其他地方。
3、調(diào)用時(shí)候還必須傳遞Model中的創(chuàng)建對(duì)象的方法 等,是否我們可以進(jìn)一步的封裝呢?
?
我的設(shè)計(jì)(第二版)是: 將Field方法和Create的聲明寫在一個(gè)抽象基類中,F(xiàn)ield方法作為工具使用static,Create作為抽象方法,讓實(shí)現(xiàn)者必須實(shí)現(xiàn)類型的創(chuàng)建。
1 /// <summary>
2 /// 作為 EF中使用存儲(chǔ)過程返回集合 對(duì)象的基類
3 /// </summary>
4 /// <typeparam name="TResult"></typeparam>
5 public abstract class ProcedureModel
6 {
7 /// <summary>
8 /// 獲取record中字段的值,如果不存在則返回默認(rèn)值
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 /// 創(chuàng)建該對(duì)象
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 創(chuàng)建實(shí)體的方法
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 }
數(shù)據(jù)訪問層中:
1 /// <summary>
2 /// 執(zhí)行存儲(chǔ)過程返回集合
3 /// </summary>
4 /// <typeparam name="TResult"> 實(shí)體類型 </typeparam>
5 /// <param name="procedureName"> 儲(chǔ)存過程名稱 </param>
6 /// <param name="createTResult"> 創(chuàng)建實(shí)體的方法 </param>
7 /// <param name="parameters"> 存儲(chǔ)過程的參數(shù) </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 }
調(diào)用:
List<MyModel> resultList= bll.ExecuteProcedureList<MyModel>( " proc_getlist " );
?
通過以上可以看出,在第二個(gè)版本中,調(diào)用時(shí)去掉了對(duì)象創(chuàng)建方法的傳遞,如果其他開發(fā)者使用ExecuteProcedureList,則強(qiáng)制繼承ProdureModel抽象類,這樣的約束。
當(dāng)然上面存儲(chǔ)過程沒有做分頁(數(shù)據(jù)量大的情況,會(huì)影響性能),根據(jù)需要可以自己實(shí)現(xiàn)。第二個(gè)版本的實(shí)現(xiàn)請(qǐng)?jiān)谛枰牡胤胶侠硎褂谩?
對(duì)于第二個(gè)實(shí)現(xiàn),我有一些問題想請(qǐng)教高手們。
1、對(duì)于創(chuàng)建對(duì)象的方法Create,我覺得實(shí)現(xiàn)為static比較好,這樣每個(gè)實(shí)例都可以共用一個(gè)Create方法,無需每個(gè)對(duì)象都有Create方法,如何做?
2、對(duì)于Create方法,返回值問題?,F(xiàn)在是返回的基類型,會(huì)設(shè)計(jì)到類型轉(zhuǎn)換的問題,是否有性能影響(這里不涉及裝箱拆箱)。如果返回具體類型,該如何做?
3、對(duì)于在DAL的ExecuteProcedureList中,如何獲取創(chuàng)建對(duì)象的方法問題。目前是創(chuàng)建一個(gè)對(duì)象,通過多肽得到子類的實(shí)現(xiàn)方法,如果static如何做,(反射)?
4、總之,有沒有更好的設(shè)計(jì)呢?期待著大牛的指點(diǎn),先謝謝啦
?
上面算是拋磚引玉吧,期待大家的回復(fù)和討論,我們共同進(jìn)步,謝謝大家!
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
