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/

//我是结尾符,待续…

posted @ 2009-03-30 00:03  Teddy's Knowledge Base  Views(2909)  Comments(3Edit  收藏  举报