LINQ实战阅读笔记---第5章 超越基本的内存数据查询

5.1.1 查询非泛型集合

若你曾仔细阅读本书前几章的话,那么目前应该可以使用LINQ  to  Objects查询内存中的集合数据了。不过这里还有一个问题,即我们以为已经能够操作所有类型的集合,而实际上却只能查询部分的集合类型。原因在于,LINQ to Objects在设计时是为了配合实现了System.Collections.Generic.Ienumerable<T>接口的泛型集合类型使用的。虽然.NET Framework中的大多数集合类型,包括System.Collections.Generic.List<T>、数组、字典和队列等都实现了Ienumerable<T>接口,但问题在于Ienumerable<T>是个泛型类型的接口,但不是所有的集合类型都是泛型的。

.NET 2.0中第一次引入了泛型的概念,不过并不是所有人都使用了这个功能。不仅如此,即使你自己的代码中使用了泛型,仍有可能需要和那些不基于泛型的现有代码配合使用。比如,在引入泛型之前,.NET框架中最常用的集合类型就是System.Collections.ArrayList。

ArrayList并不是个泛型集合,也没有实现Ienumerable<T>接口,且其中的每一个元素都是弱类型的Object。这是不是就意味着LINQ无法操作于ArrayList之上了呢?我们可以用Cast操作符在非泛型集合和标准查询操作符之间搭起一座桥梁。

ArrayList books =GetArrayList();
 
var query = frombook in books.Cast<Book>()
wherebook.PageCount>150
select new {book.Title,book.Publisher.Name };
 
dataGridView.DataSource=query.ToList();
 

5.1.2 按照多个条件分组

按照图书的出版社和主题进行分组

var query = frombook in SampleData.Books

group book by new {book.Publisher,book.Subject };

在group by子句中使用 into 关键词

var query = frombook in SampleData.Books
group book by new {book.Publisher,book.Subject }
into grouping selectnew {
       publisher = grouping.Key.Publisher.Name,
       Subject = grouping.Key.Subject.Name,
       Books = grouping
};

每个分组中的元素的类型并不一定要和原序列中的元素类型一致。例如,我们可能只希望获取每本图书的标题,而不是整个的图书对象。如:通过出版社和主题将图书的标题进行分组:

var query = from bookin SampleData.Books
group book.Title bynew { book.Publisher,book.Subject }
into grouping selectnew {
       Publisher = grouping.Key.Publisher.Name,
       Subject = grouping.Key.Subject.Name,
       Titles = grouping
};

5.1.3 动态查询

自定义排序

       另一个常见的需求是按照用户的喜好对查询的结果进行排序,这时也需要使用动态查询。在查询语句中,排序条件可以通过orderby子句或是调用OrderBy操作符指定。下面的查询表达式就将结果中的图书按照其标题进行了排序:

from book inSampleData.Books
orderby book.Title
select book.Title;

用查询操作符也能够实现同样的功能:

SampleData.Books.Orderby(book=>book.Title)
.Select(book=>book.Title);

若想实现用户动态选择排序条件可以编写一个方法,让其接收一个排序选择器的委托作为参数。随后即可将这个参数传递给OrderBy操作符。OrderBy操作符的签名如下:

OrderedSequence<TElement>OrderBy<TElement,TKey>(
thisIEnumerable<TElement> source,Func<TElement,TKey> keySelector)

从上述代码中可以看出,OrderBy操作符接收的参数类型为Func<Telement,TKey>。在我们的示例中,源序列中元素的类型为Book,因此Telement即为Book类型。Key的类型则可以动态地在字符串(例如Title属性)或证书(例如PageCount属性)中选择。我们可以使用泛型方法来同时支持这两种类型的Key,其中Tkey为类型参数。

使用参数实现自定义排序的方法:

voidCustomSort<TKey>(Func<Book,TKey> selector)
{
       varbooks=SampleData.Books.OrderBy(selector);
       ObjectDumper.Write(books);
}

在查询表达式中使用参数实现自定义排序

void CustomSort<TKey>(Func<Book,TKey> selector)
{
       var books=from book in SampleData.Books
       orderby selector(book) select book;
       ObjectDumper.Write(books);
}

用如下语句即可调用该方法:

CustomSort(book=>book.Title);

或:

CustomSort(book=>book.Publisher.Name);

不过上述实现中并不支持降序排列。若想添加对降序的支持,我们可以修改现有方法‘

voidCustomSort<TKey>(Func<Book,TKey> selector,Boolean ascending)
{
       IEnumerable<Book> books =SampleData.Books;
       books = ascending?books.OrderBy(selector)
                                   :books.OrderByDescending(selector);
       ObjectDumper.Write(books);
}

这样,CustomSort方法只能够通过显式调用查询操作符实现。查询表达式中不能够包含对ascending参数的判断,因为其orderby子句必须是静态的。

这个额外的ascending参数允许我们在OrderBy和OrderByDescending两个操作符中进行选择。使用如下的语句即可让结果降序排列,而不是默认的升序:

CustomSort(book=>book.Title,false);

根据用户输入创建查询语句

var query =SampleData.Books
.Where(book=>book.PageCount>=(int)cbxPageCount.SelectedValue)
.Where(book=>book.Title.Contains(txtTitleFilter.Text))
 
if(cbxSortOrder.SelectedIndex==1)
       query =query.OrderBy(book=>book.Title);
elseif(cbxSortOrder.SelectedIndex==2)
       query =query.OrderBy(book=>book.Publisher.Name);
elseif(cbxSortOrder.SelectedIndex==3)
       query =query.OrderBy(book=>book.PageCount);
 
query =query.Select(book=>new{
       book.Title,
       book.PageCount,
       publisher=book.Publisher.Name});
 
dataGridView1.DataSource=query.ToList();

为了提高代码的可复用性和可读性,我们最好将这个查询语句移到专门的方法之中。

voidConditionalQuery<TSortKey>(int minPageCount,String titleFilter,
Func<Book,TSortKey>sortSelector)
{
       var query = SampleData.Books
       .Where(book=>book.PageCount>=minPageCount.Value)
       .Where(book=>book.Title.Contains(txtTitleFilter))
       .OrderBy(sortSelector)
       .Select(book=>new{
              book.Title,
              book.PageCount,
              Publisher=book.Publisher.Name});
 
       dataGridView1.DataSource=query.ToList();
}

这里我们使用了显示的操作符语法,而不是查询表达式,以便为接下来的呃改进做准备。上述方法可以通过如下方法调用。

int? minPageCount;
string titleFilter;
 
minPageCount=(int?)cbxPageCount.SelectedValue;
titleFilter =txtTitleFilter.Text;
if(cbxSortOrder2.SelectedIndex==1)
{
       ConditionalQuery(minPageCount,titleFilter,book=>book.Title);
}
elseif(cbxSortOrder2.SelectedIndex==2)
{
       ConditionalQuery(minPageCount,titleFilter,book=>book.Publisher.Name);
}
elseif(cbxSortOrder2.SelectedIndex==3)
{
       ConditionalQuery(minPageCount,titleFilter,book=>book.PageCount);
}
else
{
       ConditionalQuery(minPageCount,titleFilter,null);
}

看上去不错,不过我们的程序还没有结束---这个查询还不够灵活。实际上,代码中还存在着一个潜在的问题:若是用户没有提供限定条件,那么将不会得到正确的结果---程序还没有考虑到没有限定的情况。

       我们需要解决这个问题,并在添加条件之前判断是否真的有必要添加。若用户没有指定某个条件,程序则应该忽略与其相关的子句。

ConditionalQuery 方法的完整版本,已经考虑到了判断查询条件是否为空

voidConditionalQuery<TSortKey>(int? minPageCount,String titleFilter,
Func<Book,TSortKey>sortSelector)
{
       IEnumerable<Book> query;
 
       query = SampleData.Books;
       if(minPageCount.HasValue)
              query =query.Where(book=>book.PageCount>=minPageCount.Value);
       if(!String.IsNullOrEmpty(titleFilter))
              query =query.Where(book=>book.Title.Contains(titleFilter));
       if(sortSelector!=null)
              query =query.OrderBy(sortSelector);
      
       var completeQuery = query.Select(
       book=>new{
              book.Title,
              book.PageCount,
              Publisher=book.Publisher.Name});
 
       dataGridView1.DataSource=completeQuery.ToList();
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值