LINQ to ADO.NET
它包含 LINQ to DataSet和LINQ to SQL兩個相關技術。
LINQ to DataSet:提供對DataSet、DataTable進行查詢
LINQ to SQL:通過LINQ對象模型直接與數據庫進行數據交互
- LINQ to DataSet概述
DataSet是ADO.NET進行無連接模式數據庫訪問的關鍵.
1.緩存不同數據源中的數據
2.表示層,與界面控件集成進行數據綁定
3.中間層,提供保留數據關係形狀的緩存幷包括快速查詢和層次結構導航服務
DataSet,允許應用程序把數據子集從一個或多個數據源導入應用程序空間。從面應用程序可以在內存中操作這些數據,同時保留它的關係形狀。
痛點:DataSet查詢功能存在限制,對於複雜的情況,必須編寫自定義的查詢,這會導致應用程序性能以及可維護性都變得低下。
ADO.NET負責把數據讀入到DataSet中,LINQ to DataSet查詢在DataSet和DataTable對象中緩存的數據。
只有在DataSet有填充值後,才能使用LINQ to DataSet查詢DataSet對象。LINQ to DataSet通過使用DataRowExtensions和DataTableExtensions類中的擴展方法可以更快更容易查詢DataSet中的數據。
DataTableExtensions類提供的擴展方法把DataTable轉化成LINQ可以查詢的IEnumerable類型。
DataTableExtensions成員:
AsDataView:創建並返回支持LINQ的DataView對象
AsEnumerable:返回IEnumerable對象,這個對象可以用在LINQ表達式或方法查詢中
CopyToDataTable:在給定輸入IEnumerable對象的情況下,返回包含DataRow對象副本的DataTable
DataRowExtensions類提供的擴展方法對DataRow字段數據的類化讀取和設置。
DataRowExtensions成員:
Field:提供對DataRow中的每個列值的強類型訪問
SetField:爲DataRow中的指定列設設置一個新值
- LINQ to SQL概述
LINQ to SQL是ADO.NET系列技術的一部分,它基於由ADO.NET提供程序模型提供的服務。
可以把LINQ to SQL代碼與現有的ADO.NET應用程序混合使用。
LINQ to SQL架構在ADO.NET之上,通過ADO.NET進行數據庫操作,同時使用LINQ to SQL提供程序實現對象模型和關係數據庫模型之間的轉換。
對象關係模型是LINQ to SQL的核心部分,它提供數據庫模型和對象模型之間的映射關係。
LINQ to SQL查詢時所用的語法與在LINQ中使用的語法一樣,不同的是,查詢中引用的對象被映射到數據庫中的元素。在應用程序執行期間,通過LINQ to SQL來請求LINQ查詢執行。LINQ to SQL支持程序隨後把LINQ查詢轉換成等效的SQL命令,並委託ADO.NET提供程序執行數據庫操作。
ADO.NET提供程序把查詢結果作爲DataReader返回,然後LINQ to SQL提供程序把ADO.NET結果轉換成用戶對象的IQueryable集合,並對IQueryable集合進行查詢,返回查詢結果。
IQueryable接口是LINQ to SQL中使用到的主要接口,它從IEnumerable繼承而來,表示一個可以查詢的數據集,可以通過LINQ對其進行查詢。
LINQ to DataSet複雜數據查詢
LINQ to DataSet的使用通常包含如下步驟:
1.獲取DataSet/DataTable數據源:可以通過ADO.NET技術從數據庫獲取,可以通過XML技術從XML文件獲取,也可以從其它形式的數據源獲取,甚至是可以在內存中直接創建並填充DataSet/DataTable對象。
2.把DataTable轉換成IEnumerable類型:在LINQ to DataSet中,通過DataTableExtensions擴展和AsEnumerable()方法從DataTable獲取一個等價的IEnumerable對象
3.使用LINQ語法來編寫查詢
4.使用查詢結果
注意:DataSet本身是DataTable的集合,它可以包含一個或多個DataTable及它們之間的關係。LINQ to DataSet實際是對DataTable進行數據查詢,並非對DataSet進行查詢。
- 單表數據查詢
使用DataTable類中AsEnumerable()方法,把DataTablel轉成一個類型爲IEnumerable的可枚舉數據集合
public static EnumerableRowCollection AsEnumerable(DataTable source);
從上面這個步驟中,從DataTable中獲取的元素類型爲DataRow。要進一步訪問數據表的記錄的具體字段數據,需要使用DataRow的一個擴展方泛型方法,Field()方法,來獲取DataRow的某個字段的數據
Field()最常用的重載版本:
public static T Field(DataRow row,DataColumn column);
public static T Field(DataRow row,int columnIndex);
public static T Field(DataRow row,string columnName);
column:表示數據列要返回數據的字段
columnIndex:表示從0開始的索引列索引。
columnName:表示要返回數據的字段的名稱。
使用Visual Studio新建C#控制檯應用程序
添加一個方法用來創建DataSet
//創建一個包含數據的DataSet
static DataSet BuildOneDTDataSet()
{
string[] nameSet = { "王霞", "張三", "李四", "李花", "王五", "陸六", "夏七", "吳八" };
string[] sexSet = { "女", "男", "男", "女", "男", "男", "男", "男" };
int[] ageSet = { 18, 20, 21, 22, 19, 20, 25, 24 };
//創建一個DataSet對象 PeopleDS
DataSet ds = new DataSet("PeopleDS");
//創建一個DataTable對象 PeopleDT
DataTable dt = new DataTable("PeopleDT");
//把數據表dt添加到數據集ds當中
ds.Tables.Add(dt);
//設置數據表dt中各字段的信息
dt.Columns.AddRange(new DataColumn[]
{
new DataColumn("Name",Type.GetType("System.String")),
new DataColumn("Sex",Type.GetType("System.String")),
new DataColumn("Age",Type.GetType("System.Int32"))
});
//對於上面已建立的結果,添加相關的數據表信息
for (int i = 0; i < nameSet.Length; i++)
{
//新增行數據
DataRow row = dt.NewRow();
row["Name"] = nameSet[i];
row["Sex"] = sexSet[i];
row["Age"] = ageSet[i];
//數據表中添加數據
dt.Rows.Add(row);
}
return ds;
}
添加一個查詢方法:
static void UseSelect()
{
//初始化一個數據集ds
DataSet ds = BuildOneDTDataSet();
//從ds數據集中獲取指定的數據表
DataTable dt = ds.Tables["PeopleDT"];
//查詢出所有的員
var query =
from p in dt.AsEnumerable()
select p;
foreach (var item in query)
{
Console.WriteLine("Name:{0}\tSex:{1}\tAge:{2}",
item.Field<string>("Name"),
item.Field<string>("Sex"),
item.Field<int>("Age"));
}
//查詢某個單獨元素
string str = ""; //等查詢的是哪列,由用戶指定
Console.Write("請輸入要查詢的數據:");
while (true)
{
string tmp = Console.ReadLine();
if (tmp.Equals("Name") || tmp.Equals("Sex") || tmp.Equals("Age"))
{
str = tmp;
break;
}
else
{
Console.Write("Error!請重新輸入需要查詢的列:");
continue;
}
}
var query1 =
from p1 in dt.AsEnumerable()
select p1.Field<string>(str);
Console.WriteLine("根據 " + str + " 查詢結果爲:");
foreach (var item in query1)
{
Console.Write("{0} ",item);
}
Console.WriteLine();
}
在工程中的Main方法中添加如下代碼進行測試:
UseSelect();
Console.ReadKey();
編譯運行後結果如下:
除了使用select語句外,還要以對DataTable記錄進行where過濾,對查詢結果進行排序
在工程中添加如下方法:
static void SelectAll()
{
DataSet ds = BuildOneDTDataSet();
DataTable dt = ds.Tables["PeopleDT"];
Stopwatch swatch = new Stopwatch();
swatch.Start();
var query =
from p in dt.AsEnumerable()
select p;
Console.WriteLine("查詢所有數據:");
foreach (var p in query)
{
Console.WriteLine("Name:{0}\tSex:{1}\tAge:{2}",
p.Field<string>("Name"),
p.Field<string>("Sex"),
p.Field<int>("Age"));
}
swatch.Stop();
Console.WriteLine("執行耗時:{0}毫秒", swatch.ElapsedMilliseconds.ToString());
}
static void UseOrderbByWhere()
{
//獲取數據集ds
DataSet ds = BuildOneDTDataSet();
//獲取數據集ds中的表格PeopleDT的數據表 dt
DataTable dt = ds.Tables["PeopleDT"];
Stopwatch swatch = new Stopwatch();
swatch.Start();
//查詢數據表中年齡大於20歲的,並且按年齡進行從低到高排序
var query =
from p in dt.AsEnumerable()
orderby p.Field<int>("Age")
where p.Field<int>("Age") > 20
select p;
Console.WriteLine("查詢年齡大於20,並按年齡進行從低到高排序:");
foreach (var p in query)
{
Console.WriteLine("Name:{0}\tSex:{1}\tAge:{2}",
p.Field<string>("Name"),
p.Field<string>("Sex"),
p.Field<int>("Age"));
}
swatch.Stop();
Console.WriteLine("執行耗時:{0}毫秒", swatch.ElapsedMilliseconds.ToString());
}
在Main方法中使用如下代碼進行測試:
//查詢所有數據
SelectAll();
//按條件查詢並做排序
UseOrderbByWhere();
Console.ReadKey();
編譯運行的結果如下:
使用LINQ to DataSet查詢DataTable的數據可以分爲兩個部分:
1.把DataTable轉換成爲IEnumerable數據集合,這裏使用AsEnumerable()方法
2.對IEnumerable進行操作
- 多表數據查詢
一個數據訂DataSet中一般來說會包含多個數據表DataTable,而且數據表之間有一定的關聯關係,從而表示一個關係型數據庫。
通過LINQ to DataSet可以查詢多個數據表中的數據,這個時候需要使用多個from子句進行復合查詢,同時使用where子句來進行多表之間的關係判斷。
使用Visual Studio新增C#控制檯應用程序
創建DataSet方法如下:
static DataSet BuildDataSet()
{
DataSet ds = new DataSet("StudyInfo");
//創建StudyInfo的數據表 Student
DataTable StudentDt = new DataTable("Student");
ds.Tables.Add(StudentDt);
//學生信息數據列
StudentDt.Columns.AddRange(new DataColumn[] {
new DataColumn("StudentID",Type.GetType("System.String")),
new DataColumn("StudentName",Type.GetType("System.String")),
new DataColumn("StudentSex",Type.GetType("System.String")),
new DataColumn("StudentAge",Type.GetType("System.Int32"))
});
//把學生信息添加到數據表
StudentDt.Rows.Add("00001", "張無忌", "男", 20);
StudentDt.Rows.Add("00002", "喬峯", "男", 19);
StudentDt.Rows.Add("00003", "楊霞", "女", 21);
StudentDt.Rows.Add("00004", "趙敏", "女", 22);
StudentDt.Rows.Add("00005", "郭靖", "男", 18);
//創建StudyInfo的數據表 Score
DataTable ScoreDt = new DataTable("Score");
ds.Tables.Add(ScoreDt);
//成績信息數據列
ScoreDt.Columns.AddRange(new DataColumn[] {
new DataColumn("ScoreID",Type.GetType("System.String")),
new DataColumn("Math",Type.GetType("System.Double")),
new DataColumn("Chinese",Type.GetType("System.Double")),
new DataColumn("English",Type.GetType("System.Double"))
});
//把成績信息添加到數據表
ScoreDt.Rows.Add("001",80.5,75.5,78.0);
ScoreDt.Rows.Add("002", 88.5, 80.5, 60.0);
ScoreDt.Rows.Add("003", 75.0, 90.5, 80.0);
ScoreDt.Rows.Add("004", 59.5, 80.5, 75.0);
//創建StudyInfo的數據表 StudentScore
DataTable StuScoreDt = new DataTable("StudentScore");
ds.Tables.Add(StuScoreDt);
//學生成績信息數據列
StuScoreDt.Columns.AddRange(new DataColumn[] {
new DataColumn("StudentScoreID",Type.GetType("System.String")),
new DataColumn("StudentID",Type.GetType("System.String")),
new DataColumn("ScoreID",Type.GetType("System.String"))
});
//把學生成績信息數據表中添加數據
StuScoreDt.Rows.Add("1", "00001", "003");
StuScoreDt.Rows.Add("2", "00002", "001");
StuScoreDt.Rows.Add("3", "00003", "002");
StuScoreDt.Rows.Add("4", "00004", "001");
StuScoreDt.Rows.Add("5", "00005", "004");
return ds;
}
添加一個數據查詢方法
static void QueryStuScoresByID(string StudentID)
{
//獲取數據集
DataSet ds = BuildDataSet();
//獲取數據表
DataTable dtStu = ds.Tables["Student"];
DataTable dtScore = ds.Tables["Score"];
DataTable dtStuScore = ds.Tables["StudentScore"];
var query1 =
from stu in dtStu.AsEnumerable()
from score in dtScore.AsEnumerable()
from stuscore in dtStuScore.AsEnumerable()
where stu.Field<string>("StudentID").Equals(stuscore.Field<string>("StudentID"))
where score.Field<string>("ScoreID").Equals(stuscore.Field<string>("ScoreID"))
where stu.Field<string>("StudentID").Equals(StudentID)
//使用匿名類
select new
{
Name = stu.Field<string>("StudentName"),
Math_score = score.Field<double>("Math"),
Chinese_score = score.Field<double>("chinese"),
English_score = score.Field<double>("English")
};
Console.WriteLine("查詢ID爲:{0}的學生成績如下:", StudentID);
foreach (var item in query1)
{
Console.WriteLine("姓名:{0},數學:{1},語文:{2},英語:{3}",
item.Name,item.Math_score,item.Chinese_score,item.English_score);
}
}
在Main方法中添加如下代碼進行測試:
//創建DataSet
BuildDataSet();
//計算查詢效率
Stopwatch sw = new Stopwatch();
sw.Start();
//執行查詢
QueryStuScoresByID("00001");
sw.Stop();
Console.WriteLine("執行耗時:{0}毫秒",sw.ElapsedMilliseconds.ToString());
Console.ReadKey();
編譯運行結果如下:
- 使用查詢創建查詢數據表
LINQ to DataSet通過DataTableExtensions類提供的擴展方法CopyToDataTable(),會把數據表中獲取到的查詢結果直接複製到一個新的數據表(DataTable)中,從而可以把查詢結果綁定到界面控件DataGridView等。
CopyToDataTable()包含3個重載版本:
1.public static DataTable CopyToDataTable(IEnumerable source) where T : DataRow
2.public static void CopyToDataTable(IEnumerable source,DataTable table,LoadOption options) where T : DataRow
3.public static void CopyToDataTable(IEnumerable source,DataTable table,LoadOption options,FillErrorEventHandler errorHandler) where T : DataRow
table:目標數據表對象,用來保存數據
options:指定DataTable的加載屬性
errorHandler:一個函數委託,可以指定自定義的異常處理操作
CopToDataTable()執行過程
1.複製源表中DataTable(實現IQueryable接口的DataTable對象)。IEnumerable源通常來源於LINQ to DataSet表達式或方法查詢
2.目標DataTable的架構從源表中第一個DataRow對象的列生成,克隆表的名稱是源表名稱加上單詞query
3.對於源表中的每一行,把行內容複製到新DataRow對象中,然後把這個對象插入到目標DataTable中
4.複製完源表中所有DataRow對象後,返回複製的DataTable。如果源序列不包含任何DataRow對象,則返回一個空的DataTable
在程序中添加如下方進行數據拷貝:
static void UseCopyToDt()
{
//獲取數據集
DataSet ds = BuildDataSet();
//獲取數據表
DataTable dtStu = ds.Tables["Student"];
DataTable dtScore = ds.Tables["Score"];
DataTable dtStuScore = ds.Tables["StudentScore"];
var query1 =
from stu in dtStu.AsEnumerable()
from score in dtScore.AsEnumerable()
from stuscore in dtStuScore.AsEnumerable()
where stu.Field<string>("StudentID").Equals(stuscore.Field<string>("StudentID"))
where score.Field<string>("ScoreID").Equals(stuscore.Field<string>("ScoreID"))
where (int)stu["StudentAge"] > 20
select stu;
DataTable newDt = query1.CopyToDataTable();
Console.WriteLine("學生列表:");
foreach(var item in newDt.AsEnumerable())
{
Console.WriteLine("ID:{0},姓名:{1},性別:{2},年齡:{3}",
item["StudentID"],item["StudentName"],item["StudentSex"],item["StudentAge"]);
}
}
在Main方法中添加如下測試代碼:
Console.WriteLine();
sw.Start();
//執行拷貝
UseCopyToDt();
sw.Stop();
Console.WriteLine("拷貝執行耗時:{0}毫秒", sw.ElapsedMilliseconds.ToString());
編譯運行結果如下:
在實際應用中,使用CopyToDataTable()創建副本通常用於界面綁定
- 修改表中字段數據
在LINQ to DataSet中使用Field()方法來獲取數據表中指定字段數據,也可以使用SetField()來對字段數據進行修改
DataRowExtensions.SetField()方法的3個重載版本
1.public static void SetField(DataRow row,DataColumn column,T value);
2.public static void SetField(DataRow row,int columnIndex,T value);
3.public static void SetField(DataRow row,string columnName,T value);
column:表示要設置數據的列對象(DataColumn類型)
columnIndex:從0開始的要設置數據的列索引
columnName:要設置數據的列名稱
通常來說一個數據表的列名是固定的,所以建議使用列名指定數據的列
在程序中添加一個修改指定學生年齡的方法:
//修改學生的年齡
static void AddStudentAge(string studentID, int age)
{
//獲取數據集
DataSet ds = BuildDataSet();
//獲取得學生數據表
DataTable dtStu = ds.Tables["Student"];
var query2 =
from stu in dtStu.AsEnumerable()
where stu.Field<string>("StudentID").Equals(studentID)
select stu;
foreach (var item in query2)
{
Console.Write("學號爲:{0}的學生,{1},原始年齡爲:{2}\n",
item["StudentID"],item["StudentName"],item["StudentAge"]);
item.SetField<int>("StudentAge", age);
Console.Write("學號爲:{0}的學生,{1},修改後年齡爲:{2}\n",
item["StudentID"], item["StudentName"], item["StudentAge"]);
}
}
在Main方法中添加如下代碼進行測試:
Console.WriteLine();
sw.Start();
//執行拷貝
AddStudentAge("00001", 30);
sw.Stop();
Console.WriteLine("修改記錄執行耗時:{0}毫秒", sw.ElapsedMilliseconds.ToString());
編譯運行結果如下:
注意:SetField()方法是直接修改數據表中的記錄。如果要保持原數據表不變,那麼在使用SetField()之前,應該先備份源數據表(使用CopyToDataTable()方法進行備份)。
- 數據視圖DataView
數據視圖DataView,是常用的只讀形式數據保存形式,可以把它綁定到UI界面,以用戶提供形象動態的數據查閱功能。
DataView本身可以從DataTable或DataSet獲取,也可以通過LINQ查詢來獲取DataView。
在LINQ to DataSet中,可以使用DataTableExtensions.AsDataView()擴展方法從DataTable或LINQ查詢中創建一個與數據源對應的DataView對象。
AsDataView()方法包含兩個重載版本:
1.public static DataView AsDataView(DataTable table);
2.public static DataView AsDataView(EnumerableRowCollection source) where T:DataRow
在程序中添加一個方法,使用LINQ to DataSet查詢後轉爲DataView
//創建DataView
static void CreateDataView()
{
//獲取數據集ds
DataSet ds = BuildDataSet();
//獲取數據表dt
DataTable dt = ds.Tables["Student"];
//使用DataTable的AsDataView方法創建DataView
DataView dv = dt.AsDataView();
//使用LINQ查詢來創建DataView
EnumerableRowCollection<DataRow> query1 =
from stu in dt.AsEnumerable()
select stu;
//根據LINQ查詢獲取到的結果來創建DataView
DataView dv_stu = query1.AsDataView();
Console.WriteLine("DataView數據輸出:");
Console.WriteLine("所有學生:");
Console.WriteLine("ID\t姓名\t性別\t年齡");
foreach (DataRowView drv in dv)
{
for (int i = 0; i < drv.Row.ItemArray.Length; i++)
{
Console.Write(drv[i]+"\t");
}
Console.WriteLine();
}
Console.WriteLine();
EnumerableRowCollection<DataRow> query2 =
from stu in dt.AsEnumerable()
where stu.Field<String>("StudentName").StartsWith("楊")
select stu;
DataView dv_filter = query2.AsDataView();
Console.WriteLine("姓楊的學生:");
Console.WriteLine("ID\t姓名\t性別\t年齡");
foreach (DataRowView drv in dv_filter)
{
for (int i = 0; i < drv.Row.ItemArray.Length; i++)
{
Console.Write(drv[i]+"\t");
}
Console.WriteLine();
}
Console.WriteLine();
EnumerableRowCollection<DataRow> query3 =
from stu in dt.AsEnumerable()
orderby stu.Field<int>("StudentAge")
select stu;
DataView dv_sort = query3.AsDataView();
Console.WriteLine("按學生的年齡進行從小到大的排序:");
Console.WriteLine("ID\t姓名\t性別\t年齡");
foreach (DataRowView drv in dv_sort)
{
for (int i = 0; i < drv.Row.ItemArray.Length; i++)
{
Console.Write(drv[i] + "\t");
}
Console.WriteLine();
}
}
添加另一個方法直接使用DataTable轉爲DataView,並且直接做相關查詢、排序的過濾
static void UseDataView()
{
//獲取數據集ds
DataSet ds = BuildDataSet();
//獲取數據表dt
DataTable dt = ds.Tables["Student"];
//根據Datatable的AsDataView方法獲取到DataView
DataView dv = dt.AsDataView();
//設置DataView的過濾信息使用RowFilter
dv.RowFilter = "StudentAge > 20";
Console.WriteLine("年齡大於20歲的學生信息:");
Console.WriteLine("ID\t姓名\t性別\t年齡");
foreach (DataRowView drv in dv)
{
for (int i = 0; i < drv.Row.ItemArray.Length; i++)
{
Console.Write(drv[i] + "\t");
}
Console.WriteLine();
}
Console.WriteLine();
//清除查詢條件
dv.RowFilter = string.Empty;//這裏也可以寫爲dv.RowFilter = null;
dv.RowFilter = "StudentName like '楊%'";
Console.WriteLine("姓楊的學生信息:");
Console.WriteLine("ID\t姓名\t性別\t年齡");
foreach (DataRowView drv in dv)
{
for (int i = 0; i < drv.Row.ItemArray.Length; i++)
{
Console.Write(drv[i] + "\t");
}
Console.WriteLine();
}
Console.WriteLine();
//清除查詢條件
dv.RowFilter = string.Empty;//這裏也可以寫爲dv.RowFilter = null;
dv.Sort = "StudentAge asc,StudentName desc";
Console.WriteLine("按學生年齡升序,姓名降序排列:");
Console.WriteLine("ID\t姓名\t性別\t年齡");
foreach (DataRowView drv in dv)
{
for (int i = 0; i < drv.Row.ItemArray.Length; i++)
{
Console.Write(drv[i] + "\t");
}
Console.WriteLine();
}
Console.WriteLine();
//清除查詢條件
dv.RowFilter = string.Empty;//這裏也可以寫爲dv.RowFilter = null;
}
在Main方法中添加如下代碼進行測試:
Console.WriteLine();
sw.Start();
//使用DataViews查詢數據
CreateDataView();
sw.Stop();
Console.WriteLine("查詢記錄執行耗時:{0}毫秒", sw.ElapsedMilliseconds.ToString());
Console.WriteLine();
sw.Start();
//使用DataViews查詢數據
UseDataView();
sw.Stop();
Console.WriteLine("查詢記錄執行耗時:{0}毫秒", sw.ElapsedMilliseconds.ToString());
編譯運行的結果如下:
注意:直接使用DataView可以進行查詢過濾和排序,但只是它的排序和過濾功能有限,無法實現自定義的過濾函數和排序方法,在進行復雜的過濾或排序操作時,可以先使用LINQ查詢來創建一個DataView,作爲一個最終或者是一個臨時的視圖,然後再在這個視圖上進行簡單的查詢過濾或排序。