LINQ(Language Integrated Query) 

LINQ语言集成查询是一组用于C#语言的扩展。它允许编写C#代码对数据集进行查询,这等同于使用数据库查询语句,这样程序员不必掌握数据库查询语句而是使用Linq就能完成相同的查询任务。而传统数据查询的弱点很多,比如执行简单查询也需要冗长的操作代码,查询语句是字符串格式,无法让编译器执行检查错误及早提示,查询不是强类型,查询参数容易写错,查询结果没有真正面向对象,每次查询取结果还得事先知道列名或列索引,不使用抽象工厂的前提下,查询语句是定死的,只能应用于一种数据库。 即使使用抽象工厂,代码量也巨大。 在Linq中,正是由Enumerable类扩展了IEnumerable<T>接口的方法,才使所有实现了IEnumerable<T>接口的类型可以使用Linq查询。而在System.Collections命名空间下的集合类虽然不能使用那些从IEnumerable<T>上扩展的方法,但可以通过调用非泛型集合的Cast<T>()将其转换为泛型集合,从而可以使用Enumerable类提供的扩展方法。System.Linq.Enumerable 类
 

 

Lambda表达式

格式

Lambda表达式比函数有着更为简洁的语法格式。在某些情况下使用Lamnbda就可以替代函数,让代码变得更简洁易读。而定义Lambda表达式与定义函数其实差别不大,而且还做到了简化。 

e => e.Name == "sam" 
定义函数你得声明函数的权限修饰符、形态修饰符和参数列表、返回类型等,而Lambda就这么一段代码就做完了函数的工作
e:参数
=>:操作符,可描述执行后面的逻辑……操作符后面的则是单个表达式,也可以是语句块。即函数的方法体。

示例

复制代码
//有{}代码块的称为语句Lambda,无{}代码块的称为Lambda表达式
(string e=> e.Name == "sam"// 显示声明了参数 操作符后面是求值表达式 也即返回一个求值的结果
=> e.Name == "sam"// 隐式声明了参数 操作符后面是求值表达式
=> return e.Name == "sam"}  // 隐式声明了参数 操作符后面是求值语句块
(string estring x)=>return e.Name == "sam" && e.Age 32//显示声明了多参数 操作符后面是求值语句块
(ex=> return e.Name == "sam" && e.Age 32//隐式声明了多参数 操作符后面是求值语句块
()=>Console.WriteLine(); //无参数 操作符后面是执行语句 返回void
复制代码

Lambda与委托

Lambda就是一个匿名的函数,所以你可以将Lambda当成委托实例作为参数进行传递,如果要这样做,则你定义的Lambda表达式的参数和返回值类型必须符合委托的参数和返回类型。 

复制代码
class Program
{
    static void ShowTime(Func<DateTimed//接收一个泛型委托
    {
        string timed().ToString(); 
        Console.WriteLine(time);
    }
    static void Main(string[] args)
    {
        //定义一个返回时间的Lambda表达式
        //因为Lambda表达式就是函数的简洁版,所以它也可以是委托类型,以便将它当做参数传递
        //使用内置Func<T>泛型委托是因为这种委托被设计成可以有返回值,这符合Lambda的需求,它返回一个时间
        //使用Action委托就不可以了,因为Action委托被设计成不能有返回值,而Lambda又需要返回一个值
        Func<DateTimeGetDateTime () => DateTime.Now;
        ShowTime(GetDateTime);
    }
}
复制代码

 

LINQ的扩展方法(LINQ操作符)

Enumerable类为IEnumrable<T>接口扩展出了一系列的方法,这些方法为实现了IEnumrable<T>接口的泛型集合提供了查询、排序、提取等操作,也将这些扩展方法称为LINQ操作符。

C# - Enumerable 类

 

LINQ延迟查询机制

LINQ查询操作符并不会立即执行,它总是在查询结果集真正被使用的时候才会开启查询。 而且每次只将迭代的当前项存入内存中处理。这就降低了内存占用率。

复制代码
static double Square(double n)
{
    Console.WriteLine("执行查询:" n);
    return n;
}
static void Main(string[] args)
{
    double[] array 10058,900 };
    //延迟查询机制保证下面的查询并赋值的操作不会执行,直到records真正被使用的时候才会开启查询
    var records from in array
                    select Square(n);

    //想象一下假设有上千条数据需要被查询出来放进records,可是程序并不知道你在后来有没有使用过records
    //如果你根本不使用records,那么查询就没有意义,如果你真的没有使用它,那么上千条记录读进内存就是在浪费资源
    //所以查询并不会真的开启直到你确实使用了records时,查询才会开始运作

    //records被迭代时才会自动打开查询
    foreach (var in records}
}
复制代码

但警惕聚集计算操作符和某些需要一次性统计所有集合的操作符如OrderBy、Reverse、ToList、ToArray、ToDictionary,它们都会破坏延迟查询,通常情况下,使用Linq操作符返回的结果如果不是IEnumerable<T>类型则说明该查询会立即执行,这会造成一次性将所有集合项存入内存以便处理。这就是为什么很多人说LINQ查询的性能不高的根本原因。

 

LINQ查询表达式

LINQ查询表达式类似于SQL查询语句,但个人不喜欢它的书写格式。它是由from、join group by等句子组成的LINQ查询表达式,而LINQ查询表达式与直接使用LINQ扩展方法执行查询在语法结构上是不一样的,我认为后者更易于阅读,所以此处略过。

 

LINQ操作符

1.过滤

操作符:Where ( ) | ElementAt ( ) | ElementAtOrDefault ( ) | Single ( ) | SingleOrDefault ( ) | First ( ) | FirstOrDefault ( ) | Last ( ) | LastOrDefault ( )

Where():对泛型集合项进行筛选。此方法要求一个Fun<T,Key>的泛型委托,它会自动迭代集合项,每迭代一次将调用泛型委托,并把集合中的一条记录作为参数传递给委托(Lambda),Where()方法期待该委托返回一个布尔值,为真时,将对象保留,否则排除。言简意赅地说,此方法是用于减少集合的行数。此方法所要求的委托还可以有第三个参数:Index,表示当前对象在集合中的索引号。

books.Where((bindex=> b.Title.Length 10 && index 1)

ElementAt():根据参数索引查找处于该索引处的项,类似的ElementAtOrDefault()保证在该索引处没有项时将安全返回一个default<T>

Single() :返回集合中的唯一的项或提供一个过滤的参数,类似的FirstOrDefault()保证在集合中如果没有项则安全返回一个default<T>,两个方法都将在此情况下引发异常:如果集合存在多个项

First():返回集合中的第一个项或提供一个过滤的参数,如果集合为空则引发异常,类似的FirstOrDefault()保证在集合中如果没有项则安全返回一个default<T>

Last():返回集合中的最后一个项或提供一个过滤的参数,如果集合为空则引发异常,类似的LastOrDefault()保证在集合中如果没有项则安全返回一个default<T>

2.投影

操作符:Select ( ) | SelectMany()

Select():指定需要查询的项的成员以便将它们组成新的结果集返回。言简意赅地说,此方法是用于减少集合的列数。

SelectMany():指定需要查询的项的集合成员以便将它们组成新的结果集返回。主要应用于一个对象的属性是一个子集合时,将取出子集合并组成新的结果集返回。此方法所要求的委托还可以有第三个参数:Index,表示当前对象在集合中的索引号。

//过滤出图书标题大于10的图书并在结果集中查询出图书作者的各项信息
var records SampleData.books.Where(=> b.Title.Length 10 ).SelectMany=> b.Authors ); //Authors是book的属性,是一个包含n个属性的集合

3.去重

操作符:Distinct ( ) | Except ( ) | Intersect ( ) | Union ( )

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
{
    /// <summary>
    /// 常用的方法
    /// </summary>
    public static class LinqHelper<T>
    {
        #region 公共方法

        public static IEqualityComparer<TCreateComparer<TV>(Func<TTVkeySelector)
        {
            return new CommonEqualityComparer<TV>(keySelector);
        }

        public static IEqualityComparer<TCreateComparer<TV>(Func<TTVkeySelectorIEqualityComparer<TVcomparer)
        {
            return new CommonEqualityComparer<TV>(keySelectorcomparer);
        }

        #endregion

        #region Nested type: CommonEqualityComparer

        private class CommonEqualityComparer<TVIEqualityComparer<T>
        {
            private readonly IEqualityComparer<TV_comparer;
            private readonly Func<TTV_keySelector;

            public CommonEqualityComparer(Func<TTVkeySelectorIEqualityComparer<TVcomparer)
            {
                _keySelector keySelector;
                _comparer comparer;
            }

            public CommonEqualityComparer(Func<TTVkeySelector)
                this(keySelectorEqualityComparer<TV>.Default)
            }

            #region IEqualityComparer<T> Members

            public bool Equals(xy)
            {
                return _comparer.Equals(_keySelector(x), _keySelector(y));
            }

            public int GetHashCode(obj)
            {
                return _comparer.GetHashCode(_keySelector(obj));
            }

            #endregion
        }

        #endregion
    }
}
复制代码

有了以上的自定义的比较器,现在可以对项的某个成员进行去重了。

var records SampleData.Authors.Distinct(LinqHelper<Author>.CreateComparer<string>(a=>a.FirstName)); 

Except():将参数指定的集合与当前集合做比较,返回没有同时出现在两个集合中的项。也即求差集

Intersect():将参数指定的集合与当前集合做比较,返回同时出现在两个集合中的项。也即求交集

Union():将参数指定的集合与当前集合合并为一个集合返回,也即求并集。

4.转换

操作符:ToArray ( ) | ToList ( ) | ToDictionary ( ) | Cast<T>() | AsEnumerable ( ) | AsQueryable ( ) | ToLookup ( )

前三个操作符将立即执行查询并将结果集转换为数组、泛型集合、泛型字典集合。LINQ有延迟查询机制,以To做前缀的转换方法可立即开启查询。最Cast<T>将非泛型集合转换为泛型集合以便可以执行Linq查询,以As做前缀的两个方法用于转换目标类型,ToLookup() 同时也属于分组查询,请参看分组操作符。

5.聚合

操作符:Sum ( ) | Count ( ) | Min ( ) | Max ( ) | Average ( ) | Aggregate ( )

所见即所得,注意Count()方法,实现了ICollection接口的集合类型有一个Count的属性,而扩展方法为集合提供了Count()方法。无论是Count还是Count()都会执行遍历计算总项数,效率不如Any(),如果只是想确认集合是否包含至少一个项,应使用Any()方法。注意Aggregate()方法用于自定义的聚合操作。

6.排序

操作符:OrderBy ( ) | OrderByDescending ( ) | ThenBy ( ) | ThenByDescending ( ) | Reverse ( )

OrderBy():OrderBy()只能使用一次,如果多次使用了OrderBy()方法,那么只有最后一个会生效,要在一个已经OrderBy的集合上执行再排序应使用ThenBy(),ThenBy()可出现N次。

7.嵌套

所谓嵌套即在一个操作符的查询中嵌套了另一个操作符。 

//查询出版社并同时查询图书列表,找到与当前出版社匹配的图书
var records SampleData.Publishers.Select(=> p).Select(=> new publisher p.Namebooks SampleData.books.Where(=> b.Publisher == p).Select(=> b.Title)  });

8.分组

操作符:GroupBy ( ) | ToLookup ( )

GroupBy():将集合中的项按委托指定的规则进行分组。返回一个IEnumerable<IGrouping<TKeyTSource>>的泛型字典集合,该字典存储的是一个Key(组头)和归组的集合项。

//将出版社相同的图书分为一组。出版社本身是一个对象,被作为分组的依据,group即是组对象,它存储了分组的项集合,通过Key能得到出版社本身
var records SampleData.books.GroupBy(=> 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)
    });
复制代码

ToLookup():将集合中的项按委托指定的规则进行分组。返回一个ILookup<TKeyTSource>的无法删除、添加的泛型字典集合,该字典存储的是一个一对多关系的集合项,Key(组头)和归组的集合项。

注意此方法会立即执行,没有延迟。

复制代码
var products = GetList ( );
var groups = products.ToLookup ( p => p.Category ); //根据Category进行分组,Category将作为字典的键
products.RemoveAll ( p => p.Category == "Garden" );
foreach ( var group in groups )
{
    Console.WriteLine ( group.Key );
    foreach ( var item in group )
    {
        Console.WriteLine (  item );
    }
}
复制代码

9.联结

操作符:Join ( ) | GroupJoin ( )

9-1.内联结:只返回匹配的结果集。

Join ( 联结的另一个集合,能返回被查询的左边集合的关联键位的函数,能返回被联结的右边集合的关联键位的函数,根据前两个函数的参数能创建新结果集的函数 )

//内联结查询
var records SampleData.Publishers.JoinSampleData.books=> p.Name=> b.Publisher.Name=> new publisherNamep.Name bookTitle b.Title );
//将查询结果装入了一个匿名对象,也可以不指定匿名对象的字段名称,这样,则会将查询的字段作为匿名对象的字段
=> new {  p.Name b.Title };

9-1.左联结(组联结):不满足匹配时,保证左边集合数据的完整性,右边集合以null填充

GroupJoin ( 联结的另一个集合,能返回被查询的左边集合的关联键位的函数,能返回被联结的右边集合的关联键位的函数,根据前两个函数的参数能创建新结果集的函数 ) 

复制代码
//左外联结:lanmbda表达式版本必须使用GroupJoin,实际上是将两个集合以左边集合的键为组头将数据进行了分组,
//所以下面的p是出版社(组头),books是分组后的图书集合(分组后的图书集合注册在组头下面)
//测试books.Count()==0而不是测试books==null是因为books已经被初始化,所以不可能是null
var results SampleData.Publishers
.GroupJoin(SampleData.books=> p.Name=> b.Publisher.Name(pbooks=>
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 };

复制代码
//左联结查询
var records from in SampleData.Publishers
                join in SampleData.books
                on p.Name equals b.Publisher.Name
                into newTables
                from in newTables.DefaultIfEmpty()
                select new pName p.NamebName == default(Book"没有图书" b.Title };
复制代码

9-2.右联结:要先满足哪个集合就把该集合写在左边就行了

10.串联

操作符:Concat ( )

将另一个序列与当前序列合并为一个序列

11.分区

操作符:Skip ( ) | SkipWhile ( ) | Take ( ) | TakeWhile ( )

Skip():跳过集合中指定个数的项,返回剩下的项。

SkipWhile():返回满足条件的项。

Take():从索引起始位置开始提取指定个数的项。

TakeWhile():提取满足条件的项

12.生成

操作符:DefaultIfEmpty ( ) 

DefaultIfEmpty() :用于当集合为空时为集合提供带有默认值的一个项

13.判断

操作符:All ( ) | Any ( ) | Contains ( )

All() :集合中所有项都满足条件吗?

Any():集合至少包含了一个项吗?

Contains():集合包含了参数指定的项吗?

14.比较

操作符:SequenceEqual ( )
逐一比较两个集合中的项是否相等,如果相等则两个集合被视为相等。

 

 

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);

复制代码
object[] objArray "sam"32"beijingStreet"false'a' };
var records objArray.Select(=> m.GetType().FullName).OrderBy(=> 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.Isbn.Contains("9")).Select(=> b.Title);
ObjectDumper.Write(records);

List<BookbList 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.Isbn.Contains("9")).Select(=> b.Title);
ObjectDumper.Write(records);

Dictionary<stringintbNary new Dictionary<stringint>
{
    "sam"12 },
    "corz"22 },
    "korn"53 },
};

var records bNary.Where(=> o.Value 20);
ObjectDumper.Write(records);
复制代码

查询非泛型集合

只需要使用Cast<T>将非泛型集合转换为泛型即可。 

//存进ArrayList的元素都是object弱类型
ArrayList list = new ArrayList()
{
new Book{ Title="寂静的春天"},
new Book{ Title="万有引力之虹"},
new Book{ Title="解体概要"}
};

var records = list.Cast<Book>().Select(b => new { bName= b.Title });

复制代码
//存进ArrayList的元素都是object弱类型
ArrayList list new ArrayList()
{
    new BookTitle="寂静的春天"},
    new BookTitle="万有引力之虹"},
    new BookTitle="解体概要"}
};

var records list.Cast<Book>().Select(=> new bNameb.Title });
复制代码

参数化查询

可在查询中使用变量,还可以动态构造查询,比如定义一个函数,函数以Func泛型委托做参数,通过条件测试传递不同的Lambda表达式。

<body>
<select name="combobox">
<option value="0">查询标题</option>
<option value="1">查询出版社</option>
</select>
<div>
@ViewData["show"]
</div>
</body>

复制代码
<body>
    <select name="combobox">
        <option value="0">查询标题</option>
        <option value="1">查询出版社</option>
    </select>
    <div> 
        @ViewData["show"]
    </div>
</body>
复制代码

[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);
}

复制代码
[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);
}
复制代码

LINQ查询是一系列的链式操作,所以完全可以根据条件动态增加查询,所以以下代码并不会发生覆盖而是追加。

[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();
}

复制代码
[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();
}
复制代码

 读取文件

一个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;
}
}
}

复制代码
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;
        }
    }
}
复制代码

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();
}

复制代码
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();
}
复制代码

 

LINQ To XML

XObject类
表示XML节点的抽象基类
AddAnnotation(object)
//添加注释节点。
Annotation(Type)
//获取第一个注释节点,类似行为的有Annotation<T>()、Annotations(Type)、Annotations<T>()。
AddAnnotation(object)
//添加注释节点。
Annotation(Type)
//获取第一个注释节点,类似行为的有Annotation<T>()、Annotations(Type)、Annotations<T>()。

XNode类

表示项、注释、 文档类型、处理指令、文本节点。节点可以是项节点、属性节点、文本节点。 

AddAfterSelf(object)
//紧跟在此节点之后添加指定的内容
AddBeforeSelf(object)
//紧跟在此节点之前添加指定的内容
Remove()
//从父节点总移除自身
Ancestors()
//获取当前节点的祖先元素节点集合
ElementsAfterSelf()
//获取当前节点后面的兄弟元素节点集合
ElementsBeforeSelf()
//获取当前节点的前面的兄弟元素节点集合
NodesAfterSelf()
//获取当前节点后面的兄弟元素节点集合
NodesBeforeSelf()
//获取当前节点前面的兄弟元素节点集合

复制代码
AddAfterSelf(object)
//紧跟在此节点之后添加指定的内容
AddBeforeSelf(object)
//紧跟在此节点之前添加指定的内容
Remove()
//从父节点总移除自身
Ancestors()
//获取当前节点的祖先元素节点集合
ElementsAfterSelf()
//获取当前节点后面的兄弟元素节点集合
ElementsBeforeSelf()
//获取当前节点的前面的兄弟元素节点集合
NodesAfterSelf()
//获取当前节点后面的兄弟元素节点集合
NodesBeforeSelf()
//获取当前节点前面的兄弟元素节点集合
复制代码

 XContainer类

表示可包含其他节点的节点。

Nodes()
//获取当前节点包含的所有子节点集合
Elements()
//获取当前节点包含的所有子元素节点集合
Elements(XName)
//获取当前节点包含的所有能匹配参数指定的XName的子元素节点集合
Element(XName)
//获取当前节点包含的能匹配参数指定的XName的第一个子元素节点
Descendants()
//获取当前节点包含的所有子代元素节点集合,即后代元素节点集合
DescendantNodes()
//获取当前节点包含的所有子代节点集合,即后代节点集合
Descendants(XName)
//获取当前节点包含的所有能匹配参数指定的XName的子代元素节点集合,即后代元素节点集合
Add(object)
//添加子节
AddFirst(object)
//将参数指定的节点添加为作为自身包含的第一个子节
RemoveNodes()
//删除自身的所有子节
ReplaceNodes(object)
//将自身包含的所有子节替换为参数指定的节点
ReplaceWith(object)
//将自身替换为参数指定的节点

复制代码
Nodes()
//获取当前节点包含的所有子节点集合
Elements()
//获取当前节点包含的所有子元素节点集合
Elements(XName)
//获取当前节点包含的所有能匹配参数指定的XName的子元素节点集合
Element(XName)
//获取当前节点包含的能匹配参数指定的XName的第一个子元素节点
Descendants()
//获取当前节点包含的所有子代元素节点集合,即后代元素节点集合
DescendantNodes()
//获取当前节点包含的所有子代节点集合,即后代节点集合
Descendants(XName)
//获取当前节点包含的所有能匹配参数指定的XName的子代元素节点集合,即后代元素节点集合
Add(object)
//添加子节
AddFirst(object)
//将参数指定的节点添加为作为自身包含的第一个子节
RemoveNodes()
//删除自身的所有子节
ReplaceNodes(object)
//将自身包含的所有子节替换为参数指定的节点
ReplaceWith(object)
//将自身替换为参数指定的节点
复制代码

XElement类

表示XML项节点。  

XElement(XNameString, String | XElement | XAttribute | XProcessingInstruction | XComment | IEnumerable )
复制代码
XElement(XNameString, String | XElement | XAttribute | XProcessingInstruction | XComment | IEnumerable )
//XElement的构造函数,用于创建XML元素节点,参数2如果是String则成为元素节点包含的文本节点,否则可同时创建出后代元素节点
XNameString:元素节点名称的字符表示或XName对象

//示例1:使用函数式链式操作创建XML元素节点
XElement books = new XElement("books",
    new XElement("book",
        new XElement("author", "寒食"),
        new XElement("author", "寒食")
    )
);
 
//示例2:调用函数创建XML元素节点
XElement book = new XElement("book");
book.Add(new XElement("author","寒食"));
book.Add(new XElement("author", "无垠"));
XElement books = new XElement("books");
books.Add(book);
 
//示例3:创建命名空间
XElement books = new XElement("{http://google.com/}books",
    new XElement("book",
        new XElement("author", "寒食"),
        new XElement("author", "寒食")
    )
);

//示例4:创建命名空间前缀
XElement books = new XElement("books",
        new XElement("book",
            new XAttribute(XNamespace.Xmlns + "1", ns),
            new XElement("author", "寒食"),
            new XElement("author", "寒食")
        )
);


Add(XElement)
//添加子元素节点
Save()
//保存XML文档
WriteTo(XmlWriter)
//将元素节点写入XmlWriter中
Attribute(XName)
//获取属性节点集合
Attribute(XName)
//根据指定的属性节点名获取属性节点值
AncestorsAndSelf()
//获取自身并包括其祖先元素节点集合
DescendantsAndSelf()
//获取自身并包括其后代元素节点集合
RemoveAll()
//移除所有后代
RemoveAttributes()
//移除自身的所有属性节点
RemoveAnnotations(Type)
//移除自身的所有注释节点
SetAttributeValue(XName, Object)
//根据指定的属性节点名设置属性节点值
SetElementValue(XName, Object)
//根据指定的属性节点名设置子元素节点的属性节点值

//==========静态方法==========
load(Path, LoadOptions)
//加载XML文档
//LoadOptions:可选的枚举,可能的值有:
//None:不保留无意义的空白
//PreserveWhitespace:保留无意义的空白
//SetBaseUri:保留URI信息
//SetLineInfo:保留请求的行信息
//示例:
try
{
    XElement x = XElement.Load(Server.MapPath("/books.xml"), LoadOptions.PreserveWhitespace); // 可从URL、磁盘路径加载
}


Parse()
//从XML字符串解析出XElement
//示例:
XElement x = XElement.Parse(@"               
    <book>
        <title>LINQ in Action</title>
        <author>Fabrice Marguerie</author>
        <author>Steve Eichert</author>
        <author>Jim Wooley</author>
        <publisher>Manning</publisher>
        <rating>4</rating>
    </book>    
");
复制代码
//XElement的构造函数,用于创建XML元素节点,参数2如果是String则成为元素节点包含的文本节点,否则可同时创建出后代元素节点
XNameString:元素节点名称的字符表示或XName对象
 
//示例1:使用函数式链式操作创建XML元素节点
XElement books = new XElement("books",
    new XElement("book",
        new XElement("author", "寒食"),
        new XElement("author", "寒食")
    )
);
 
//示例2:调用函数创建XML元素节点
XElement book = new XElement("book");
book.Add(new XElement("author","寒食"));
book.Add(new XElement("author", "无垠"));
XElement books = new XElement("books");
books.Add(book);
 
//示例3:创建命名空间
XElement books = new XElement("{http://google.com/}books",
    new XElement("book",
        new XElement("author", "寒食"),
        new XElement("author", "寒食")
    )
);
 
//示例4:创建命名空间前缀
XElement books = new XElement("books",
        new XElement("book",
            new XAttribute(XNamespace.Xmlns + "1", ns),
            new XElement("author", "寒食"),
            new XElement("author", "寒食")
        )
);
 
 
Add(XElement)
//添加子元素节点
Save()
//保存XML文档
WriteTo(XmlWriter)
//将元素节点写入XmlWriter中
Attribute(XName)
//获取属性节点集合
Attribute(XName)
//根据指定的属性节点名获取属性节点值
AncestorsAndSelf()
//获取自身并包括其祖先元素节点集合
DescendantsAndSelf()
//获取自身并包括其后代元素节点集合
RemoveAll()
//移除所有后代
RemoveAttributes()
//移除自身的所有属性节点
RemoveAnnotations(Type)
//移除自身的所有注释节点
SetAttributeValue(XName, Object)
//根据指定的属性节点名设置属性节点值
SetElementValue(XName, Object)
//根据指定的属性节点名设置子元素节点的属性节点值
 
//==========静态方法==========
load(Path, LoadOptions)
//加载XML文档
//LoadOptions:可选的枚举,可能的值有:
//None:不保留无意义的空白
//PreserveWhitespace:保留无意义的空白
//SetBaseUri:保留URI信息
//SetLineInfo:保留请求的行信息
//示例:
try
{
    XElement x = XElement.Load(Server.MapPath("/books.xml"), LoadOptions.PreserveWhitespace); // 可从URL、磁盘路径加载
}
 
 
Parse()
//从XML字符串解析出XElement
//示例:
XElement x = XElement.Parse(@"               
    <book>
        <title>LINQ in Action</title>
        <author>Fabrice Marguerie</author>
        <author>Steve Eichert</author>
        <author>Jim Wooley</author>
        <publisher>Manning</publisher>
        <rating>4</rating>
    </book>    
");
 

 XDocument类

表示XML文档,与XElement一样,它们都提供Load()、Parse()、Save()和WriteTo()方法,但此类可以包含更多信息,如包含一个根项的XElement、XML声明、XML文档类型和XML处理指令。如果使用函数式链式操作创建XML项,则使用XDocument与使用XElement创建XML项的语法格式是完全一样的。 

//XDocument比XElement多了以下内容,其它则是一样的
复制代码
//XDocument比XElement多了以下内容,其它则是一样的
XDocument doc = new XDocument("books",
    new XDeclaration("1.0", "utf-8", "yes"), // 表示XML文档版本、字符编码、是否是独立信息的节点
    new XProcessingInstruction("XML-sylesheet", "friendly-rss.xsl"),  // 表示XML文档处理指令的节点,此处为XML文档配置了一个样式表用以格式化XML文档以便在浏览器以友好的界面显示XML数据
    new XDocumentType("HTML", "-//w3c//DTD HTML 4.01//EN", "http://www.w3.org/TR/html4/strict/dtd", null) // 表示XML文档类型声明的节点           
    )
);
复制代码
XDocument doc = new XDocument("books",
    new XDeclaration("1.0""utf-8""yes"), // 表示XML文档版本、字符编码、是否是独立信息的节点
    new XProcessingInstruction("XML-sylesheet""friendly-rss.xsl"),  // 表示XML文档处理指令的节点,此处为XML文档配置了一个样式表用以格式化XML文档以便在浏览器以友好的界面显示XML数据
    new XDocumentType("HTML""-//w3c//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict/dtd"null// 表示XML文档类型声明的节点           
    )
);

 XAttribute类 

Remove()
复制代码
Remove()
//从父元素节点中移除自身

//==========属性==========
Parent
//获取父元素节点
复制代码
//从父元素节点中移除自身

//==========属性==========
Parent
//获取父元素节点

 XName

表示项的名字或项属性的名字

读取XML的简单示例  

<?xml version="1.0" encoding="utf-8" ?>
复制代码
<?xml version="1.0" encoding="utf-8" ?>
<category name="知识">
          <childcategory name="自然">
                    <books>
                              <book>寂静的春天</book>
                              <book>自然之力</book>
                    </books>
          </childcategory>
          <childcategory name="宗教">
                    <books>
                              <book>佛陀的证悟</book>
                    </books>
          </childcategory>
          <childcategory name="社会">
                    <books>
                              <book>智利天下写春秋</book>
                              <book>文明的解析</book>
                    </books>
          </childcategory>
</category>
复制代码
<category name="知识">
          <childcategory name="自然">
                    <books>
                              <book>寂静的春天</book>
                              <book>自然之力</book>
                    </books>
          </childcategory>
          <childcategory name="宗教">
                    <books>
                              <book>佛陀的证悟</book>
                    </books>
          </childcategory>
          <childcategory name="社会">
                    <books>
                              <book>智利天下写春秋</book>
                              <book>文明的解析</book>
                    </books>
          </childcategory>
</category>
public class DefaultController : Controller
复制代码
public class DefaultController : Controller
{
    public ActionResult Index()
    {
        XElement root = XElement.Load(Server.MapPath("~/books.xml"), LoadOptions.PreserveWhitespace);
        var childcategory = root.Element("childcategory"); // 获取第一个childcategory元素节点
        var childcategoryAttri = childcategory.Attribute("name"); // 获取第一个childcategory的name属性节点
        IEnumerable<XElement> books = childcategory.Element("books").Elements("book"); // 获取第一个childcategory所包含的第一个books元素节点包含的所有book元素节点
        ViewData["childcategory"] = childcategory;
        ViewData["childcategoryAttri"] = childcategoryAttri;
        ViewData["childs"] = string.Join("", books.ToList());
        // 输出XElement时会将它所包含的所有后代一并输出
        // 输出 IEnumerable<XElement>元素节点集合时必须ToList后与字符相连
        return View();
    }
}
复制代码
{
    public ActionResult Index()
    {
        XElement root = XElement.Load(Server.MapPath("~/books.xml"), LoadOptions.PreserveWhitespace);
        var childcategory = root.Element("childcategory"); // 获取第一个childcategory元素节点
        var childcategoryAttri = childcategory.Attribute("name"); // 获取第一个childcategory的name属性节点
        IEnumerable<XElement> books = childcategory.Element("books").Elements("book"); // 获取第一个childcategory所包含的第一个books元素节点包含的所有book元素节点
        ViewData["childcategory"] = childcategory;
        ViewData["childcategoryAttri"] = childcategoryAttri;
        ViewData["childs"] = string.Join("", books.ToList());
        // 输出XElement时会将它所包含的所有后代一并输出
        // 输出 IEnumerable<XElement>元素节点集合时必须ToList后与字符相连
        return View();
    }
}

 

// 获取每个book元素节点,从集合中过滤出其包含的文本为寂静的春天的元素节点
// (string)XElement可将元素节点转为其包含的文本 这样就可以执行一次判断 前提是该元素节点和包含的文本是在一行显示的
ViewData["show"] =string.Join("",root.Descendants("book").Where(book => (string)book == "寂静的春天"));
// 获取每个book元素节点,从集合中过滤出其包含的文本为寂静的春天的元素节点
// (string)XElement可将元素节点转为其包含的文本 这样就可以执行一次判断 前提是该元素节点和包含的文本是在一行显示的
ViewData["show"=string.Join("",root.Descendants("book").Where(book => (string)book == "寂静的春天"));

 

LINQ To Entity

当查询数据库表的结果集时,比如在调用Where方法返回的结果集上再调用其它Linq扩展方法会抛出异常,这可能是因为数据库结果集已经返回,应将其ToList后转化为内存中的集合对象,然后才能使用Linq to Object的方法进行进一步的过滤。否则链式的Linq方法会被当做是在数据库执行查询
MyEntity.DrugsErpEntities dbcontext = new MyEntity.DrugsErpEntities();
//如果Where后没有ToList,而是直接调用Select方法将会引发异常
var list = dbcontext.UserManagers.Where(user => user.LoginName == "a").ToList().Select((p, i) => new { u = p.LoginName, id = p.UserId, index = i }).ToList();
DbSet<TEntity> 类
此类实现了IQueryable<T>接口,一系列的扩展方法可用于Linq查询。
复制代码
Include()
//使用预加载策略建立左联结查询,模型必须具有导航属性,此方法根据导航属性来查询与主实体所关联的其它实体,此方法会立即开启查询
MusicStoreDB db = new MusicStoreDB();
var albums = db.Albums.Include(a => a.Artist).Include(a => a.Genre); //Artist和Genre都是Album的导航属性,不需要像Join方法那般指定主外键,只需要指定导航属性即可建立左联结查询

//批量删除记录
//示例:
public ContentResult DelBtch(string IDs) //IDs,需要被删除的记录的ID,如"1,15,32"
{
    try
    {
        var records = DBContext.TbRights.RemoveRange(DBContext.TbRights.Where(t => IDs.Contains(t.TbRightId.ToString())));
        DBContext.SaveChanges();
        DBContext.Dispose();
        return Content("{msg:'操作成功'}");
    }
    catch
    {
        return Content("{msg:'操作失败'}");
    }
}

AddRange()
//批量添加记录

AddOrUpdate(TEntity[] )
//System.Data.Entity.Migrations命名空间中为DbSet<TEntity>定义的扩展,自动识别插入或更新,当实体主键在数据库表中不存在时视为插入,当实体主键在数据库表中存在时视为更新。在参数数组包含的实体的主键在数据库表中既有存在的也有不存在的情况时,没关系,该更新该插入,此方法自动帮你完成。
//注意:如果某个字段为空,也将插入或更新到数据库,如果数据库设置了不允许空则引发异常
//示例:
var dirtyRecs = JsonConvert.DeserializeObject<List<TbRight>>(dirtyRecords); //包含了需要更新和需要插入的实体         
DBContext.TbRights.AddOrUpdate(dirtyRecs.ToArray() ); //同时执行了插入和更新
复制代码

插入数据前检测数据库是否已经具有一条完全相同的记录

#region 员工权限批量插入
/*
xTbUserRight:需要插入数据库的记录的Json表示
userIDs:以逗号分隔的员工ID的字符表示
checkValues:以逗号分隔的权限ID的字符表示
查询员工权限表时先对照userIDs和checkValues查出相同的记录,如果存在这样的记录则先删除它们
最后将xTbUserRight转化为的实体记录集合插入数据库
*/
public ContentResult AddTbUserRightBtch( string xTbUserRight , string xUserIDs , string xCheckValues )
{
    int [ ] userIDs = Array.ConvertAll ( xUserIDs.Split ( ',' ) , o => int.Parse ( o ) );
    int [ ] checkValues = Array.ConvertAll ( xCheckValues.Split ( ',' ) , o => int.Parse ( o ) );
    var newRecords = JsonConvert.DeserializeObject<List<TbUserRight>> ( xTbUserRight );
    var oldRecords = DBContext.TbUserRights.Where ( t => userIDs.Contains ( t.UserId ) && checkValues.Contains ( t.TbRightId ) );
    if ( oldRecords.Count ( ) != 0 )
    {
        DBContext.TbUserRights.RemoveRange ( oldRecords );
    }
    DBContext.TbUserRights.AddRange ( newRecords );
    DBContext.SaveChanges ( );
    return Content ( "{msg:'操作成功'}" );
}
#endregion
 

根据某个字段的数据集合查询出另一张表中对应的记录

复制代码
int UserId = int.Parse ( xUserId );
//UserAuthority是用户权限表实体,它存储三个字段,主键、用户ID和用户权限ID,一个用户具有多个权限ID,结构如下:
//PrimaryID      UserID      AuthorityID
//   1                   32             20
//   2                   32             26
//Authority是权限表实体,它存储两个字段,主键和权限名称,结构如下:
//AuthorityID   AuthorityName
//    32             ERP登录权限
//现在要查询出当前用户所具有的所有权限的记录
var userAuthorityID = DBContext.UserAuthority.Where ( t => t.UserID == UserId ).Select ( t => t.AuthorityID ).ToList ( ); //获取用户权限ID集合        
var userInclud = DBContext.Authority.Where ( t => userAuthorityID.Contains ( t.AuthorityID ) ); //获取用户具有的权限记录
var userNotInclud = DBContext.Authority.Where ( t => !userAuthorityID.Contains ( t.AuthorityID ) ); //获取用户不具有的权限记录
nt UserId = int.Parse ( xUserId );
//UserAuthority是用户权限表实体,它存储三个字段,主键、用户ID和用户权限ID,一个用户具有多个权限ID,结构如下:
//PrimaryID      UserID      AuthorityID
//   1                   32             20
//   2                   32             26
//Authority是权限表实体,它存储两个字段,主键和权限名称,结构如下:
//AuthorityID   AuthorityName
//    32             ERP登录权限
//现在要查询出当前用户所具有的所有权限的记录
var userAuthorityID = DBContext.UserAuthority.Where ( t => t.UserID == UserId ).Select ( t => t.AuthorityID ).ToList ( ); //获取用户权限ID集合        
var userInclud = DBContext.Authority.Where ( t => userAuthorityID.Contains ( t.AuthorityID ) ); //获取用户具有的权限记录
var userNotInclud = DBContext.Authority.Where ( t => !userAuthorityID.Contains ( t.AuthorityID ) ); //获取用户不具有的权限记录
https://i.cnblogs.com/EditPosts.aspx?postid=8481133
 







































































































































方法