标签:阅读   desktop   依据   amr   调用   ups   出版   赋值   聚合   
LINQ(Language Integrated Query)
LINQ语言集成查询是一组用于C#语言的扩展。它允许编写C#代码对数据集进行查询,这等同于使用数据库查询语句,这样程序员不必掌握数据库查询语句而是使用Linq就能完成相同的查询任务。而传统数据查询的弱点很多,比如执行简单查询也需要冗长的操作代码,查询语句是字符串格式,无法让编译器执行检查错误及早提示,查询不是强类型,查询参数容易写错,查询结果没有真正面向对象,每次查询取结果还得事先知道列名或列索引,不使用抽象工厂的前提下,查询语句是定死的,只能应用于一种数据库。 即使使用抽象工厂,代码量也巨大 

 
Lambda表达式
格式
Lambda表达式比函数有着更为简洁的语法格式。在某些情况下使用Lamnbda就可以替代函数,让代码变得更简洁易读。而定义Lambda表达式与定义函数其实差别不大,而且还做到了简化。 
e => e.Name == "sam" 
定义函数你得声明函数的权限修饰符、形态修饰符和参数列表、返回类型等,而Lambda就这么一段代码就做完了函数的工作
e:参数
=>:操作符,可描述执行后面的逻辑……操作符后面的则是表达式,也可以是语句块。无论是表达式还是语句块都必须返回一个结果,哪怕是返回void。
 
示例
(string e) => e.Name == "sam"; 
e => e.Name == "sam"; 
e => { return e.Name == "sam"; }  
(string e, string x)=>{ return e.Name == "sam" && e.Age = 32; } 
(e, x) => { return e.Name == "sam" && e.Age = 32; } 
()=>Console.WriteLine(); 
 
Lambda与委托
Lambda就是一个匿名的函数,所以你可以将Lambda当成委托实例作为参数进行传递,如果要这样做,则你定义的Lambda表达式的参数和返回值类型必须符合委托的参数和返回类型。 

 
class Program
{
    static void ShowTime(Func<DateTime> d) 
    {
        string time= d().ToString(); 
        Console.WriteLine(time);
    }
    static void Main(string[] args)
    {
        
        
        
        
        Func<DateTime> GetDateTime = () => DateTime.Now;
        ShowTime(GetDateTime);
    }
}
 
View Code 
LINQ的扩展方法(LINQ操作符)
LINQ对实现了IEnumrable<T>接口的泛型集合类型扩展出了一系列的方法,这些方法提供了排序、提取等操作,也将这些扩展方法称为LINQ操作符。

 
1.OrderByDescending()
 
2.Take(int count)
 
3.Sum(Fun < T, decimal > selector)
 
View Code 
LINQ延迟查询机制
LINQ查询操作符并不会立即执行,它总是在查询结果集真正被使用的时候才会开启查询。 而且每次只讲迭代的当前元素存入内存中处理。这就降低了内存占用率。

 
static double Square(double n)
{
    Console.WriteLine("执行查询:" + n);
    return n;
}
static void Main(string[] args)
{
    double[] array = { 100, 5, 8,900 };
    
    var records = from n in array
                    select Square(n);
 
    
    
    
 
    
    foreach (var r in records) { }
}
 
测试延迟查询但警惕聚集计算操作符和某些需要一次性统计所有集合的操作符如OrderBy、Reverse、ToList、ToArray、ToDictionary,它们都会破坏延迟查询,一次性将所有集合元素存入内存以便处理。这就是为什么很多人说LINQ查询的性能不高的根本原因。
 
LINQ查询表达式
LINQ查询表达式类似于SQL查询语句,但个人不喜欢它的书写格式。它是由from、join group by等句子组成的LINQ查询表达式,而LINQ查询表达式与直接使用LINQ扩展方法执行查询在语法结构上是不一样的,我认为后者更易于阅读,所以此处略过。
 
LINQ表达式树(System.Linq.Expressions.Expression)
表达式树是将代码以一种抽象的方式表示成一棵直观的对象树,树中每个节点就代表了一个表达式。表达式树不是可执行代码,它只是一种数据结构。但树能存储表达式的信息,以便根据不同的数据源构造出不同的查询方式、在运行期间分析表达式树,并进行查询语句的优化。
表达式树的创建
根据表达式的不同类型,可创建不同的表达式树。比如单个值的表达式使用ConstantExpression创建,而两个值的运算的表达式则由BinaryExpression创建。Lambda表达式树又由LambdaExpression创建,它们都派生自Expression基类。其它类可参考MSDN DOC。 
单值表达式树

 
ConstantExpression aa = ConstantExpression.Constant(6); 
ExpressionType nodeType = aa.NodeType; 
Type structType = aa.Type; 
 
单值表达式树
求值表达式树

 
ConstantExpression aa = ConstantExpression.Constant(6);
ConstantExpression bb = ConstantExpression.Constant(6);
BinaryExpression sum = BinaryExpression.Add(aa, bb); 
ExpressionType nodeType=sum.NodeType; 
Type structType = sum.Type; 
 
求值表达式树
 图
图Lambda表达式树
Lambda是一个匿名函数,所以它自身是一个表达式,而其每个参数、方法体代码都是表达式。Lambda表达式树是由LambdaExpression创建,但构造这样的树要使用Expression.Lambda()方法,而通常可能使用Expression<Func<TSource>>来创建Lambda表达式树。
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource> predicate);
//IQueryable<TSource>泛型接口类型的泛型集合实现了IEnumerable<TSource>相同的LINQ扩展方法,但它的第二个参数是能构造表达式树的Expression<Func<TSource>>类型
public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource>> predicate); 
 
 

 
Expression<Func<int, int, int>> lambdaTree = (x, y) => x + y;
ParameterExpression paramNode1 = lambdaTree.Parameters[0]; 
ParameterExpression paramNode2 = lambdaTree.Parameters[1]; 
BinaryExpression body = lambdaTree.Body as BinaryExpression; 
ParameterExpression leftNode = body.Left as ParameterExpression; 
ParameterExpression rightNode = body.Right as ParameterExpression; 
Func<int, int, int> GetLambda = lambdaTree.Compile(); 
int result = GetLambda(1, 2);
ExpressionType nodeType = lambdaTree.NodeType; 
Type type = lambdaTree.Type; 
 
Lambda表达式树
 图
图 
LINQ To Object
查询泛型集合
所有的LINQ操作符(即扩展方法)均定义在System.Linq.Enumerable静态类中,为所有实现了IEnumerable<T>的强类型集合扩展出了LINQ查询方法。所以数组、泛型集合都可以使用LINQ查询。

 
object[] objArray = { "sam", 32, "beijingStreet", false, ‘a‘ };
var records = objArray.Select(m => m.GetType().FullName).OrderBy(t => t);
ObjectDumper.Write(records);
 
Book[] books =
{
    new Book{  Title="万有引力之虹", Isbn="993748928", PageCount=300 },
    new Book{  Title="解体概要", Isbn="325757665", PageCount=500 },
    new Book{  Title="寂静的春天", Isbn="229911000", PageCount=200 }
};
 
var records = books.Where(b => b.Isbn.Contains("9")).Select(b => b.Title);
ObjectDumper.Write(records);
 
List<Book> bList = new List<Book>
{
    new Book{  Title="万有引力之虹", Isbn="993748928", PageCount=300 },
    new Book{  Title="解体概要", Isbn="325757665", PageCount=500 },
    new Book{  Title="寂静的春天", Isbn="229911000", PageCount=200 }
};
var records = bList.Where(b => b.Isbn.Contains("9")).Select(b => b.Title);
ObjectDumper.Write(records);
 
Dictionary<string, int> bNary = new Dictionary<string, int>
{
    { "sam", 12 },
    { "corz", 22 },
    { "korn", 53 },
};
 
var records = bNary.Where(o => o.Value > 20);
ObjectDumper.Write(records);
 
View Code查询非泛型集合
只需要使用Cast<T>将非泛型集合转换为泛型即可。 

 
ArrayList list = new ArrayList()
{
    new Book{ Title="寂静的春天"},
    new Book{ Title="万有引力之虹"},
    new Book{ Title="解体概要"}
};
 
var records = list.Cast<Book>().Select(b => new { bName= b.Title });
 
View Code参数化查询
可在查询中使用变量,还可以动态构造查询,比如定义一个函数,函数以Func泛型委托做参数,通过条件测试传递不同的Lambda表达式。

 
<body>
    <select name="combobox">
        <option value="0">查询标题</option>
        <option value="1">查询出版社</option>
    </select>
    <div> 
        @ViewData["show"]
    </div>
</body>
 
View Code
 
[HttpPost]
public ActionResult Index(string combobox)
{
    string recordsJson = string.Empty;
    switch (combobox)
    {
        case "0":
            recordsJson=ComboboxLambda(b => b.Title);
                break;
        case "1":
            recordsJson=ComboboxLambda(b=>b.Publisher.Name);
            break;                
    }
    ViewData["show"] = recordsJson;
    return View();
}
 
[NonAction]
public string ComboboxLambda<T>(Func<Book,T> selector)
{
    var records = SampleData.books.Select(selector);
    return JsonConvert.SerializeObject(records);
}
 
View CodeLINQ查询是一系列的链式操作,所以完全可以根据条件动态增加查询,所以以下代码并不会发生覆盖而是追加。

 
[HttpPost]
public ActionResult Index(int maxPage, string orderByTitle)
{
 
    IEnumerable<Book> books = SampleData.books;
    if (maxPage != 0)
    {
        books = books.Where(b => b.PageCount > maxPage);
    }
    if (!string.IsNullOrEmpty(orderByTitle))
    {
        books = books.OrderBy(b => b.Title);
    }
    books = books.Select(b => b); //查询会在符合条件的判断中形成链式操作
 
    ViewData["show"] = JsonConvert.SerializeObject(books);
    return View();
}
 
View Code 读取文件
一个txt文件存储了图书信息,为StreamReder添加一个扩展方法用于获取所有行,再通过LINQ查询将数据打包。 


 
public static class StreamRederExtention
{
    /// <summary>
    /// 读取每一行
    /// </summary>
    /// <param name="source"></param>
    /// <returns></returns>
    public static IEnumerable<string> Lines(this StreamReader source)
    {
        string line;
        while (!string.IsNullOrEmpty(line = source.ReadLine()))
        {
            yield return line;
        }
    }
}
 
View Code
 
public ActionResult Index()
{
    string filePath = Server.MapPath("~/bookMessage.txt");
    StreamReader reader = new StreamReader(filePath);
    using (reader)
    {
        var bookMsg = reader.Lines()
        .Where(line => !line.StartsWith("#")) //过滤掉首行
        .Select(line => line.Split(‘,‘))
        .Select(part => new
        {
            Isnb = part[0],
            title = part[1],
            publisher = part[2],
            author = part[3].Split(‘;‘).Select(authorPerson => authorPerson)
        });
 
        ViewData["show"] = JsonConvert.SerializeObject(bookMsg);
    }
    return View();
}
 
View Code 
 
 
 
 
 
 
 
LINQ操作符
1.过滤
操作符:Where()
用于对泛型集合元素进行筛选。此方法要求一个Fun<T,Key>的泛型委托,它会自动迭代集合元素,每迭代一次将调用泛型委托,并把集合中的一条记录作为参数传递给委托(Lambda),Where()方法期待该委托返回一个布尔值,为真时,将对象保留,否则排除。此方法所要求的委托还可以有第三个参数:Index,表示当前对象在集合中的索引号。如
books.Where((b, index) => b.Title.Length > 10 && index > 1)
 
2.投影
2-1.操作符:Select()
指定需要查询的元素的成员以便将它们组成新的结果集返回。
操作符:SelectMany()
指定需要查询的元素的集合成员以便将它们组成新的结果集返回。主要应用于一个对象的属性是一个子集合时,将取出子集合并组成新的结果集返回。此方法所要求的委托还可以有第三个参数:Index,表示当前对象在集合中的索引号。
var records = SampleData.books.Where(b => b.Title.Length > 10 ).SelectMany( b => b.Authors ); 
 
3.去重
操作符:Distinct()
去除集合中的重复元素并返回去除后的结果集。此方法内部维护了一个测试相等性的比较器,如果元素是结构或字符串类型,则比较所有元素的值以确认元素是否相等,如果元素是引用类型,则比较所有元素在堆上的引用地址以确认元素是否相等。可是多半在执行查询时查询的是对象,对象多半都是不相等的堆引用,所以根本没法去重,别急!此方法还有重载,重载版可实现自定义的测试相等性的规则,重载版需要第二参数,参数是一个IEqualityComparer<T>泛型接口,也即你必须实现这个接口从而实现自定义的比较器。

 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Text;
using System.Web.UI;
using System.IO;
 
 
namespace Yin.General
{
    
    
    
    public static class LinqHelper<T>
    {
        #region 公共方法
 
        public static IEqualityComparer<T> CreateComparer<TV>(Func<T, TV> keySelector)
        {
            return new CommonEqualityComparer<TV>(keySelector);
        }
 
        public static IEqualityComparer<T> CreateComparer<TV>(Func<T, TV> keySelector, IEqualityComparer<TV> comparer)
        {
            return new CommonEqualityComparer<TV>(keySelector, comparer);
        }
 
        #endregion
 
        #region Nested type: CommonEqualityComparer
 
        private class CommonEqualityComparer<TV> : IEqualityComparer<T>
        {
            private readonly IEqualityComparer<TV> _comparer;
            private readonly Func<T, TV> _keySelector;
 
            public CommonEqualityComparer(Func<T, TV> keySelector, IEqualityComparer<TV> comparer)
            {
                _keySelector = keySelector;
                _comparer = comparer;
            }
 
            public CommonEqualityComparer(Func<T, TV> keySelector)
                : this(keySelector, EqualityComparer<TV>.Default)
            { }
 
            #region IEqualityComparer<T> Members
 
            public bool Equals(T x, T y)
            {
                return _comparer.Equals(_keySelector(x), _keySelector(y));
            }
 
            public int GetHashCode(T obj)
            {
                return _comparer.GetHashCode(_keySelector(obj));
            }
 
            #endregion
        }
 
        #endregion
    }
}
 
自定义比较器有了以上的自定义的比较器,现在可以对元素的某个成员进行去重了。
var records = SampleData.Authors.Distinct(LinqHelper<Author>.CreateComparer<string>(a=>a.FirstName)); 
 
4.转换
操作符:ToArray() | ToList() | ToDictionary() | ToCast<T>()
 
前三个操作符将立即执行查询并将结果集转换为数组、泛型集合、泛型字典集合。LINQ有延迟查询机制,转换方法可立即开启查询。最后一个将非泛型集合转换为泛型集合,以此可查询非泛型集合
5.聚合
操作符:Sum() | Count() | Min() | Max()
所见即所得
6.排序
操作符:OrderBy() | OrderByDescending() | ThenBy() | ThenByDescending()
7.嵌套
所谓嵌套即在一个操作符的查询中嵌套了另一个操作符。 
var records = SampleData.Publishers.Select(p => p).Select(p => new { publisher = p.Name, books = SampleData.books.Where(b => b.Publisher == p).Select(b => b.Title)  });
 
8.分组
操作符:GroupBy()
将集合中的元素按委托指定的规则进行分组。返回一个IEnumerable<IGrouping<TKey, TSource>>的泛型字典集合,该集合存储的是每个组的元素集合和一个Key,Key则是按此元素进行分组的元素。
var records = SampleData.books.GroupBy(b => b.Publisher).Select(group => new { books = group.Select(b=>b.Title), publisher = group.Key.Name });
 
多条件分组
即同时满足多个条件的相同元素被分为一组。
//按多个条件分组
//此处查询图书,按图书出版社和图书类别进行分组,
//将这两个条件封装到一个匿名对象中,此时GroupBy会迭代每本图书并对照出版社和图书类别是否与另一本书是完全一样的,完全一样则会分成一组,不一样则单独分一组
//GroupBy返回的并不是匿名对象,而是分组后的图书信息集合groups
var records = SampleData.books.GroupBy(b => new { b.Publisher, b.Subject });
 
//如果想在集合中显示分组标题则可以在组上继续迭代以便在每个组中增加标题
//迭代groups,取出每一个组(group),通过Key可以得到封装分组条件的匿名对象
var records = SampleData.books.GroupBy(b => new { b.Publisher, b.Subject }).Select(group => new {
        pName = group.Key.Publisher.Name,
        subject = group.Key.Subject.Name,
        bName = group.Select(b=>b.Title)
    });
 
9.联结
9-1.内联结:只返回匹配的结果集。
操作符:Join(联结的另一个集合,能返回被查询的左边集合的关联键位的函数,能返回被联结的右边集合的关联键位的函数,根据前两个函数的参数能创建新结果集的函数)
var records = SampleData.Publishers.Join( SampleData.books, p => p.Name, b => b.Publisher.Name, ( p , b ) => new { publisherName= p.Name , bookTitle = b.Title } );
( p , b ) => new {  p.Name , b.Title };
 
9-1.左联结(组联结):不满足匹配时,保证左边集合数据的完整性,右边集合以null填充
操作符:GroupJoin(联结的另一个集合,能返回被查询的左边集合的关联键位的函数,能返回被联结的右边集合的关联键位的函数,根据前两个函数的参数能创建新结果集的函数) 
var results = SampleData.Publishers
.GroupJoin(SampleData.books, p => p.Name, b => b.Publisher.Name, (p, books) =>
new { 出版社=p.Name,图书= books.Count()==0?"没有图书":string.Join(",",books) });
 
LINQ表达式版本的左外联结
LINQ操作符其实没有左联结的说法,所使用的就是组联结而已,而LINQ表达式可以实现真正的左联结。 

 
var records = from p in SampleData.Publishers
                join b in SampleData.books
                on p.Name equals b.Publisher.Name
                into newTables
                from b in newTables.DefaultIfEmpty()
                select new { pName = p.Name, bName = b == default(Book) ? "没有图书" : b.Title };
 
View Code9-2.右联结:要先满足哪个集合就把该集合写在左边就行了
10.略过
操作符:Skip()
跳过集合中指定个数的元素,返回剩下的元素。
11.提取
操作符:Take()
从索引起始位置开始提取指定个数的元素。
 
 
 
 
 
 
 
 
 
 
 
 
LINQ To SQL
让类使用Table和Column特性
System.Data.Linq.Mapping命名空间提供了为类添加特性,特性可以看成是对类、类成员字段的一种描述。当为类应用特性后,使用LINQ查询时,类将被映射为数据库的表,查询语句就可以支持强类型对象,同时数据库返回的结果也同样被映射为强类型的对象。特性Table应用于类,特性Column应用于字段。

 
[Table(Name = "Employees")] //声明此类映射数据库的Employees表
public class Employee
{
    [Column(IsPrimaryKey =true,Name = "EmployeeID")] //声明ID映射EmployeeID列并且是主键
    public int ID { get; set; }
    [Column(Name = "FirstName")] 
    public string Name { get; set; }
    [Column]
    public string City { get; set; } //与数据库表同名的字段只需要声明是Column
    [Column]
    public string Address { get; set; }
}
 
View Code
 
public class DefaultController : Controller
{
    public ActionResult Index()
    {          
        DataContext context = new DataContext("server=DESKTOP-D445L4U;database=Northwind;uid=sa;pwd=123456");
        var records = from e in context.GetTable<Employee>()
                      where e.ID % 2 == 0
                      select e;
        StringBuilder s = new StringBuilder();
        foreach(var record in records)
        {
            s.AppendFormat("<div>ID:{0}   Name:{1}   Address:{2}   City:{3}</div>", record.ID, record.Name, record.Address, record.City);
        }
 
        ViewData["records"] = s.ToString();
        return View();
    }
}
 
View Code 
 
ASP.NET -  LINQ 语言集成查询
标签:阅读   desktop   依据   amr   调用   ups   出版   赋值   聚合   
原文地址:http://www.cnblogs.com/myrocknroll/p/7338793.html