MVC对集合筛选,不使用Where(),而使用FindAll()

当想对集合筛选的时候,经常想到用Where过滤,而实际上List<T>.FindAll()也是不错的选择。

如果有一个订单,属性有下单时间、区域等等。如何使用List<T>.FindAll(),根据年份、月份、天、区域来过滤呢?

□ 思路

List<T>.FindAll(Predicate<T> match)方法
Predicate<T>是一个泛型委托,返回bool类型:
public delegate bool Predicate<T>(T obj)
也就是说,match参数是一个返回bool类型的方法,由此,我们可以定义一个泛型类,并在其中自定义返回bool类型的方法,即筛选规则,最后把该方法交给委托。

最终视图界面输入年份、月份、天与订单类中的下单时间属性进行比较。首先想到把年份、月份、天封装到一个类中与订单类进行比较。考虑到有可能存在其它类也有时间属性,都有可能进行有关时间的筛选。这样,可以把时间属性抽象成接口,凡是实现该接口的,都可以与封装年份、月份、天的类进行比较。

using System;
 
namespace ObjectFilter.Interface
{
    public interface IDate
    {
        DateTime Date { get; set; } 
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

让Order类实现IDate接口。

using System;
using ObjectFilter.Interface;
 
namespace ObjectFilter.Models
{
    public class Order : IDate
    {
        public int ID { get; set; }
        public DateTime Date { get; set; } //接口属性
        public string Area { get; set; }
    }
}
 

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

封装年份、月份、天的类需要面向IDate接口,并提供一个返回bool类型的比较方法。

    public class DateFilter<T> where T:IDate
    {
        private int year;
        private int month;
        private int day;
 
        public DateFilter(int year, int month, int day)
        {
            this.year = year;
            this.month = month;
            this.day = day;
        }
 
        public DateFilter(DateTime date) : this(date.Year, date.Month, date.Day){}
        public DateFilter(int year, int month) : this(year, month, 0){}
        public DateFilter(int year) : this(year, 0, 0){}
        public DateFilter() : this(0, 0, 0){}
 
        //自定义规则:只要有T的Date属性值与本类的字段有一个不等,就返回false
        public virtual bool MatchRule(T item)
        {
            if(year != 0 && year !=item.Date.Year)
                return false;
            if(month != 0 && month != item.Date.Month)
                return false;
            if(day !=0 && day != item.Date.Day)
                return false;
            return true;
        }
    }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

如何针对Order中的Area属性筛选呢?可以设计一个DateFilter<Order>的继承类,使子类不但有父类的筛选逻辑,并在此基础上增加一个对Area属性的筛选。

   1:      public class OrderFilter : DateFilter<Order>
   2:      {
   3:          private string area;
   4:   
   5:          public OrderFilter(int year, int month, int day, string area)
   6:              : base(year, month, day)
   7:          {
   8:              this.area = area;
   9:          }
  10:   
  11:          public override bool MatchRule(Order item)
  12:          {
  13:              bool result = base.MatchRule(item);
  14:              if (result == false)
  15:                  return false;
  16:              if (string.IsNullOrEmpty(area) || String.Compare(item.Area, area, true) == 0)
  17:              {
  18:                  return true;
  19:              }
  20:              else
  21:              {
  22:                  return false;
  23:              }
  24:          }
  25:      }
  26:   

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

模拟一个服务层,一个方法用来获取所有订单信息,另一个方法根据条件筛选出订单集合,只需要把自定义泛型类DateFilter<Order>的MatchRule交给Predicate<T>泛型委托就行。

using System;
using System.Collections.Generic;
using ObjectFilter.Helper;
using ObjectFilter.Models;
 
namespace ObjectFilter
{
    public static class Service
    {
        public static List<Order> GetOrders()
        {
            return new List<Order>()
            {
                new Order(){ID = 1,Area = "青岛",Date = new DateTime(2013,8,1)},
                new Order(){ID = 2,Area = "平度",Date = new DateTime(2013,9,1)},
                new Order(){ID = 3,Area = "即墨",Date = new DateTime(2013,9,3)},
                new Order(){ID = 4,Area = "香港",Date = new DateTime(2013,9,3)},
                new Order(){ID = 5,Area = "北京",Date = new DateTime(2014,1,2)},
                new Order(){ID = 6,Area = "上海",Date = new DateTime(2014,1,2)},
            };
        }
 
        public static List<Order> GetOrdersBy(DateFilter<Order> filter)
        {
            return GetOrders().FindAll(new Predicate<Order>(filter.MatchRule));
        }
    }
}
 

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

控制器有2个方法,一个方法返回所有的订单,另一个方法接收来自前台的筛选参数,根据这些参数生成自定义泛型类DateFilter<Order>实例作为服务层方法参数,最终返回部分视图。

using ObjectFilter.Helper;
using ObjectFilter.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
 
namespace ObjectFilter.Controllers
{
    public class HomeController : Controller
    {
 
        public ActionResult Index()
        {
            List<SelectListItem> years = new List<SelectListItem>();
            years.Add(new SelectListItem(){Text = "2013",Value = "2013"});
            years.Add(new SelectListItem() { Text = "2014", Value = "2014" });
            ViewData["y"] = years;
 
            List<SelectListItem> months = new List<SelectListItem>();
            for (int i = 1; i <= 12; i++)
            {
                months.Add(new SelectListItem(){Text =i.ToString(),Value = i.ToString()});
            }
            ViewData["m"] = months;
 
            List<SelectListItem> days = new List<SelectListItem>();
            for (int i = 1; i <= 31; i++)
            {
                days.Add(new SelectListItem(){Text = i.ToString(),Value = i.ToString()});
            }
            ViewData["d"] = days;
 
            return View(Service.GetOrders());
        }
 
        [HttpPost]
        public ActionResult GetOrdersBy(string y, string m, string d, string area)
        {
            DateFilter<Order> filter = new OrderFilter(Convert.ToInt32(y), Convert.ToInt32(m), Convert.ToInt32(d), area);
            return View(Service.GetOrdersBy(filter));
        }
 
    }
}
 

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, "Courier New", courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

呈现所有订单的视图界面中,当点击"筛选"按钮,把筛选条件通过ajax传递给控制器方法,返回的部分视图最终呈现到本页的一个div中。

展开@model IEnumerable<ObjectFilter.Models.Order>

@{
ViewBag.Title = "Index";
Layout = "~/Views/Shared/_Layout.cshtml";
} @Html.DropDownList("year",(IEnumerable<SelectListItem>)ViewData["y"],"选择年份",new {id="year"}) <br/>
@Html.DropDownList("month",(IEnumerable<SelectListItem>)ViewData["m"],"选择月份",new {id="month"}) <br/>
@Html.DropDownList("day",(IEnumerable<SelectListItem>)ViewData["d"],"选择天数",new {id="day"}) <br/>
<input type="text" id="area"/> <br/>
<input type="button" id="btn" value="筛选"/>
<div id="content">
<table>
<tr>
<th>
@Html.DisplayNameFor(model => model.Date)
</th>
<th>
@Html.DisplayNameFor(model => model.Area)
</th>
<th></th>
</tr> @foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Date)
</td>
<td>
@Html.DisplayFor(modelItem => item.Area)
</td>
<td>
</td>
</tr>
} </table>
</div>
<hr/>
<h3>以下为筛选结果:</h3>
<div id="filter"></div> @section scripts
{
<script type="text/javascript">
$(function() {
$('#btn').on('click', function() {
$.ajax({
cache: false,
url: '@Url.Action("GetOrdersBy", "Home")',
type: "POST",
data: { y: $('#year').val(), m: $('#month').val(), d: $('#day').val(), area: $('#area').val() },
success: function (data) {
$('#filter').html(data);
},
error: function (jqXhr, textStatus, errorThrown) {
alert("出错了 '" + jqXhr.status + "' (状态: '" + textStatus + "', 错误为: '" + errorThrown + "')");
}
});
});
});
</script>
} 点击"筛选"按钮返回部分视图为: @model IEnumerable<ObjectFilter.Models.Order> <table>
<tr>
<th>
@Html.DisplayNameFor(model => model.Date)
</th>
<th>
@Html.DisplayNameFor(model => model.Area)
</th>
<th></th>
</tr> @foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Date)
</td>
<td>
@Html.DisplayFor(modelItem => item.Area)
</td>
<td>
</td>
</tr>
}
</table>

结果:

输入时间筛选:

MVC对集合筛选,不使用Where(),而使用FindAll()  MVC对集合筛选,不使用Where(),而使用FindAll()

输入时间和区域筛选:

MVC对集合筛选,不使用Where(),而使用FindAll()  MVC对集合筛选,不使用Where(),而使用FindAll()

□ 总结

先把目标对象中需要筛选的属性抽象成接口属性,然后设计一个能接收筛选条件的泛型类,并提供一个返回bool类型的方法,该方法把接收到的来自客户端的筛选条件与目标对象的属性进行比较。最后,只需要把自定义泛型类返回bool类型的方法交给Predicate<T>这个泛型委托即可:FindAll(new Predicate<Order>(filter.MatchRule))。

上一篇:CADisplayLink以及和NSTimer的区别


下一篇:poj-1056-IMMEDIATE DECODABILITY(字典)