您的位置:首页 > 移动开发

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

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);
}

然后,我们可以为上面这个服务契约,定义一个基于查询对象的默认的实现:

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控件的实现摘要:

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
}

参考

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

//我是结尾符,待续…
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐