这篇来谈论一下Linq第三个方面的应用:Linq to Object,只要是继承了IEnumerable或IQueryable接口的Object都能使用Linq特性进行操作。在操作过程当中可能很多人都觉得不好调试不能实时地观察结果数据集,想把IQuery的Linq查询语句转换成数据表DataTable,要怎么实现转换呢?来看一下。
先来说一场景解释一下为什么需要用Linq来解决一些问题,能解决一些什么样的问题,相对于SQL,DataTable等一些传统操作方式有哪些优势?
场景:目前主要数据源有多个,而且数据库的类型可能不一样(SQL Server、Oracle等),也可能分布在不同的服务器上,数据量比较大,要操作的表基本都是千万级的数据量,而且不同数据库里面的表需要作Join查询才能得到满足条件的数据。
传统操作方式:
A). 如果同是SQL Server数据库,那么两台数据库之间可以建立Linked Server连接,这样分布在两台或多台不同服务器上的数据库就能满足联合操作的条件。但因为是Linked Server连接,需要网络传输,性能上比同非分布式的操作要差很多。
B). 如果是SQL Server数据库和Oracle数据库,那么两台数据库之间也可以建立Linked Server连接,这样分布在两台或多台不同服务器上的数据库就能满足联合操作的条件。但如果要在SQL Server上访问Oracle数据源该怎么操作? 有一种方式是:OpenQuery(sql),这个性能当然也不会好。
上面两种场景虽然都有实现方式,但主要问题是性能,如果是web系统,需要实时反映查询结果该怎么办?很简单,web页面呈现会有时间限制,如果1~2分钟的时间去加载数据,那么后果是什么?页面直接timeout了,就算Ajax,用户能耐心地等待两分钟吗?所以可以排除用这种传统方式的可能性。
替代操作方式 -- 商业智能(BI):
如果要用BI的话,可以很轻松地解决所有这些问题,但这需要在数据库仓库层面作相应设计,运用定时运行的Job可以定期把不同数据源的数据汇集到查询数据库,然后直接限制用户可访问的数据源,比如限定访问特定时期内的数据,或者提供可预定的历史报表功能。
如果只是系统里面一个小的需求,那么需要重新架构一个复杂的数据仓库吗?答案是:成本过高。
替代的操作方式 -- 操作DataTable 或 Linq操作:
先将所需的数据分别从不同的分布式的数据库里面尽量查询出来,尽量的含义是,利用已经知道的查询条件尽量返回一个最小的结果集,同样还是为了解决性能问题。然后放在DataTable里面,剩下的操作是处理DataTable的问题了。
处理DataTable有两种方式:DataTable数据行循环, Linq。
' DataTable数据行循环
For Each row As DataRow In table.Rows
' 业务逻辑 row("column name")
Next
' --------------------------------
' Linq操作, 以前项目里面的一个例子。
'Load all records from Assets Change tracking table
getAssetChangeTrackingData(searchResultsTable, assetChangeTrackingSearchResultsTable, assetchangeTrackingListForSP, _
assetchangeTrackingListForSP, assetChangeTrackingModsFileOrigin, assetchangetrackingModsEDSAssetID, _
String.Empty, String.Empty, String.Empty, String.Empty, String.Empty)
'Load 2D History Barcode data and gmassets data
getTwoDHistoryJoiningData(datamartTable, TwoDhistTable, assetchangeTrackingListForSP, SerialNumberList, _
BarcodeList, AssetIDList, RSNList, BidSegmentList)
'Get finial Binding data table
'--------------------------------------------------------------------------------------------
Dim searchResult = searchResultsTable.AsEnumerable()
Dim changingTracking = assetChangeTrackingSearchResultsTable.AsEnumerable()
'Load records from Assets Changing table
Dim finalResult = From search In searchResult _
Join changing In changingTracking _
On search.Field(Of String)("Asset ID") Equals changing.Field(Of String)("Asset ID") _
And search.Field(Of String)("File Origin") Equals changing.Field(Of String)("File Origin") _
Select New With _
{ _
.SerialNumber = search.Field(Of String)("Serial Number") _
, .AssetAction = search.Field(Of String)("Asset Action Taken") _
}
dtFirst = CustomLINQtoDataSetMethods.CopyToDataTable(finalResult)
Dim finalQuery = dtFirst.AsEnumerable()
'Load records that are newly added from Assets Changing table
Dim searchNoneIn = From search In searchResult _
Where Not (From changing In finalQuery _
Where Not changing.IsNull("SerialNumber") _
And changing.Field(Of String)("AssetAction").ToUpper = "M" _
Select changing.Field(Of String)("SerialNumber")).Contains(search.Field(Of String)("Serial Number") _
) _
And search.Field(Of String)("Asset Action Taken").ToUpper = "A" _
Select New With _
{ _
.SerialNumber = search.Field(Of String)("Serial Number") _
, .BarCode = search.Field(Of