现在做一个项目,在数据库和表名以及列名都不确定的情况下,返回查询的数据集。这在ASP.NET或者C/S模式很容易实现。但是在现有的SL+WCF技术下是不能实现的,首先返回值就不好确定,WCF的返回值类型必须是确定的。比较返回值为Object类型就不可以。而且返回DataTable和DataSet又不可以。在客户接收不到数据,返回的数据类型为Object。那么我们可以通过普通的方式取得数据集,然后把数据集转换为XML格式的字符串,然后在客户端把字符串解析为类型DataSet数据集。
其实把DataSet数据集转化为XML格式的字符串以及把XML格式的字符串解析为DataSet数据集的工作,已经有加拿大的同行把业务逻辑封装好了,我们只要调用其中的方法就可以了,你也可以反编译看看老外的代码,老外的原文地址:
http://silverlightdataset.net/silverlightdataset/Default.aspx,其中用到的组件为Silverlight.DataSetConnector.dll(服务端使用)和Silverlight.DataSet.dll(用于客户端)
下面是服务端的程序
/// <summary>
/// 取得数据集
/// </summary>
/// <param name="strDataBase">服务器名称</param>
/// <param name="strDataTable">表名</param>
/// <param name="data">XML格式</param>
/// <param name="page">当前分页</param>
/// <param name="pageCount">分页总数</param>
/// <param name="strTableKeys">表主键</param>
/// <param name="strTableOrder">排列字段</param>
/// <returns></returns>
public string GetAllOriginalMetaData(string strDataBase, string strDataTable, string data, int page, out int pageCount,string strTableKeys,string strTableOrder)
{
pageCount = 0;
try
{
DataSet dataSet = Connector.FromXml(data);
//由于数据库和数据表的不确定性,所以不用LINQ,直接访问数据库
using (SqlConnection sqlConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["MetadataConnectionString"].ConnectionString))
{
try
{
//判断连接是否打开
if (sqlConnection.State == ConnectionState.Closed)
{
sqlConnection.Open();
}
string strTable = strDataBase + ".dbo." + strDataTable;
//判断是否第一次加载数据,如果是,则获取总页数
if (page == -1)
{
string strsql = "SELECT Count(*) as Rowscount FROM " + strTable;
SqlCommand scCount = new SqlCommand(strsql, sqlConnection);
pageCount = Convert.ToInt32(scCount.ExecuteScalar());
page+=2;
}
SqlCommand sc = new SqlCommand();
//使用存储过程
sc.CommandType = CommandType.StoredProcedure;
//存储过程名
sc.CommandText = "PrcPager";
sc.Connection = sqlConnection;
//存储过程参数
SqlParameter[] parameters = {
new SqlParameter("@currPage", SqlDbType.Int,4),//当前页
new SqlParameter("@tabName",SqlDbType.NVarChar,2000),//表名
new SqlParameter("@ascColumn",SqlDbType.NVarChar,100),//排序字段
new SqlParameter("@bitOrderType",SqlDbType.Bit),
new SqlParameter("@pkColumn",SqlDbType.NVarChar,100)//主键字段
};
parameters[0].Value = page;
parameters[1].Value = strTable;
parameters[2].Value = strTableOrder;
parameters[3].Value =1;
parameters[4].Value = strTableKeys;
sc.Parameters.AddRange(parameters);
SqlDataAdapter sqlDataAdapter = new SqlDataAdapter();
sqlDataAdapter.SelectCommand = sc;
sqlDataAdapter.Fill(dataSet, "TableName");
//返回XML格式的字符串数据
return Connector.ToXml(dataSet);
}
catch (Exception e)
{
AccessLog.WriteLog(e.Message);
return null;
}
finally
{
sqlConnection.Close();
}
}
}
catch (Exception e)
{
//AccessLog.WriteLog(e.Message);
return null;
}
}
由于返回的集合是以XML格式的字符串,所以如果数据量太大时,可以要占用大量的带宽,也可能会超时。所以使用分页,每次返回的数据集为当前的数据集。
下面是分页存储过程
IF EXISTS(SELECT Name FROM SYSOBJECTS WHERE Name='PrcPager' AND TYPE='P')
DROP PROCEDURE PrcPager
GO
CREATE PROCEDURE PrcPager
-- 获得某一页的数据 --
@currPage int = 1, --当前页页码 (即Top currPage)
@showColumn varchar(2000) = '*', --需要得到的字段 (即 column1,column2,......)
@tabName varchar(2000), --需要查看的表名 (即 from table_name)
@strCondition varchar(2000) = '', --查询条件 (即 where condition......) 不用加where关键字
@ascColumn varchar(100) = '', --排序的字段名 (即 order by column asc/desc)
@bitOrderType bit = 0, ---排序的类型 (0为升序,1为降序)
@pkColumn varchar(100) = '', --主键名称
@pageSize int = 20 --分页大小
AS
BEGIN -- 存储过程开始
-- 该存储过程需要用到的几个变量
DECLARE @strSql varchar(4000) --该存储过程最后执行的语句
DECLARE @strOrderType varchar(1000) --排序类型语句 (order by column asc或者order by column desc)
BEGIN
IF @bitOrderType = 1 -- bitOrderType=1即执行降序
BEGIN
SET @strOrderType = ' ORDER BY '+@ascColumn+' DESC'
END
ELSE
BEGIN
SET @strOrderType = ' ORDER BY '+@ascColumn+' ASC'
END
IF @currPage = 1 -- 如果是第一页
BEGIN
IF @strCondition != ''
SET @strSql = 'SELECT TOP '+STR(@pageSize)+' '+@showColumn+' FROM '+@tabName+
' WHERE '+@strCondition+@strOrderType
ELSE
SET @strSql = 'SELECT TOP '+STR(@pageSize)+' '+@showColumn+' FROM '+@tabName+@strOrderType
END
ELSE -- 其他页
BEGIN
IF @strCondition !=''
SET @strSql ='select top '+str(@pageSize)+' '+@showColumn+' from (select top '+str(@currPage*@pageSize)+@showColumn+' from '
+@tabName+' WHERE ('+@strCondition+') '+@strOrderType+') as temp where '+@pkColumn+' not in (SELECT TOP '+STR((@currPage-1)*@pageSize)
+' '+@pkColumn+ ' FROM '+@tabName+' WHERE ('+@strCondition+') ' +@strOrderType+')'
ELSE
SET @strSql ='select top '+str(@pageSize)+' '+@showColumn+' from (select top '+str(@currPage*@pageSize)+@showColumn+' from '+@tabName+@strOrderType+') as temp'
+' where '+@pkColumn+' not in (SELECT TOP '+STR((@currPage-1)*@pageSize)+' '+@pkColumn+ ' FROM '+@tabName+@strOrderType+')'
END
END
EXEC (@strSql)
--print @strSql
END -- 存储过程结束
GO
下面就来介绍客户端。其实按照加拿大的老兄的代码很简单,只要使用下面的代码就可以了
a) Add reference to Silverlight.DataSet.dll from Silverlight Application project and “using” directive to your page:
using Silverlight;
b) Create Silverlight DataSet and send it to server;
DataSet dataSet = new DataSet();
// Cretate DataTables, DataColumns, and add DataRows ...
// Use your knowledge about ADO.Net DataSet for this task
// Call WCF service
proxy.ProcessRequestAsync(dataSet.ToXml(true));
c) Receive response from server:
void proxy_ProcessRequestCompleted(object sender, ProcessRequestCompletedEventArgs e)
{
// Create Silverlight DataSet from xml string
DataSet dataSet = new DataSet();
dataSet.FromXml(e.Result);
// Process server response here
}
但是SL的DataGrid不接受DATASET或者DataTable类型的数据源的,他接受的是实休集合。所以我们就需要扩展SL的DataGrid,使其变为像ASP.NET中的DataView那样,有DataSource属性,有DataBind方法。这个有源代码可以下载,你可以根据自己的需要再来扩展。下载地址为:
http://slbindabledatagrid.codeplex.com/,其实这个方法的核心还是要把数据集转化为List类型的集合,然后这个结果再赋值给DataGrid.
所以我要实现的方法,就是先用加拿大的同行的方法把XML格式的字符串解析成他的DataSet,然后再把这个DataSet的值变成扩展DataGrid的类型的DataSet。下面请看详细代码
public void GetAllFOrigialMetaData(string strDataBase, string strDataTable,string strDataTableKeys,string strDataTableOrder,int pageindex)
{
DataSet dataSet = new DataSet();
//客户端类
MetadataServiceClient msc = new MetadataServiceClient();
msc.Endpoint.Address = Utility.ProcessServiceAddress(msc.Endpoint.Address);
msc.GetAllOriginalMetaDataCompleted += new EventHandler<GetAllOriginalMetaDataCompletedEventArgs>(
(o, e) =>
{
DataSet dataSets = new DataSet();
//使用接口把服务端传的数据解析为DataSet数据集
dataSets.FromXml(e.Result);
ds = dataSets;
_pageCount = e.pageCount;
if (OnCompleted != null)
{
OnCompleted();
}
OnCompleted = null;
}
);
msc.GetAllOriginalMetaDataAsync(strDataBase, strDataTable, dataSet.ToXml(true), pageindex, strDataTableKeys, strDataTableOrder);
}
/// <summary>
/// 初始化数据
/// </summary>
/// <param name="pageindex">当前页码,为-1时,表示第一次加载数据</param>
private void InitData(int pageindex)
{
Windows ws = new Windows(this.FindName("waiting") as Waiting);
Windows.ShowWaitingOfChild();
//把参数转化为数组
string[] arr = Convert.ToString(Tag).Split('|');
FOrigialMetaData fomd = new FOrigialMetaData();
fomd.OnCompleted += new FOrigialMetaData.OnComplete(
() =>
{
string strcaption = string.Empty;
//初始化扩展的DataTable
Data.DataTable dt = new Data.DataTable("MyDataTable");
fomd.OnCompletedCols += new FOrigialMetaData.OnComplete(
() =>
{
//列数据集合
_list = fomd.List;
string str="varchar";
//根据原始数据的列初始化变量dt的列
foreach (Silverlight.DataColumn dc in fomd.FOrigialMetaDataSet.Tables[0].Columns)
{
List<tbDataColumns> tdc=null;
//如果列集合存在
if (_list.Count > 0)
{
tdc = _list.Where(t => t.cnvcColumnEName == dc.ColumnName).ToList<tbDataColumns>();
//查找的列不存在,则继续
if (tdc.Count==0)
continue;
//赋值中文列名及类型
strcaption = tdc.ElementAt(0).cnvcColumnName;
str = tdc.ElementAt(0).cnvcDataType;
}
else //不存在列集合
{
strcaption = dc.ColumnName;
}
Data.DataColumn dcchild = new HIEG2.Portal.Data.DataColumn(dc.ColumnName, strcaption, true, true, true, true, str);
dt.Columns.Add(dcchild);
}
//复制每一行的数据
foreach (Silverlight.DataRow dr in fomd.FOrigialMetaDataSet.Tables[0].Rows)
{
Data.DataRow drchild = new HIEG2.Portal.Data.DataRow();
foreach (Data.DataColumn dc in dt.Columns)
{
//当数据类型为DateTime时,需要格式化为yyyy-MM-dd HH:mm:ss
if (dc.strDataType.ToLower() == "datetime")
{
if (Convert.ToString(dr[dc.ColumnName]) != "")
drchild[dc.ColumnName] = Convert.ToDateTime(dr[dc.ColumnName]).ToString("yyyy-MM-dd HH:mm:ss");
else
drchild[dc.ColumnName] = "";
continue;
}
drchild[dc.ColumnName] = dr[dc.ColumnName];
}
dt.Rows.Add(drchild);
}
Data.DataSet ds = new Data.DataSet("MyDataSet");
ds.Tables.Add(dt);
//把数据源赋值给表格集合
radGridView.DataSource = ds;
radGridView.DataMember = "MyDataTable";
//绑定数据
radGridView.DataBind();
//第一次加载时,需要设置分页
if (pageindex == -1)
{
radDataPager.BindSource(fomd.PageCount, PageSize);
radDataPager.PageIndexChanged -= new EventHandler<EventArgs>(dpEmployee_PageIndexChanged);
radDataPager.PageIndexChanged += new EventHandler<EventArgs>(dpEmployee_PageIndexChanged);
}
Windows.HideWaitingOfChild();
}
);
fomd.GetAllOrigialMetaDataCols(arr[0]);
}
);
//取得当前页的原始数据
fomd.GetAllFOrigialMetaData(arr[1], arr[2],arr[3],arr[4],pageindex);
}
void dpEmployee_PageIndexChanged(object sender, EventArgs e)
{
InitData(radDataPager.PageIndex+1);
}
}
通过上面的方法,其实上可以实现在不确定数据库和数据表的情况下,SL端从WCF取得数据集。不过还有一个地方需要扩展,那就是DataPager,具体的实现,请看:
http://www.cnblogs.com/xiaozhuang/archive/2009/08/17/1548129.html,其实我的代码中也已经有实现,下面是主要的代码,用于扩展
DataPager
public static class DataPageExtension
{
public static void BindSource(this DataPager dataPager, int totalCount, int pageSize)
{
List<int> list = new List<int>(totalCount);
for (int i = 0; i < totalCount; i++) list.Add(i);
PagedCollectionView pcv = new PagedCollectionView(list);
pcv.PageSize = pageSize;
dataPager.Source = pcv;
}
}
好了,今天就写到这了,有什么问题,大家可以一块交流,如果写的有不足之处,请指出。