Apply SOA Design Patterns with WCF (5) WCF Based ASP.NET DataSouce Control (基於WCF的數據源控件)

Original (原創) by Teddy’s Knowledge Base

Content (目錄)

(1) WCF Configuration Centralization (WCF配置集中管理)

(2) WCF Automatic Deployment (WCF自動化部署)

(3) WCF Automatic Service Locating (WCF自動化服務定位)

(4) WCF Database Paging & Sorting (WCF數據庫分頁和排序)

(5) WCF Based ASP.NET DataSouce Control (基於WCF的數據源控件)

(6) 1 + 2 + 3 + 4 + 5 = ?

English Version

摘要

本文介紹如何實現一個基於WCF的ASP.NET數據源控件,從而使得跨WCF通信的數據庫CRUD,尤其是複雜的分頁排序更簡單。

正文

我們需要基於WCF的ASP.NET數據源控件嗎?

ASP.NET的數據源設計是的 ASP.NET頁面上的數據綁定十分簡單,但是.NET Framework到目前爲止內置提供的DataSource控件,對WCF的支持都不是很方便。SqlDataSource和LinqDataSource暴露了太多SQL的細節,僅支持SQL Server數據庫,並且完全不支持WCF;ObjectDataSOource則太通用了,以至於僅僅爲了實現一個很簡單的數據庫分頁排序功能也需要寫很多代碼。因此,如果我們基於WCF和ASP.NET來實現SOA,那麼,一個基於WCF的ASP.NET數據源控件絕對值得去設計。

我們手頭都有哪些武器了?

實現

首先,我們可以定義一個爲所有的查詢共享的WCF服務契約。下面的代碼是IQueryService服務契約:

 1     [ServiceContract]
 2     public interface IQueryService
 3     {
 4         [OperationContract]
 5         DataTable Select(Criteria criteria);
 6         [OperationContract]
 7         int SelectCount(Criteria criteria);
 8         [OperationContract]
 9         int Update(Criteria criteria, DataTable modifiedTable, ConflictOption conflictDetection);
10     }

然後,我們可以爲上面這個服務契約,定義一個基於查詢對象的默認的實現:

 1     public sealed class QueryService : IQueryService
 2     {
 3         public DataTable Select(Criteria criteria)
 4         {
 5             if (criteria == null)
 6                 throw new ArgumentNullException("criteria");
 7 
 8             using (var adapter = new QueryCommandFactory(criteria).GetQueryDataAdapter())
 9             {
10                 var connection = adapter.SelectCommand.Connection;
11                 try
12                 {
13                     if (connection.State != ConnectionState.Open)
14                         connection.Open();
15                     var table = new DataTable(criteria._tableName);
16                     adapter.Fill(table);
17                     return table;
18                 }
19                 finally
20                 {
21                     if (connection.State != ConnectionState.Closed)
22                         connection.Close();
23                     connection.Dispose();
24                 }
25             }
26         }
27 
28         public int SelectCount(Criteria criteria)
29         {
30             if (criteria == null)
31                 throw new ArgumentNullException("criteria");
32 
33             using (var cmd = new QueryCommandFactory(criteria).GetCountCommand())
34             {
35                 var connection = cmd.Connection;
36                 try
37                 {
38                     if (connection.State != ConnectionState.Open)
39                         connection.Open();
40                     return Convert.ToInt32(cmd.ExecuteScalar());
41                 }
42                 finally
43                 {
44                     if (connection.State != ConnectionState.Closed)
45                         connection.Close();
46                     connection.Dispose();
47                 }
48             }
49         }
50 
51         public int Update(Criteria criteria, DataTable modifiedTable, ConflictOption conflictDetection)
52         {
53             if (criteria == null)
54                 throw new ArgumentNullException("criteria");
55 
56             using (var adapter = new QueryCommandFactory(criteria).GetUpdatableQueryDataAdapter(conflictDetection))
57             {
58                 var connection = adapter.SelectCommand.Connection;
59                 try
60                 {
61                     if (connection.State != ConnectionState.Open)
62                         connection.Open();
63                     return adapter.Update(modifiedTable);
64                 }
65                 finally
66                 {
67                     if (connection.State != ConnectionState.Closed)
68                         connection.Close();
69                     connection.Dispose();
70                 }
71             }
72         }
73     }

最後,只要通過能和第三方服務定位器整合的ServiceManager類,參見文章(3),如果我們能實現一個帶一個Criteria屬性作爲查詢的輸入的數據源控件,我們就能很容易的基於能自動化的定位的IQueryService服務的實現CRUD。要實現一個自定義的自定義的數據源控件,可以參考.NET Framework中的SqlDataSource控件的實現,下面的代碼是這個基於WCF的QueryDataSource控件的實現摘要:

  1     public sealed class QueryDataSource : DataSourceControl
  2     {
  3         #region Private Fields
  4 
  5         //
  6 
  7         #endregion
  8 
  9         #region Protected Methods
 10 
 11         protected override DataSourceView GetView(string viewName)
 12         {
 13             if (_view == null)
 14                 _view = new QueryDataSourceView(this);
 15             return _view;
 16         }
 17 
 18         protected override void OnInit(System.EventArgs e)
 19         {
 20             base.OnInit(e);
 21 
 22             if (HttpContext.Current == null)
 23                 return;
 24             if (!UseLocalQueryService)
 25             {
 26                 _locator = ServiceManager.GetServiceLocator(typeof(IQueryService));
 27                 _service = _locator.GetService<IQueryService>();
 28             }
 29             else
 30             {
 31                 var serviceType = Type.GetType(_defaultQueryServiceImplType);
 32                 _service = (IQueryService)Activator.CreateInstance(serviceType);
 33                 if (_service == null)
 34                     throw new FileLoadException("Could not load assembly - NIntegrate.Query.Command.dll.");
 35             }
 36         }
 37 
 38         #endregion
 39 
 40         #region Public Properties
 41 
 42         [Category("Data"), DefaultValue(false), Description("When the value of this property equals true, it always using NIntegrate.Query.Command.QueryService class as QueryService insteads of trying to get the IQueryService implementation instance from ServiceManager class.")]
 43         public bool UseLocalQueryService { getset; }
 44 
 45         [Category("Data"), Description("Specify the criteria.")]
 46         public Criteria Criteria
 47         {
 48             internal get
 49             {
 50                 if (_criteria == null && EnableViewState
 51                     && ViewState["Criteria"!= null)
 52                 {
 53                     _criteria = QueryHelper.CriteriaDeserialize(
 54                         (string)ViewState["Criteria"]);
 55                 }
 56 
 57                 return _criteria;
 58             }
 59             set
 60             {
 61                 if (value == null)
 62                     return;
 63 
 64                 _criteria = value;
 65                 if (EnableViewState)
 66                     ViewState["Criteria"= QueryHelper.CriteriaSerialize(value.ToBaseCriteria());
 67             }
 68         }
 69 
 70         //
 71 
 72         #endregion
 73 
 74         #region Events
 75 
 76         //
 77 
 78         #endregion
 79 
 80         #region Dispose()
 81 
 82         public override void Dispose()
 83         {
 84             Dispose(true);
 85             GC.SuppressFinalize(this);
 86         }
 87 
 88         private bool disposed;
 89 
 90         private void Dispose(bool disposing)
 91         {
 92             if (disposed) return;
 93             if (disposing)
 94             {
 95                 var dispose = _service as IDisposable;
 96                 if (dispose != null)
 97                     dispose.Dispose();
 98                 if (_locator != null)
 99                     _locator.Dispose();
100             }
101 
102             disposed = true;
103         }
104 
105         ~QueryDataSource()
106         {
107             Dispose(false);
108         }
109 
110         #endregion
111     }
112 
113     internal sealed class QueryDataSourceView : DataSourceView
114         {
115             #region Private Membes
116 
117             //
118 
119             #endregion
120 
121             #region Constructors
122 
123             internal QueryDataSourceView(QueryDataSource owner)
124                 : base(owner, "Default")
125             {
126                 if (owner == null)
127                     throw new ArgumentNullException("owner");
128 
129                 _owner = owner;
130             }
131 
132             #endregion
133 
134             #region Public Properties
135 
136             public override bool CanInsert
137             {
138                 get
139                 {
140                     return true;
141                 }
142             }
143 
144             public override bool CanUpdate
145             {
146                 get
147                 {
148                     return true;
149                 }
150             }
151 
152             public override bool CanDelete
153             {
154                 get
155                 {
156                     return true;
157                 }
158             }
159 
160             public override bool CanRetrieveTotalRowCount
161             {
162                 get
163                 {
164                     return true;
165                 }
166             }
167 
168             public override bool CanPage
169             {
170                 get
171                 {
172                     return true;
173                 }
174             }
175 
176             public override bool CanSort
177             {
178                 get
179                 {
180                     return true;
181                 }
182             }
183 
184             #endregion
185 
186             #region Protected Methods
187 
188             protected override int ExecuteInsert(IDictionary values)
189             {
190                 //
191 
192                 var table = _owner._service.Select(criteria);
193                 var row = table.NewRow();
194                 var en = values.GetEnumerator();
195                 while (en.MoveNext())
196                 {
197                     if (table.Columns.Contains(en.Key.ToString()))
198                         row[en.Key.ToString()] = TransformType(table.Columns[en.Key.ToString()].DataType, en.Value);
199                 }
200                 table.Rows.Add(row);
201 
202                 var conflictDetection = ConflictOption.OverwriteChanges;
203                 if (_owner.ConflictDetection == ConflictOptions.CompareAllValues)
204                 {
205                     conflictDetection = ConflictOption.CompareAllSearchableValues;
206                 }
207 
208                 var affectedRows = _owner._service.Update(_owner.Criteria, table, conflictDetection);
209 
210                 //
211             }
212 
213             protected override int ExecuteUpdate(IDictionary keys, IDictionary values, IDictionary oldValues)
214             {
215                //
216 
217                 var table = _owner._service.Select(criteria);
218                 if (table == null || table.Rows.Count == 0)
219                     throw new DataException("No row is matching specified key values.");
220                 if (table.Rows.Count > 1)
221                     throw new DataException("More than one rows are matching specified key values, please check the key columns setting.");
222                 var row = table.Rows[0];
223                 var conflictDetection = ConflictOption.OverwriteChanges;
224                 if (_owner.ConflictDetection == ConflictOptions.CompareAllValues)
225                 {
226                     DetectDataRowConflicts(oldValues, row);
227                     conflictDetection = ConflictOption.CompareAllSearchableValues;
228                 }
229 
230                 var en = values.GetEnumerator();
231                 while (en.MoveNext())
232                 {
233                     if (table.Columns.Contains(en.Key.ToString()))
234                         row[en.Key.ToString()] = TransformType(table.Columns[en.Key.ToString()].DataType, en.Value);
235                 }
236 
237                 var affectedRows = _owner._service.Update(_owner.Criteria, table, conflictDetection);
238 
239                 //
240             }
241 
242             protected override int ExecuteDelete(IDictionary keys, IDictionary oldValues)
243             {
244                 //
245 
246                 var table = _owner._service.Select(criteria);
247                 if (table == null || table.Rows.Count == 0)
248                     throw new DataException("No row is matching specified key values.");
249                 if (table.Rows.Count > 1)
250                     throw new DataException("More than one rows are matching specified key values, please check the key columns setting.");
251 
252                 var row = table.Rows[0];
253                 var conflictDetection = ConflictOption.OverwriteChanges;
254                 if (_owner.ConflictDetection == ConflictOptions.CompareAllValues)
255                 {
256                     DetectDataRowConflicts(oldValues, row);
257                     conflictDetection = ConflictOption.CompareAllSearchableValues;
258                 }
259 
260                 row.Delete();
261 
262                 var affectedRows = _owner._service.Update(_owner.Criteria, table, conflictDetection);
263                 //
264             }
265 
266             protected override IEnumerable ExecuteSelect(DataSourceSelectArguments arguments)
267             {
268                 //
269 
270                 if (arguments != null && arguments != DataSourceSelectArguments.Empty)
271                 {
272                     //adjust criteria according to arguments
273                     if (arguments.RetrieveTotalRowCount)
274                     {
275                         arguments.TotalRowCount = _owner._service.SelectCount(criteria);
276                         _owner.LastTotalCount = arguments.TotalRowCount;
277                     }
278                     if (arguments.MaximumRows > 0)
279                         criteria.MaxResults(arguments.MaximumRows);
280                     if (arguments.StartRowIndex > 0)
281                         criteria.SkipResults(arguments.StartRowIndex);
282                     if (!string.IsNullOrEmpty(arguments.SortExpression))
283                     {
284                         if (_owner.AlwaysAppendDefaultSortBysWhenSorting)
285                             InsertSortExpressinAtTopOfSortBys(arguments.SortExpression, criteria);
286                         else
287                         {
288                             criteria._sortBys.Clear();
289                             AppendSortExpression(criteria, arguments.SortExpression);
290                         }
291                     }
292                 }
293 
294                 return new DataView(_owner._service.Select(criteria));
295             }
296 
297             #endregion
298         }

參考

(1) SOA Design Pattern Catalog: http://www.soapatterns.org/

//我是結尾符,待續…

Tag標籤: nintegrate soa
發佈了187 篇原創文章 · 獲贊 1 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章