)
Standard Query Operators(2) 好现在我们来一个一个的看
Select 就是一般的查询返回数据。 先定义一个数组
name[] disk = { new name{parent ="Micorsoft",child =new List<string>{ ".net","SQL Server"}}, new name{parent ="Sun",child =new List<string>{ "netBean","MySQL"}}, new name{parent ="IBM",child =new List<string>{ "eclipse","DB2"}} } ;
SelectMany 提供多个from的查询,也就说如果你要使用Query Syntax的方式实现一样的功能,就要使用多个from的情况,就是一对多的,对多个子集合的查询. 他们两的代码在SelectMany button中
// QuerySyntax SelectMany listBox1.Items.Add( " _____________QuerySyntax SelectMany____ " ); var query2 = from d in disk select d; foreach (var q2 in query2.SelectMany(d2 => d2.child)) { listBox1.Items.Add(q2); }
这个就是简单的查询child,下面两个是对应:一个是使用多个Select来实现对子集合的查询和用一个SelectMany实现 // MethodSyntax usge selectMany two listBox2.Items.Add( " ____________MethodSyntax usge selectMany two " ); var methodquery2 = disk.Select(d => d); foreach (var q in methodquery2) { var subQuery = q.child.Select(qs => qs); foreach (var sq in subQuery) { listBox2.Items.Add(sq); } } // usga SelectMany one IEnumerable < string > query1 = disk.AsQueryable().SelectMany(d => d.child); foreach (var q in query1) { listBox3.Items.Add(q); }
Sorting Data 其实排序在之前的一 中我们有使用过.OrderBy,OrderByDesending是第一级的排序,ThenByDescending,ThenBy是子级的排序.Reverse是倒置.
// MethodSyntax Reverse listBox2.Items.Add( " ____________MethodSyntax usge Reverse________ " ); var methodQuery3 = disk.AsQueryable().SelectMany(d => d.child).Reverse(); foreach (var q in methodQuery3) { listBox2.Items.Add(q); }
Joining Operators 结合或关联两个数据源对象的一些行为,他们的结合是通过一个共同的值或属性.而在LINQ中主要是通过key匹配.在面向对象编程时,就是对象之间的相关性,Join方法提供的Join和groupJoin.Join实现的是inner join,join比配的是另一个数据集或返回值.GroupJoin方法是扩展inner join和left outer join.一个left outer join是一个返回第一个数据源的所有元素. 图(1) 这一部分以后会更深入讨论. Join 就是在两个数据源的值比配的部分.就是会输出给A数据指定key与B数据的Key如果他们的key相比配的相应的数据.(就是inner join )
public static IEnumerable < TResult > Join < TOuter, TInner, TKey, TResult > ( this IEnumerable < TOuter > outer, IEnumerable < TInner > inner, Func < TOuter, TKey > outerKeySelector, Func < TInner, TKey > innerKeySelector, Func < TOuter, TInner, TResult > resultSelector)
下面是多表的内连查询 Table < Contact > contact = context.GetTable < Contact > (); Table < Employee > emp = context.GetTable < Employee > (); Table < Individual > ind = context.GetTable < Individual > (); Table < Customer > cus = context.GetTable < Customer > (); // use table Contact,Individual,Customer;key is ID var joinQuery2 = from c in contact join em in emp on c.ContactID equals em.ContactID join i in ind on c.ContactID equals (i.ContactID - 791 ) join cu in cus on i.CustomerID equals cu.CustomerID where c.FirstName.StartsWith( " S " ) orderby c.LastName select new { i.CustomerID,cu.AccountNumber,c.ContactID,c.LastName} ; // em.EmployeeID,c.FirstName,c.EmailAddress}; foreach (var j2 in joinQuery2) { listBox3.Items.Add(j2.LastName+"_______" + j2.CustomerID +"___"+ j2.AccountNumber); }
SQL是: SELECT [t0].[ContactID]FROM [Person].[Contact] AS [t0]INNER JOIN [HumanResources].[Employee] AS [t1] ON [t0].[ContactID] = [t1].[ContactID]INNER JOIN [Sales].[Individual] AS [t2] ON [t0].[ContactID] = [t2].[ContactID] - 791 INNER JOIN [Sales].[Customer] AS [t3] ON [t2].[CustomerID] = [t3].[CustomerID]WHERE [t0].[FirstName] LIKE 'S % ' ORDER BY [t0].[LastName]
下面他们是一对 ,基本的内连查询,使用两中方式QuerySyntax和MethodSyntax // Usage QuerySyntax join var joinQuery = from c in contact join es in emp on c.ContactID equals es.ContactID where c.FirstName.StartsWith( " S " ) orderby c.LastName select new { c.FirstName, c.LastName, c.MiddleName, es.Title } ; foreach (var j in joinQuery) { listBox1.Items.Add(j.FirstName); } // MethodSyntax join listBox2.Items.Add( " ______________MethodSyntax join_______ " ); var joinMethodQuery = contact.Join(emp, con => con.ContactID, em => em.ContactID, (con, em) => new { Contact = con.FirstName, myEmployee = em.ContactID } ); foreach (var q4 in joinMethodQuery) { listBox2.Items.Add(q4.Contact); }
GroupJoin: 就是第一个数据源中加入相应的第二个数据的值并将他们一起返回,主要是使用在有层次结构的数据,可以输出有层次的结果,保留他们次序.就是outer里面的元素与inner里面的匹配的元素的集合一起配对.(就是group join. ) 看GroupJoin方法 public static IEnumerable < TResult > GroupJoin < TOuter, TInner, TKey, TResult > ( this IEnumerable < TOuter > outer, IEnumerable < TInner > inner, Func < TOuter, TKey > outerKeySelector, Func < TInner, TKey > innerKeySelector, Func < TOuter, IEnumerable < TInner > , TResult > resultSelector)
我们主要来研究参数: 它是要被延迟执行的! outer 类型是IEnumerable(TOuter)是的第一数据源,其实这一个是不需要指定的,因为是this IEnumerable<TOuter> outer inner 类型是IEnumerable(Tinner)第二个数据源Join到第一个数据源,也就是第一个数据与第二个数据的inner outerKeySelector 类型为Func(TOuter,Tkey),从第一个数据中提取key. innerKeySelector 类型为Func(TInner,TKey),从第二个数据中提取key. resultSelector类型为System.Func(TOuter, IEnumerable(TInner), TResult) 创建一个由第个数据的一个元素和在第二个数据中匹配key的元素集合.是两个参数值组成的匿名方法. 返回类型是IEnumerable<TResult> 看代码 class Person { public string Name { get; set; } } class Pet { public string Name { get; set; } public Person Owner { get; set; } } class Son { public string Name { get; set; } public Pet Owner { get; set; } } // Groupjoin example public void GroupJoinEx1() { Person magnus = new Person { Name = "Hedlund, Magnus" }; Person terry = new Person { Name = "Adams, Terry" }; Person charlotte = new Person { Name = "Weiss, Charlotte" }; Pet barley = new Pet { Name = "Barley", Owner = terry}; Pet boots = new Pet { Name = "Boots", Owner = terry}; Pet whiskers = new Pet { Name = "Whiskers", Owner = charlotte}; Pet daisy = new Pet { Name = "Daisy", Owner = magnus}; Son Massif = new Son { Name = "Massif", Owner = barley }; Son Mass = new Son { Name = "Mass", Owner = boots }; Son Mono = new Son { Name = "Mono", Owner = whiskers }; Son Marie = new Son { Name = "Marie", Owner = daisy }; List<Person> people = new List<Person> { magnus, terry, charlotte }; List<Pet> pets = new List<Pet> { barley, boots, whiskers, daisy }; List<Son> Sons = new List<Son> { Massif, Mass, Mono, Marie }; var query = people.GroupJoin(pets, person => person, pet => pet.Owner, (person, petCollection) => new { OwnerName = person.Name, //Pets = petCollection Pets = petCollection.Select(pet => pet.Name) //Pets = petCollection.Select(pet => pet.Owner) }); foreach (var obj in query) { listBox2.Items.Add(obj.OwnerName); foreach (var pet in obj.Pets) listBox2.Items.Add("_______________"+pet); } var query2 = pets.GroupJoin(Sons, p=> p, s =>s.Owner, (myPet, sonCollection) => new { //他们是同一级别的 OwnerName = myPet.Owner.Name, //Pets = petCollection Pets = sonCollection.Select(so => so.Owner.Name), //Pets = petCollection.Select(pet => pet.Owner) }); foreach (var obj in query2) { listBox2.Items.Add(obj.OwnerName); foreach (var pet in obj.Pets) { listBox2.Items.Add("_______________" + pet); } } }
基本的内连查询,使用两中方式QuerySyntax和MethodSyntax. 定义三个List,而他们的List的关系是通过Owner来传递的,Son里面持有Pet的实例,而Pet的实现中又持有Peson的实例,在第一个中查询person名和pet名,在第二个中也是查询person名和pet名,但他们都是通过同一层的对象的子一级对象来来查找的. inner = {System.Collections.Generic.List<CSharpLanguage_C_app.LinqAPP.Pet>} innerKeySelector = {System.Func<CSharpLanguage_C_app.LinqAPP.Pet,CSharpLanguage_C_app.LinqAPP.Person>} outer = {System.Collections.Generic.List<CSharpLanguage_C_app.LinqAPP.Person>} outerKeySelector = {System.Func<CSharpLanguage_C_app.LinqAPP.Person,CSharpLanguage_C_app.LinqAPP.Person>} resultSelector = {System.Func<CSharpLanguage_C_app.LinqAPP.Person,System.Collections.Generic.IEnumerable<CSharpLanguage_C_app.LinqAPP.Pet>,<string,System.Collections.Generic.IEnumerable<string>> >} 首先你要看上面的图(1)整个过程就是给出outer,outer是Person,在给出inner是之前的Person与Pet才能找到inner,然后给出outer的key,inner的key,Func(TInner,TKey)是提取TInner中指定类型值,之后指定一个outer和一个层次结构数据的次级的数据. 再回到我们对表的操作: Table < Contact > contact = context.GetTable < Contact > (); Table < Employee > emp = context.GetTable < Employee > (); var groupJoinQuery = contact.GroupJoin(emp, con => con.ContactID, em => em.ContactID, (myCon, myEm) => new { newCon = myCon.ContactID, newEm = myEm.Select(lastEm => lastEm.ContactID) } ); foreach (var j3 in groupJoinQuery) { listBox3.Items.Add(j3.newCon); foreach (var order in j3.newEm) { listBox3.Items.Add(order); } }
GroupBy 就是按指定的值分组,以前 .
Table < Contact > contact = context.GetTable < Contact > (); // MethodSyntax Groupby var queryGroup = contact.Where(c => c.ContactID > 1500 && c.FirstName.StartsWith( " S " )).GroupBy(c => c.ContactID); listBox2.Items.Add( " MethodSyntax Groupby " ); foreach (var q in queryGroup) { listBox2.Items.Add(q.Key); foreach (var cust in q) { listBox2.Items.Add(""+cust.LastName); } } // QuerySyntax var queryGroup2 = (from c in contact where c.ContactID > 1500 && c.FirstName.StartsWith( " S " ) select c).GroupBy(c => c.ContactID); listBox2.Items.Add( " MethodSyntax Groupby " ); foreach (var q2 in queryGroup2) { listBox1.Items.Add(q2.Key); foreach (var cust2 in q2) { listBox1.Items.Add("" + cust2.LastName); } }
Concat 串联两个数据.
Concat串联两个数据. public static IEnumerable < TSource > Concat < TSource > ( this IEnumerable < TSource > first, IEnumerable < TSource > second)
Table < Contact > contact = context.GetTable < Contact > (); Table < Employee > emp = context.GetTable < Employee > (); // MethodSyntax var contactQuery = contact.Select(con => con.ContactID).Concat(emp.Select(em => em.EmployeeID)); foreach (var q in contactQuery) { listBox2.Items.Add("+"+q); } // QuerySyntax var contactQuery2 = (from con in contact select con.ContactID).Concat(from em in emp select em.EmployeeID); foreach (var q2 in contactQuery) { listBox1.Items.Add("+" + q2); }
Aggregating聚集两个数据.遍历集合中的元素,并通过一个指定函数与一个seed value(可迁移的值)对照,保留符合条件的. public static TSource Aggregate<TSource>( this IEnumerable<TSource> source, Func<TSource, TSource, TSource> func )
int [] randomNumbers = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15} ; var queryAgg = randomNumbers.Select(r => r).Aggregate( 0 , (total, next) => next + next >= 25 ? total + 1 : total); listBox1.Items.Add(queryAgg);
其实我们可以在方法里面处理一些逻辑后再返回一些相应的值。 还有Average,Count,Max,Min,Sum基本就和以前一样. Set OperatorsDistinct 是移出相同的值; Union 结合数据里面唯一的元素 Intersect 这个是交集, Except 相同数据之外的部分.
DefaultIfEmpty方法和Empty方法 Empty方法 返回一个空的集合 Empty(TResult)()方法缓存着一个TResult类型的数据.当对象返回时,它就已经没有元素. public static IEnumerable<TResult> Empty<TResult>() DefaultIfEmpty方法 如果你的集合是Empty就用指定的默认集合去代替它,为一个空集合提供一个默认元素. public static IEnumerable<TSource> DefaultIfEmpty<TSource>( this IEnumerable<TSource> source ) Range 创建一个集合来产生numbers数据. public static IEnumerable<int> Range( int start, int count ) Repeat 创建一个单独的值去替换替换自己的某部分. public static IEnumerable<TResult> Repeat<TResult>( TResult element, int count )
Conversion Operators AsEnumerable这里返回查询输出类型为IEnumerable(T),就说你能改变数据源类,全部该成IEnumerable(T)类型输出,(相应的泛型). 解决方法是指定 where 的客户端泛型 IEnumerable<T> 实现以替换泛型 IQueryable<T>。 public static IEnumerable<TSource> AsEnumerable<TSource>( this IEnumerable<TSource> source ) 其实这个是很常用的!
Table < Contact > contact = context.GetTable < Contact > (); IEnumerable < Contact > query = contact.AsEnumerable().Where(con => con.FirstName.Contains( " K " )); IEnumerable < Contact > query1 = contact.Where(con => con.FirstName.Contains( " K " )); foreach (Contact item in query) { listBox1.Items.Add(item.FirstName); }
上面一句 query = {System.Linq.Enumerable.WhereIterator<LINQSQO_WFormsApp.Form1.Contact>} 下面一句 Method = {System.Linq.IQueryable`1[LINQSQO_WFormsApp.Form1+Contact] Where[Contact](System.Linq.IQueryable`1[LINQSQO_WFormsApp.Form1+Contact], System.Linq.Expressions.Expression`1[System.Func`2[LINQSQO_WFormsApp.Form1+Contact,System.Boolean]])} 这里我不能很好的总结成一,两句话就说明白,所以给一个长一点的. 我们在没有实现IEnumerable<TSource>前我们调用在System.Query.Sequence 类中的方法如Where, Select, and SelectMany,但实现后它后我们就能调用公有Where方法. 看下面这个实现IEnumerable<TSource>的查询 IEnumerable<Contact> query = contact.AsEnumerable().Where(con => con.FirstName.Contains("K")); 和这个对应 [System.Linq.Expressions.MethodCallExpression] = {Table(Contact).Where(con => con.FirstName.Contains("K"))} 没有实现它的查询: IEnumerable<Contact> query1 = contact.Where(con=> con.FirstName.Contains("K")); query = {System.Linq.Enumerable.WhereIterator<LINQSQO_WFormsApp.Form1.Contact>} Method = {System.Linq.IQueryable`1[LINQSQO_WFormsApp.Form1+Contact] Where[Contact](System.Linq.IQueryable`1[LINQSQO_WFormsApp.Form1+Contact], System.Linq.Expressions.Expression`1[System.Func`2[LINQSQO_WFormsApp.Form1+Contact,System.Boolean]])} 我想你应该清楚他们的区别了. 还有LINQ一个个并行执行的结构,有本地对象集合是local queries ,而远程数据源查询是interpreted queries,所以你需要分清是那种查询,操作集合你要实现IEnumerable<T>.而且查询操作符号是Enumerable类里面.而remote execution的,操作符号则要实现IQueryable<T>. expression tree转换成为SQL使用的树时候是被remote execution,所以我们会调用带有"predicate argument"(谓词参数);但如果remote execution不能执行,我们就要调用本地执行的方法!其实我们SQL是要到服务器端去执行的,可以隐藏自己定义的方法,使用standard query operators.(在以后LINQ to SQL中我们还会谈到它!).好拉这一下"同拉""爽拉"! Cast和OfType在 中有很好的描述也有很好的事例.ToArray,ToList之前我们已经用熟悉的! 这里我们来使用 ToDictionary 可以将序列转化为字典,从IEnumerable(T)中创建一个Dictionary(TKey,TValue)按照指定的selector function. public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector ) 我们来实践一下:
Table < Contact > contact = context.GetTable < Contact > (); Dictionary < int , string > dict = contact.ToDictionary(con => con.ContactID, con => con.FirstName); foreach (KeyValuePair < int , string > item in dict) { listBox1.Items.Add(item.Key + " "+item.Value); }
ToLookup(TSource,Tkey) 一个一对多的字典,是一个key对Collections的值(明白来吧!),按照一个指定的Key从IEnumerable(T)中创建一个Lookup(Tkey,TElment). public static ILookup<TKey, TSource> ToLookup<TSource, TKey>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector )
ElementAt 返回一个指定index所对应的值.但在查询表达方式中是不支持的. ElementAtOrDefault 返回一个指定index所对应的值,如果这个值不存在,就返回默认值.查询表达方式中是不支持. 你可以在本地集合中使用它,这里就不说哦!
Last和First 分别是返回最后一个元素和第一个元素. FirstOrDefault 返回的是第一个元素,如果没有被找到,那就返回默认值. LastOrDefault 返回最后一个元素,如果没有,就返回默认值.查询表达方式中是不支持.
Table < Contact > contact = context.GetTable < Contact > (); var query = from c in contact where c.FirstName.StartsWith( " S " ) select c.FirstName; listBox1.Items.Add(query.First()); var query2 = from c in contact where c.FirstName.StartsWith( " ZZ " ) select c.FirstName; listBox1.Items.Add( " + " + query2.FirstOrDefault() + " + " );
Single 返回一个数据的一个单独的元素,就是这个集合中必须是一个元素.如果在数据中不是个元素那就会抛出异常!(感觉这个方法是用来判断集合里面是不只有元素样!)
Table < Contact > contact = context.GetTable < Contact > (); var query3 = from c in contact where c.LastName.StartsWith( " Kobylinski " ) select c.FirstName; try { listBox1.Items.Add(query3.Single()); } catch (System.InvalidOperationException) { listBox1.Items.Add("it's contain more than one in the sequence"); } var query = from c in contact where c.LastName.StartsWith( " Kobylinski " ) select c.FirstName; listBox1.Items.Add(query.SingleOrDefault());
Quantifier Operators All:集合中的值是否满足一个指定条件. Any: 返回值是一个boolean值,集合中任何值符合指定条件,就返回true;
Table < Contact > contact = context.GetTable < Contact > (); bool query2 = contact.All(c => c.FirstName.StartsWith( " K " )); listBox2.Items.Add(query2.ToString()); var query = from c in contact where c.LastName.StartsWith( " Z " ) select c.FirstName; listBox1.Items.Add(query.Any());
Contains 在集合中中是否包括指定的元素
Table < Contact > contact = context.GetTable < Contact > (); var query = from c in contact select c.LastName; listBox1.Items.Add(query.Contains( " Kleinerman " ));
Skip 跳过给定的数目的元素返回其余的的元素.其实就是可以输出剩余元素. SkipWhile:有指定条件的去跳过一些数据.上面的是没有逻辑控制的,而这一个我们给一些比较复杂的控制Take.
Table < Contact > contact = context.GetTable < Contact > (); listBox2.Items.Add( " ____________Skip__________ " ); var query1 = contact.Select(c => c.ContactID).Skip( 15000 ); foreach ( int q in query1) listBox2.Items.Add(q); listBox1.Items.Add( " ____________Skip__________ " ); int [] randomNumbers = { 86, 2, 77, 94, 100, 65, 5, 22, 70, 55, 81, 66, 45 } ; IEnumerable < int > skipLessThan50 = randomNumbers.OrderBy(num => num).SkipWhile(num => num < 50 ); foreach ( int number in skipLessThan50) listBox1.Items.Add( " __ " + number);
Take:是取回指定个数的元素. TakeWhile:是用其判断条件去依次判断数据中的元素取回符合条件的元素.
Table < Contact > contact = context.GetTable < Contact > (); listBox2.Items.Add( " ____________Take__________ " ); var query1 = contact.Select(c => c.ContactID).Take( 6 ); foreach ( int q in query1) { listBox2.Items.Add(q); } int [] randomNumberss = { 86, 2, 77, 94, 100, 65, 5, 22, 70, 55, 81, 66, 45 } ; listBox1.Items.Add( " ____________Take__________ " ); IEnumerable < int > query2 = randomNumberss.OrderBy(num => num).TakeWhile(num => num > 2 ); foreach ( int number in query2) listBox1.Items.Add( " __ " + number.ToString());
请多指教!
)