Apply SOA Design Patterns with WCF (5) WCF Based ASP.NET DataSouce Control (基于WCF的数据源控件)
2009-03-30 00:03
375 查看
Original (原创) by Teddy’s Knowledge Base
(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 = ?
[b]English Version[/b]
自动、透明的WCF服务发布和定位,参见文章(2)和文章(3)
一组可以跨WCF通信的查询类,参见文章(4)
然后,我们可以为上面这个服务契约,定义一个基于查询对象的默认的实现:
//我是结尾符,待续…
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 = ?
[b]English Version[/b]
摘要
本文介绍如何实现一个基于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数据源控件绝对值得去设计。我们手头都有哪些武器了?
集中化的配置管理,参见文章(1)自动、透明的WCF服务发布和定位,参见文章(2)和文章(3)
一组可以跨WCF通信的查询类,参见文章(4)
实现
首先,我们可以定义一个为所有的查询共享的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);
}
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);
}
然后,我们可以为上面这个服务契约,定义一个基于查询对象的默认的实现:
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 {
var connection = adapter.SelectCommand.Connection;
try
{
if (connection.State != ConnectionState.Open)
connection.Open();
var table = new DataTable(criteria._tableName);
adapter.Fill(table);
return table;
}
finally
{
if (connection.State != ConnectionState.Closed)
connection.Close();
connection.Dispose();
}
}
}
public int SelectCount(Criteria criteria)
{
if (criteria == null)
throw new ArgumentNullException("criteria");
using (var cmd = new QueryCommandFactory(criteria).GetCountCommand())
{
var connection = cmd.Connection;
try
{
if (connection.State != ConnectionState.Open)
connection.Open();
return Convert.ToInt32(cmd.ExecuteScalar());
}
finally
{
if (connection.State != ConnectionState.Closed)
connection.Close();
connection.Dispose();
}
}
}
public int Update(Criteria criteria, DataTable modifiedTable, ConflictOption conflictDetection)
{
if (criteria == null)
throw new ArgumentNullException("criteria");
using (var adapter = new QueryCommandFactory(criteria).GetUpdatableQueryDataAdapter(conflictDetection))
{
var connection = adapter.SelectCommand.Connection;
try
{
if (connection.State != ConnectionState.Open)
connection.Open();
return adapter.Update(modifiedTable);
}
finally
{
if (connection.State != ConnectionState.Closed)
connection.Close();
connection.Dispose();
}
}
}
}
最后,只要通过能和第三方服务定位器整合的ServiceManager类,参见文章(3),如果我们能实现一个带一个Criteria属性作为查询的输入的数据源控件,我们就能很容易的基于能自动化的定位的IQueryService服务的实现CRUD。要实现一个自定义的自定义的数据源控件,可以参考.NET Framework中的SqlDataSource控件的实现,下面的代码是这个基于WCF的QueryDataSource控件的实现摘要: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 {
var connection = adapter.SelectCommand.Connection;
try
{
if (connection.State != ConnectionState.Open)
connection.Open();
var table = new DataTable(criteria._tableName);
adapter.Fill(table);
return table;
}
finally
{
if (connection.State != ConnectionState.Closed)
connection.Close();
connection.Dispose();
}
}
}
public int SelectCount(Criteria criteria)
{
if (criteria == null)
throw new ArgumentNullException("criteria");
using (var cmd = new QueryCommandFactory(criteria).GetCountCommand())
{
var connection = cmd.Connection;
try
{
if (connection.State != ConnectionState.Open)
connection.Open();
return Convert.ToInt32(cmd.ExecuteScalar());
}
finally
{
if (connection.State != ConnectionState.Closed)
connection.Close();
connection.Dispose();
}
}
}
public int Update(Criteria criteria, DataTable modifiedTable, ConflictOption conflictDetection)
{
if (criteria == null)
throw new ArgumentNullException("criteria");
using (var adapter = new QueryCommandFactory(criteria).GetUpdatableQueryDataAdapter(conflictDetection))
{
var connection = adapter.SelectCommand.Connection;
try
{
if (connection.State != ConnectionState.Open)
connection.Open();
return adapter.Update(modifiedTable);
}
finally
{
if (connection.State != ConnectionState.Closed)
connection.Close();
connection.Dispose();
}
}
}
}
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 { get; set; }
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();
}
disposed = true;
}
~QueryDataSource()
{
Dispose(false);
}
#endregion
}
internal sealed class QueryDataSourceView : DataSourceView
{
#region Private Membes
//…
#endregion
#region Constructors
internal QueryDataSourceView(QueryDataSource owner)
: base(owner, "Default")
{
if (owner == null)
throw new ArgumentNullException("owner");
_owner = owner;
}
#endregion
#region Public Properties
public override bool CanInsert
{
get
{
return true;
}
}
public override bool CanUpdate
{
get
{
return true;
}
}
public override bool CanDelete
{
get
{
return true;
}
}
public override bool CanRetrieveTotalRowCount
{
get
{
return true;
}
}
public override bool CanPage
{
get
{
return true;
}
}
public override bool CanSort
{
get
{
return true;
}
}
#endregion
#region Protected Methods
protected override int ExecuteInsert(IDictionary values)
{
//…
var table = _owner._service.Select(criteria);
var row = table.NewRow();
var en = values.GetEnumerator();
while (en.MoveNext())
{
if (table.Columns.Contains(en.Key.ToString()))
row[en.Key.ToString()] = TransformType(table.Columns[en.Key.ToString()].DataType, en.Value);
}
table.Rows.Add(row);
var conflictDetection = ConflictOption.OverwriteChanges;
if (_owner.ConflictDetection == ConflictOptions.CompareAllValues)
{
conflictDetection = ConflictOption.CompareAllSearchableValues;
}
var affectedRows = _owner._service.Update(_owner.Criteria, table, conflictDetection);
//…
}
protected override int ExecuteUpdate(IDictionary keys, IDictionary values, IDictionary oldValues)
{
//…
var table = _owner._service.Select(criteria);
if (table == null || table.Rows.Count == 0)
throw new DataException("No row is matching specified key values.");
if (table.Rows.Count > 1)
throw new DataException("More than one rows are matching specified key values, please check the key columns setting.");
var row = table.Rows[0];
var conflictDetection = ConflictOption.OverwriteChanges;
if (_owner.ConflictDetection == ConflictOptions.CompareAllValues)
{
DetectDataRowConflicts(oldValues, row);
conflictDetection = ConflictOption.CompareAllSearchableValues;
}
var en = values.GetEnumerator();
while (en.MoveNext())
{
if (table.Columns.Contains(en.Key.ToString()))
row[en.Key.ToString()] = TransformType(table.Columns[en.Key.ToString()].DataType, en.Value);
}
var affectedRows = _owner._service.Update(_owner.Criteria, table, conflictDetection);
//…
}
protected override int ExecuteDelete(IDictionary keys, IDictionary oldValues)
{
//…
var table = _owner._service.Select(criteria);
if (table == null || table.Rows.Count == 0)
throw new DataException("No row is matching specified key values.");
if (table.Rows.Count > 1)
throw new DataException("More than one rows are matching specified key values, please check the key columns setting.");
var row = table.Rows[0];
var conflictDetection = ConflictOption.OverwriteChanges;
if (_owner.ConflictDetection == ConflictOptions.CompareAllValues)
{
DetectDataRowConflicts(oldValues, row);
conflictDetection = ConflictOption.CompareAllSearchableValues;
}
row.Delete();
var affectedRows = _owner._service.Update(_owner.Criteria, table, conflictDetection);
//…
}
protected override IEnumerable ExecuteSelect(DataSourceSelectArguments arguments)
{
//…
if (arguments != null && arguments != DataSourceSelectArguments.Empty)
{
//adjust criteria according to arguments
if (arguments.RetrieveTotalRowCount)
{
arguments.TotalRowCount = _owner._service.SelectCount(criteria);
_owner.LastTotalCount = arguments.TotalRowCount;
}
if (arguments.MaximumRows > 0)
criteria.MaxResults(arguments.MaximumRows);
if (arguments.StartRowIndex > 0)
criteria.SkipResults(arguments.StartRowIndex);
if (!string.IsNullOrEmpty(arguments.SortExpression))
{
if (_owner.AlwaysAppendDefaultSortBysWhenSorting)
InsertSortExpressinAtTopOfSortBys(arguments.SortExpression, criteria);
else
{
criteria._sortBys.Clear();
AppendSortExpression(criteria, arguments.SortExpression);
}
}
}
return new DataView(_owner._service.Select(criteria));
}
#endregion
}
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 { get; set; }
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();
}
disposed = true;
}
~QueryDataSource()
{
Dispose(false);
}
#endregion
}
internal sealed class QueryDataSourceView : DataSourceView
{
#region Private Membes
//…
#endregion
#region Constructors
internal QueryDataSourceView(QueryDataSource owner)
: base(owner, "Default")
{
if (owner == null)
throw new ArgumentNullException("owner");
_owner = owner;
}
#endregion
#region Public Properties
public override bool CanInsert
{
get
{
return true;
}
}
public override bool CanUpdate
{
get
{
return true;
}
}
public override bool CanDelete
{
get
{
return true;
}
}
public override bool CanRetrieveTotalRowCount
{
get
{
return true;
}
}
public override bool CanPage
{
get
{
return true;
}
}
public override bool CanSort
{
get
{
return true;
}
}
#endregion
#region Protected Methods
protected override int ExecuteInsert(IDictionary values)
{
//…
var table = _owner._service.Select(criteria);
var row = table.NewRow();
var en = values.GetEnumerator();
while (en.MoveNext())
{
if (table.Columns.Contains(en.Key.ToString()))
row[en.Key.ToString()] = TransformType(table.Columns[en.Key.ToString()].DataType, en.Value);
}
table.Rows.Add(row);
var conflictDetection = ConflictOption.OverwriteChanges;
if (_owner.ConflictDetection == ConflictOptions.CompareAllValues)
{
conflictDetection = ConflictOption.CompareAllSearchableValues;
}
var affectedRows = _owner._service.Update(_owner.Criteria, table, conflictDetection);
//…
}
protected override int ExecuteUpdate(IDictionary keys, IDictionary values, IDictionary oldValues)
{
//…
var table = _owner._service.Select(criteria);
if (table == null || table.Rows.Count == 0)
throw new DataException("No row is matching specified key values.");
if (table.Rows.Count > 1)
throw new DataException("More than one rows are matching specified key values, please check the key columns setting.");
var row = table.Rows[0];
var conflictDetection = ConflictOption.OverwriteChanges;
if (_owner.ConflictDetection == ConflictOptions.CompareAllValues)
{
DetectDataRowConflicts(oldValues, row);
conflictDetection = ConflictOption.CompareAllSearchableValues;
}
var en = values.GetEnumerator();
while (en.MoveNext())
{
if (table.Columns.Contains(en.Key.ToString()))
row[en.Key.ToString()] = TransformType(table.Columns[en.Key.ToString()].DataType, en.Value);
}
var affectedRows = _owner._service.Update(_owner.Criteria, table, conflictDetection);
//…
}
protected override int ExecuteDelete(IDictionary keys, IDictionary oldValues)
{
//…
var table = _owner._service.Select(criteria);
if (table == null || table.Rows.Count == 0)
throw new DataException("No row is matching specified key values.");
if (table.Rows.Count > 1)
throw new DataException("More than one rows are matching specified key values, please check the key columns setting.");
var row = table.Rows[0];
var conflictDetection = ConflictOption.OverwriteChanges;
if (_owner.ConflictDetection == ConflictOptions.CompareAllValues)
{
DetectDataRowConflicts(oldValues, row);
conflictDetection = ConflictOption.CompareAllSearchableValues;
}
row.Delete();
var affectedRows = _owner._service.Update(_owner.Criteria, table, conflictDetection);
//…
}
protected override IEnumerable ExecuteSelect(DataSourceSelectArguments arguments)
{
//…
if (arguments != null && arguments != DataSourceSelectArguments.Empty)
{
//adjust criteria according to arguments
if (arguments.RetrieveTotalRowCount)
{
arguments.TotalRowCount = _owner._service.SelectCount(criteria);
_owner.LastTotalCount = arguments.TotalRowCount;
}
if (arguments.MaximumRows > 0)
criteria.MaxResults(arguments.MaximumRows);
if (arguments.StartRowIndex > 0)
criteria.SkipResults(arguments.StartRowIndex);
if (!string.IsNullOrEmpty(arguments.SortExpression))
{
if (_owner.AlwaysAppendDefaultSortBysWhenSorting)
InsertSortExpressinAtTopOfSortBys(arguments.SortExpression, criteria);
else
{
criteria._sortBys.Clear();
AppendSortExpression(criteria, arguments.SortExpression);
}
}
}
return new DataView(_owner._service.Select(criteria));
}
#endregion
}
参考
(1) SOA Design Pattern Catalog: http://www.soapatterns.org///我是结尾符,待续…
相关文章推荐
- Apply SOA Design Patterns with WCF (5) WCF Based ASP.NET DataSouce Control (基于WCF的数据源控件)
- Apply SOA Design Patterns with WCF (1) Configuration Centralization (配置集中管理)
- Apply SOA Design Patterns with WCF (2) WCF Automatic Deployment (自动化部署)
- Apply SOA Design Patterns with WCF (3) Automatic Service Locating (自动化服务定位)
- Apply SOA Design Patterns with WCF (4) WCF Database Paging & Sorting (数据库端分页和排序)
- Apply SOA Design Patterns with WCF (1) Configuration Centralization (配置集中管理)
- Apply SOA Design Patterns with WCF (4) WCF Database Paging & Sorting (数据库端分页和排序)
- Apply SOA Design Patterns with WCF (2) WCF Automatic Deployment (自动化部署)
- Apply SOA Design Patterns with WCF (3) Automatic Service Locating (自动化服务定位)
- AS.NET2.0 用户控件错误!!The base class includes the field 'MyControl_1', but its type (MyControl) is not compatible with the type of control (ASP.MyControl_ascx).
- [原创推荐]基于ASP.NET Control Toolkit仿Google的AutoComplete控件
- ASP.NET AJAX Control Toolkit 控件说明
- asp.net高级开发之数据源控件小总结
- Asp.net 2.0 自定义控件开发专题讲解[为用户控件增加DataSource属性, 能够自动识别不同数据源](示例代码下载)
- An ASP.NET Gridview Control With Custom Paging (Technical)
- 《庖丁解牛:纵向切入ASP.NET 3.5控件和组件开发技术》 --- 基于VS 2008(兼容VS 2005)/C#
- ASP.NET AjaxControlToolkit-Framework4.0 配置实用(简单介绍CalendarExtender日期控件)
- ASP.NET AJAX Control Toolkit控件使用一览表
- Asp.Net 基于客户端回调的进度条控件
- WCF技术剖析之六:为什么在基于ASP.NET应用寄宿(Hosting)下配置的BaseAddress无效