语言集成查询LINQ

来源:互联网 发布:linux 驱动精灵 编辑:程序博客网 时间:2024/05/15 05:26

目录

一、LINQ概述... 2

1LINQ简介... 2

2、体系结构... 2

3、基础回顾(匿名类型、扩展方法、拉姆达表达式)... 2

4LINQ查询幕后的实现... 2

1)、查询语法(LINQ查询)... 2

(2)、方法语法(Lambda表达式)----与(1)等价... 3

3)、方法语法(委托类型/匿名方法)----与(1)等价... 3

4)、方法语法(委托类型/具体方法)----与(1)等价... 4

5LINQ延迟执行... 4

1)、LINQ延迟执行... 4

2)、改进LINQ延迟执行... 4

二、LINQ to SQL. 5

1LINQ to SQL 简介... 5

2、实体类映射... 5

3DataContext数据上下文对象... 5

4、定义基本关系... 6

1)、一对多关系... 6

2)、多对一关系... 6

5、对象关系设计器... 6

1)、查询代码片段... 7

2)、新增代码片段... 7

3)、更新代码片段... 8

4)、删除代码片段... 8

5)、LINQ to SQL示例... 9

三、LINQ to XML. 9

1.NET Framework3.5中新的XML对象... 9

2LINQ查询XML代码片段... 10

四、LINQ to DataSet 10

1、查询一个表代码片段... 10

2、使用聚合函数代码片段... 10

3、查询两个表代码片段... 11

4、查询类型化DataSet 11

5、检测空字段代码片段... 12

6、将结果保存到DataTable代码片段... 12

五、其他... 12

1LINQ to SQL示例... 12

2、杂项... 15

3、过滤泛型List示例... 15

 

正文

一、LINQ概述

1LINQ简介

LINQ的全称:Language Integrated Query。语言集成查询。

不采用特定于关系数据库或者XML的专有方案,而采用通用方案来解决各种信息资源的访问与整合问题。

(处理数据库,就必须理解SQL;处理XML,就必须理解XpathXqueryXSLT这样的技术;处理DataSet,则需要了解ADO.NET中可以使用的各种类和特性。而LINQ提供了统一的数据视图,从而不需要考虑数据的形式和结构。)

LINQ中,查询成为编程语言的一部分。这使得查询表达式可以得到编译时的语法检查,智能感知(InteliSense)的好处。

2、体系结构

 

如图所示:

最底层包含应用程序可以操作的各种数据源。包括:对象、关系数据库、XML

倒数第二层是LINQ支持的数据源:LINQ to ObjectsLINQ to DataSetLINQ to SQLLINQ to EntitiesLINQ to XML

LINQ支持的数据源也称为LINQ提供程序;这些LINQ提供程序将以VBC#表达的查询转换为该数据源的本地语言。为了通过LINQ访问所有这些数据源,开发人员可以使用C#VB并编写LINQ查询。

3、基础回顾(匿名类型、扩展方法、拉姆达表达式)

匿名类型 :在不需要正式定义类的情况下定义数据类型。实际上,匿名类型是MS专门为LINQ设计的新功能。更多内容参见:匿名类型的相关文档。

扩展方法:扩展已有类的功能,而不需要创建该类的子类。扩展方法的出现,很重要的一个原因也是LINQ的出现。更多内容参见:扩展方法的相关文档。

拉姆达表达式:实现类似于匿名方法同样的效果。匿名方法,将代码直接与委托实例相关联。更多内容参见:匿名方法和Lambda表达式的相关文档。

4LINQ查询幕后的实现

1)、查询语法(LINQ查询)

string[] names ={

"Burke","Connor","Frank","Everett","Albert","George","Harris","David" };

IEnumerable<string> query =

    from s in names

    where s.Length == 5

    orderby s

    select s.ToUpper();

(2)、方法语法(Lambda表达式)----与(1)等价

IEnumerable<string> query = names

    .Where(s => s.Length == 5)

    .OrderBy(s => s)

    .Select(s => s.ToUpper());

 

s => s.Length == 5 | s => s | s => s.ToUpper() 是三个Lambda表达式。

Where()OrderBy()Select()是静态类Enumerable的静态方法,是泛型接口IEnumerable<T>的扩展方法。

调用静态类的扩展方法

扩展方法允许这样调用

返回值类型

Enumerable.Where

(names, s => s.Length == 5);

names.Where

(s => s.Length == 5)

IEnumerable<T>

Enumerable.OrderBy

(names, s => s);

names.OrderBy

(s => s)

IOrderedEnumerable<T>

(实现了IEnumerable<T>接口)

Enumerable.Select

(names, s => s.ToUpper());

names.Select

(s => s.ToUpper())

IEnumerable<T>

Where()方法需要的参数类型为: Func<TSource, bool>(委托类型)

        TSource:集合中元素的类型。bool:返回值类型。

OrderBy()方法需要的参数类型为:Func<TSource, TKey>(委托类型)

    TSource: 集合中元素的类型。Tkey:返回值类型。

Select()方法需要的参数类型为: Func<TSource, TResult>(委托类型)

    TSource: 集合中元素的类型。TResult:返回值类型。

 

更多内容参见:Where()OrderBy()Select()方法的定义。

3)、方法语法(委托类型/匿名方法)----与(1)等价

Func<string, bool> filter = delegate(string s) { return s.Length == 5; };

Func<string, string> extract = delegate(string s) { return s; };

Func<string, string> project = delegate(string s) { return s.ToUpper(); };

 

IEnumerable<string> query =

    names.Where(filter).OrderBy(extract).Select(project);

4)、方法语法(委托类型/具体方法)----与(1)等价

Func<string, bool> filter = Method1;

Func<string, string> extract = Method2;

Func<string, string> project = Method3;

 

IEnumerable<string> query =

    names.Where(filter).OrderBy(extract).Select(project);

 

bool Method1(string s) { return s.Length == 5; }

string Method2(string s) { return s; }

string Method3(string s) { return s.ToUpper(); }

5LINQ延迟执行

1)、LINQ延迟执行

var result =

    from c in Products

    where c.Price > 500

    select c;

foreach (Product p in result)

{

    ……

}

● 查询变量result只是对linq查询的一个描述,而非执行结果。换言之,linq查询并不立即执行得到结果,而是将查询缓存在变量result中。那么result是什么类型呢?IQueryable<T>public interface IQueryable<T> : IEnumerable<T>, IQueryable, IEnumerable

IQueryable<T>只是查询表示,类似于SqlCommand等命令表示,真正的查询执行要依赖于在其上的调用操作。SqlCommand调用ExecuteNonQuery()ExecuteReader()等方法将导致查询的执行;foreach语句会导致IQueryable<T>上的方法GetEnumerator()执行,从而导致查询的执行,并将返回结果IEnumerator<T>

缓存查询,而不立即执行的这种现象称作延迟执行。

● 延迟执行如果使用不当会导致各种程序效率问题。

2)、改进LINQ延迟执行

var result =

    from c in Products

    where c.Price > 500

    select c;

var list = result.ToList<Product>;

foreach (Product p in list)

{

    ……

}

通过调用ToList或者ToArray方法,可以直接执行linq查询,将查询结果缓存在list变量中。

二、LINQ to SQL

1LINQ to SQL 简介

面向对象领域,一切围绕对对象(字段、方法、引用)。

关系型数据库领域,一切围绕表格数据(行、列、关系)。

     LINQ将关系数据库映射为对象模型。开发人员不是直接操作数据库,而是操作表示数据库的对象模型。对对象模型执行更改之后,将其提交给数据库执行。

     LINQ To SQLADO.NET的一个重要组件,而非完全替代品。

2、实体类映射

[Table(Name = "Category")]

public class Category

{

    [Column(IsPrimaryKey=true)]

    public string CategoryId;

    [Column]

    public string Name;

    [Column]

    public string Descn;

}

[Table]特性表示将Category与数据库中的表Category相对应。

[Column]特性则对应数据库中的列。

3DataContext数据上下文对象

DataContext db = new DataContext(@"server=./sqlexpress;database=pubs;uid=sa;pwd=123;");

Table<Category> Categorys = db.GetTable<Category>();

var result =

    from c in Categorys

select c;

DataContext类非常类似于数据库连接对象,但它为LINQ to SQL提供了更多支持。

4、定义基本关系

1)、一对多关系

[Table(Name = "Category")]

public class Category

{

    ……

    private EntitySet<Product> products;

    [Association(Storage="products",OtherKey="CategoryId")]

    public EntitySet<Product> Products

    {

        get { return this.products; }

        set { this.products.Assign(value); }

    }

}

Products映射了CategoryProduct之间的一对多关系。

2)、多对一关系

[Table(Name="Product")]

public class Product

{

    ……

    private EntityRef<Category> category;

    [Association(Storage = "category", ThisKey = "CategoryId")]

    public Category Category

    {

        get { return this.category.Entity; }

        set { this.category.Entity = value; }

    }

}

Category映射了ProductCategory之间的多对一关系。

5、对象关系设计器

对象关系设计器将自动完成实体类映射、定义基本关系、创建DataContext子类等工作。

在项目中添加新项“LINQ to SQL类”,将创建默认名称为“DataClasses1.dbml”的文件。

1)、查询代码片段

//查询居住在加利福尼亚的所有作者

void Method1()

{

    DataClasses1DataContext database = new DataClasses1DataContext();

    var authors = from a in database.authors

                  where a.state == "CA"

                  select new

                  {

                      Name = a.au_fname + " " + a.au_lname

                  };

    foreach (var a in authors)

        Console.WriteLine(a.Name);

}

2)、新增代码片段

//插入新行(一个表)

void Method2()

{

    DataClasses1DataContext database = new DataClasses1DataContext();

    authors a = new authors()

    {

        au_id = "123-45-6789",

        au_fname = "wang",

        au_lname = "cheng",

        phone = "13701362855"

    };

    database.authors.InsertOnSubmit(a);//InsertOnSubmit()方法只影响对象模型

    database.SubmitChanges();//使用SubmitChanges()方法将更改保存到数据库

}

//插入新行(多个表)

void Method3()

{

    //插入来自于新作者(author)的新书(title)的书名(authortitle

    DataClasses1DataContext db = new DataClasses1DataContext();

    authors a = new authors()

    {

        au_id = "234-56-7890",

        au_fname = "zhang",

        au_lname = "ting",

        phone = "13811765843"

    };

    titles t = new titles()

    {

        title_id = "ZT5555",

        title = "C#高级编程(第六版)",

        pubdate = System.DateTime.Now,

        type = "computer"

    };

    titleauthor ta = new titleauthor()

    {

        authors = a,

        titles = t

    };

    db.titleauthor.InsertOnSubmit(ta);//需要指示titleauthor表中的title_idauthor_idLINQ to SQL会自动执行该操作。

    db.SubmitChanges();

 

}

3)、更新代码片段

//更新行

void Method4()

{

    DataClasses1DataContext db = new DataClasses1DataContext();

    titles bookTitle =

        (from t in db.titles

        where t.title_id == "ZT5555"

        select t).Single();//Single()方法返回序列中的唯一元素。如果该序列中一个元素也没有,则会跑出异常。

    bookTitle.title = "SQL Server 核心技术";

    db.SubmitChanges();

}

4)、删除代码片段

//删除行(一个表)

void Method5()

{

    DataClasses1DataContext db = new DataClasses1DataContext();

    var author = from a in db.authors

                 where a.au_id == "123-45-6789"

                 select a;

    if (author.Count() > 0)

    {

        db.authors.DeleteOnSubmit(author.First());

        db.SubmitChanges();

    }

}

//删除行(多个表)

void Method6()

{

    DataClasses1DataContext db = new DataClasses1DataContext();

    string au_id_to_remove = "234-56-7890";

    //删除author

    var author = from a in db.authors

                 where a.au_id == au_id_to_remove

                 select a;

    foreach (var a in author)

        db.authors.DeleteOnSubmit(a);

    //删除titleauthor

    var titleauthor = from ta in db.titleauthor

                      where ta.au_id == au_id_to_remove

                      select ta;

    foreach (var ta in titleauthor)

        db.titleauthor.DeleteOnSubmit(ta);

    //程序里无论先删除author,还是先删除titleauthor都无所谓

    //LINQ会自动先删除titleauthor,再删除author

    db.SubmitChanges();

 

}

5)、LINQ to SQL示例

LINQ to SQL示例

三、LINQ to XML

1.NET Framework3.5中新的XML对象

XDocument对象:这个对象可以创建XML文档。它替代了.NET3.5之前的XmlDocument对象。

Xdocument对象的一个重要成员是Load()方法。这个方法将XML文档加载到内存中。

XDocument xdoc = XDocument.Load(@"C:/Hamlet.xml");

另一个重要成员是Save()方法。可以将xml文档保存到物理文件中。

XDocument xdoc = XDocument.Load(@"C:/Hamlet.xml");

xdoc.Save(@"C:/CopyOfHamlet.xml");

XElement对象:表示单个元素的对象。

XNamespace对象:表示XML命名空间。

XComment对象:将注释添加到XML文档。

XAttribute对象:用于添加和使用属性。

2LINQ查询XML代码片段

XDocument LibraryBooks = new XDocument();

LibraryBooks = XDocument.Load("Books.xml");

var query =

    from book in LibraryBooks.Descendants("Book")

    where book.Element("Publisher").Value == "Wrox"

    select book.Element("Title").Value;

Console.WriteLine("Wrox出版的所有书籍");

foreach (var book in query)

       Console.WriteLine(book);

四、LINQ to DataSet

1、查询一个表代码片段

EnumerableRowCollection<DataRow> authors =

    from author in ds.Tables[0].AsEnumerable()

    where author.Field<string>("state") == "CA"

    select author;

foreach (DataRow dr in authors)

{

    Console.WriteLine("{0} - {1} {2}", dr["au_id"].ToString(), dr["au_fname"].ToString(), dr["au_lname"].ToString());

}

2、使用聚合函数代码片段

var query =

    ds.Tables[0].AsEnumerable()

    .Count(a => a.Field<string>("state") == "CA");

Console.WriteLine("{0}个结果", query);

 

int[] nums = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

var maxOddNum = nums.Where(n => n % 2 == 1).OrderByDescending(n => n).Max();

Console.WriteLine("最大的奇数是:{0}", maxOddNum);

 

var sumOfNums = nums.Where(n => n % 2 == 1).OrderByDescending(n => n).Sum();

Console.WriteLine("所有奇数的和为:{0}", sumOfNums);

3、查询两个表代码片段

var query =

    (from customer in customersTable.AsEnumerable()

    join order in ordersTable.AsEnumerable()

    on customer.Field<string>("customerID") equals order.Field<string>("customerID")

    select new

    {

        id=customer.Field<string>("customerID"),

        CompanyName = customer.Field<string>("CompanyName"),

        ContactName = customer.Field<string>("ContactName"),

        OrderDate = order.Field<DateTime>("OrderDate"),

        ShipCountry = order.Field<string>("ShipCountry")

    }).ToList();

dataGridView1.DataSource = query;

4、查询类型化DataSet

向项目中添加新建项DataSet,命名为:TypedCustomerDataSet.xsd

在服务器资源管理器窗口中,打开希望使用的数据库连接。该示例使用Northwind数据库。

Customer表拖到TypedCustomerDataSet.xsd的设计界面上。

SqlConnection conn;

SqlCommand cmd;

SqlDataAdapter da;

TypedCustomerDataSet ds = new TypedCustomerDataSet();

conn = new SqlConnection(@"server=.;database=northwind;uid=sa;pwd=sa");

cmd = new SqlCommand("select * from customers;", conn);

da = new SqlDataAdapter(cmd);

da.Fill(ds,"Customers");

var query =

    (from customer in ds.Customers

     where customer.Country == "USA"

    select new

    {

        customer.CustomerID,

        customer.CompanyName,

        customer.ContactName,

        customer.ContactTitle

    }).ToList();

dataGridView1.DataSource = query;

5、检测空字段代码片段

var query =

    (from customer in ds.Customers

     //where语句中的条件求值时使用了短路方式,因此必须将IsNull()方法放在其他条件之前

     //如果没有使用类型化的DataSet,就不需要显示地检查空值

     where !customer.IsNull("Region") && customer.Region == "WA"

     select new

     {

         customer.CustomerID,

         customer.CompanyName,

         customer.ContactName,

         customer.ContactTitle

     }).ToList();

dataGridView1.DataSource = query;

6、将结果保存到DataTable代码片段

var query =

    from customer in ds.Customers

     where customer.Country == "USA"

     select customer;

//CopyToDataTable()不适用于计划使用匿名类型的查询或执行表连接的查询。

DataTable USACustomers = query.CopyToDataTable();

dataGridView1.DataSource = USACustomers;

五、其他

1LINQ to SQL示例

创建一个Windows应用程序。

在项目中添加新项LINQ to SQL类,对该项使用默认名称DataClasses1.dbml

在服务器资源管理器中打开希望使用的数据库连接,在该示例中使用pubs样本数据库。

将以下表拖到DataClasses1.dbml的设计界面上:authorspublisherstitleauthortitles

保存DataClasses1.dbml文件。

下面的代码包括查询、新增、更新和删除的功能。

private void Form1_Load(object sender, EventArgs e)

{

    Method1();

}

//查询居住在加利福尼亚的所有作者

void Method1()

{

    DataClasses1DataContext database = new DataClasses1DataContext();

    var authors = from a in database.authors

                  where a.state == "CA"

                  select new

                  {

                      Name = a.au_fname + " " + a.au_lname

                  };

    foreach (var a in authors)

        Console.WriteLine(a.Name);

}

//插入新行(一个表)

void Method2()

{

    DataClasses1DataContext database = new DataClasses1DataContext();

    authors a = new authors()

    {

        au_id = "123-45-6789",

        au_fname = "wang",

        au_lname = "cheng",

        phone = "13701362855"

    };

    database.authors.InsertOnSubmit(a);//InsertOnSubmit()方法只影响对象模型

    database.SubmitChanges();//使用SubmitChanges()方法将更改保存到数据库

}

//插入新行(多个表)

void Method3()

{

    //插入来自于新作者(author)的新书(title)的书名(authortitle

    DataClasses1DataContext db = new DataClasses1DataContext();

    authors a = new authors()

    {

        au_id = "234-56-7890",

        au_fname = "zhang",

        au_lname = "ting",

        phone = "13811765843"

    };

    titles t = new titles()

    {

        title_id = "ZT5555",

        title = "C#高级编程(第六版)",

        pubdate = System.DateTime.Now,

        type = "computer"

    };

    titleauthor ta = new titleauthor()

    {

        authors = a,

        titles = t

    };

    db.titleauthor.InsertOnSubmit(ta);//需要指示titleauthor表中的title_idauthor_idLINQ to SQL会自动执行该操作。

    db.SubmitChanges();

 

}

//更新行

void Method4()

{

    DataClasses1DataContext db = new DataClasses1DataContext();

    titles bookTitle =

        (from t in db.titles

        where t.title_id == "ZT5555"

        select t).Single();//Single()方法返回序列中的唯一元素。如果该序列中一个元素也没有,则会跑出异常。

    bookTitle.title = "SQL Server 核心技术";

    db.SubmitChanges();

}

//删除行(一个表)

void Method5()

{

    DataClasses1DataContext db = new DataClasses1DataContext();

    var author = from a in db.authors

                 where a.au_id == "123-45-6789"

                 select a;

    if (author.Count() > 0)

    {

        db.authors.DeleteOnSubmit(author.First());

        db.SubmitChanges();

    }

}

//删除行(多个表)

void Method6()

{

    DataClasses1DataContext db = new DataClasses1DataContext();

    string au_id_to_remove = "234-56-7890";

    //删除author

    var author = from a in db.authors

                 where a.au_id == au_id_to_remove

                 select a;

    foreach (var a in author)

        db.authors.DeleteOnSubmit(a);

    //删除titleauthor

    var titleauthor = from ta in db.titleauthor

                      where ta.au_id == au_id_to_remove

                      select ta;

    foreach (var ta in titleauthor)

        db.titleauthor.DeleteOnSubmit(ta);

    //程序里无论先删除author,还是先删除titleauthor都无所谓

    //LINQ会自动先删除titleauthor,再删除author

    db.SubmitChanges();

}

2、杂项

数据库中的一行数据,用linq查询多次,转换成的对象是同一个对象(引用相等)。

两次查询同一行数据的中间在数据库对该行数据进行了更改,内存中的对象的值是不发生变化的。

● 过滤泛型List示例:

 

List<Customer> listCustomerAll;

Customer model;

List<Customer> listTemp = new List<Customer>();

listTemp = listCustomerAll.FindAll(

        new Predicate<Customer>(delegate(Customer c) { return c.customerID == model.ID; })

    );

 

原创粉丝点击