我有一个页面名称: CustomerList.aspx ,我在其中显示客户列表。
这也是我的表格和类文件:
public partial class Customer
{
public int CustomerID { get; set; }
public string FullName { get; set; }
public string EmailId { get; set; }
public int CustomerLocation { get; set; }
public bool IsActive { get; set; }
public bool Removed { get; set; }
public DateTime SubscribeDate { get; set; }
public Location _Location;
}
public partial class Location
{
public int LocationId { get; set; }
public string Name { get; set; }
}
Inactive = true:表示客户在系统中处于活动状态 Inactive = false:表示客户在系统中处于非活动状态。
Removed = true:表示客户已从系统中删除
Removed = false:表示客户未从系统中删除。
我将为用户提供3个过滤器来过滤客户记录。
1)位置下拉
<select>
<option Text="All" Value="0" selected="true">
<option Text="London" Value="1">
<option Text="America" Value="2">
</select>
2)状态下拉列表,其值为:全部,有效,无效:
<select>
<option Text="All" Value="0" selected="true">
<option Text="Active" Value="1">
<option Text="Inactive" Value="2">
</select>
3)统计数据下拉:
<select>
<option Text="All" Value="all" selected="true">
<option Text="Active Customers" Value="all">
<option Text="Recent subscribe customers" Value="subscribe">
<option Text="Recent unsubscribe customers" Value="unsubscribe">
</select>
当我的页面加载时,我想在我的网格中显示客户列表。
这是我的代码:
public void DisplayCustomersList()
{
DataTable list=GetCustomers(Convert.ToInt16(ddlLocation.SelectedValue),Convert.ToInt16(ddlStatus.SelectedValue),ddlstats.SelectedValue);
Grid1.DataSource = list;
Grid1.DataBind();
}
public DataTable GetCustomers(int LocationId, int ActiveId, string stats)
{
using (var context = new MyContext())
{
var data = from c in context.Customers
where c.Removed == false
select new
{
FullName = c.FullName,
c.CustomerID,
c._Location.Name,
c.IsActive,
c.SubscribeDate,
c.Removed
};
if (LocationId != 0 && ActiveId != 0)
{
if (ActiveId == 1)
{
return
MyContext.CopyToDataTable(
data.Where(x => x.LocationId == LocationId && x.IsActive == true && x.Removed == false));
}
else if(ActiveId==2)
{
return
MyContext.CopyToDataTable(
data.Where(x => x.LocationId == LocationId && x.IsActive == false && x.Removed == false));
}
return
MyContext.CopyToDataTable(
data.Where(x => x.LocationId == LocationId && x.Removed==false));
}
if (LocationId != 0 && stats != "")
{
if (stats == "all")
{
return
MyContext.CopyToDataTable(
data.Where(x => x.LocationId == LocationId && x.IsActive == true && x.Removed == false));
}
else if (stats == "subscribe")
{
return
MyContext.CopyToDataTable(
data.Where(x => x.SubscribeDate >= DateTime.Now.AddDays(-7) && x.Removed == false && x.LocationId==LocationId));
}
}
if (ActiveId != 0 && stats != "")
{
if(ActiveId==1)
{
if(stats=="all")
{
return
MyContext.CopyToDataTable(
data.Where(x => (x.SubscribeDate >= DateTime.Now.AddDays(-7) || x.IsActive == true) || (x.Removed == false) || (x.SubscribeDate >= DateTime.Now.AddDays(-7) || x.IsActive == false)));
}
else if (stats == "subscribe")
{
return
MyContext.CopyToDataTable(
data.Where(x => x.SubscribeDate >= DateTime.Now.AddDays(-7) && x.Removed == false && x.IsActive == true));
}
else if (stats == "unsubscribe")
{
return
MyContext.CopyToDataTable(
data.Where(x => x.SubscribeDate >= DateTime.Now.AddDays(-7) && x.Removed == false && x.IsActive == false));
}
}
else if(ActiveId==2)
{
if (stats == "all")
{
MyContext.CopyToDataTable(
data.Where(x => (x.SubscribeDate >= DateTime.Now.AddDays(-7) || x.IsActive == false) && (x.Removed == false)));
}
else if (stats == "subscribe")
{
return
MyContext.CopyToDataTable(
data.Where(x => x.SubscribeDate >= DateTime.Now.AddDays(-7) && x.Removed == false && x.IsActive == false));
}
else if (stats == "unsubscribe")
{
return
MyContext.CopyToDataTable(
data.Where(x => x.SubscribeDate >= DateTime.Now.AddDays(-7) && x.Removed == false && x.IsActive == false));
}
}
}
if (stats != "")
{
if (stats == "all")
{
return
MyContext.CopyToDataTable(
data.Where(x => x.IsActive == true && x.Removed == false));
}
else if (stats == "subscribe")
{
return
MyContext.CopyToDataTable(
data.Where(x => x.SubscribeDate >= DateTime.Now.AddDays(-7) && x.Removed == false && x.IsActive==true));
}
else
{
return
MyContext.CopyToDataTable(
data.Where(x => x.SubscribeDate >= DateTime.Now.AddDays(-7) && x.IsActive == false && x.Removed == false));
}
}
}
}
在所有3 dropdown selected index change event
我只是这样调用此函数:DisplayCustomersList()
所以我只想问你这是一种执行过滤器的正确方法,或者这种代码可以更好地进行优化。
任何人都可以为我提供更好的解决方案,或者如果可能的话更好地优化这些代码???
答案 0 :(得分:3)
可以在没有PredicateBuilder
的情况下优化此问题,但需要仔细和“系统”分析。
考虑一下你的情况,将所有14个一起放在一起,你实际上可以看到你只有三个决定因素,即:LocationId
,ActiveId
和stats
:
No LocationId ActiveId stats result
1 not 0 1 don't care data.Where(x => x.LocationId == LocationId && x.IsActive == true && x.Removed == false)
2 not 0 2 don't care data.Where(x => x.LocationId == LocationId && x.IsActive == false && x.Removed == false));
3 not 0 not 0-2 don't care data.Where(x => x.LocationId == LocationId && x.Removed == false));
4 not 0 don't care all data.Where(x => x.LocationId == LocationId && x.IsActive == true && x.Removed == false)
5 not 0 don't care subscribe data.Where(x => x.SubscribeDate >= DateTime.Now.AddDays(-7) && x.Removed == false && x.LocationId == LocationId));
6 don't care 1 all data.Where(x => (x.SubscribeDate >= DateTime.Now.AddDays(-7) || x.IsActive == true) || (x.Removed == false) || (x.SubscribeDate >= DateTime.Now.AddDays(-7) || x.IsActive == false)));
7 don't care 1 subscribe data.Where(x => x.SubscribeDate >= DateTime.Now.AddDays(-7) && x.Removed == false && x.IsActive == true));
8 don't care 1 unsubscribe data.Where(x => x.SubscribeDate >= DateTime.Now.AddDays(-7) && x.Removed == false && x.IsActive == false));
9 don't care 2 all data.Where(x => (x.SubscribeDate >= DateTime.Now.AddDays(-7) || x.IsActive == false) && (x.Removed == false)));
10 don't care 2 subscribe data.Where(x => x.SubscribeDate >= DateTime.Now.AddDays(-7) && x.Removed == false && x.IsActive == false));
11 don't care 2 unsubscribe data.Where(x => x.SubscribeDate >= DateTime.Now.AddDays(-7) && x.Removed == false && x.IsActive == false));
12 don't care don't care all data.Where(x => x.IsActive == true && x.Removed == false));
13 don't care don't care subscribe data.Where(x => x.SubscribeDate >= DateTime.Now.AddDays(-7) && x.Removed == false && x.IsActive == true));
14 don't care don't care unsubscribe data.Where(x => x.SubscribeDate >= DateTime.Now.AddDays(-7) && x.Removed == false && x.IsActive == false));
我观察到你的结果是非常确定的,只有很少的例外。除了结果no 6
和9
之外,您的查询谓词实际上可以分为四个基本组件(6
和9
省略)。他们是:
comp1: x.LocationId == LocationId
comp2: x.IsRemoved == false
comp3: x.IsActive == true
comp4: x.SubscribeDate >= DateTime.Now.AddDays(-7)
查询逻辑很简单:
comp1 && comp2 && comp3 && comp4
将它们与12个案例(6
和9
除外)放在一起,你会得到:
Simplification:
DC = don't care
A = applied
NA = not applied
QueryComponents
No LocationId ActiveId stats comp1 comp2 comp3 comp4
1 not 0 1 DC A A Yes NA
2 not 0 2 DC A A No NA
3 not 0 not 0-2 DC A A NA NA
4 not 0 DC all A A Yes NA
5 not 0 DC subscribe A A NA A
7 DC 1 subscribe NA A Yes A
8 DC 1 unsubscribe NA A No A
10 DC 2 subscribe NA A No A
11 DC 2 unsubscribe NA A No A
12 DC DC all NA A Yes A
13 DC DC subscribe NA A Yes A
14 DC DC unsubscribe NA A No A
现在,可以看到查询组件可以与决定因素一起映射回来:
comp1: Applied only when LocationId is not 0
comp2: Always applied //this is very good!
comp3: Yes = 1, 4, 7, 12, 13; NA = 3, 5; No = 2, 8, 10, 11, 14
comp4: Not Applied when LocationId is 0 except on case 5
因此,我们可以帮助标志(其中有4个)来确定是否应该包含查询组件,如下所示:
bool LocationIdNotApplied = LocationId == 0; //for comp1
bool IsActiveNotApplied = LocationId != 0 && (ActiveId < 0 || ActiveId > 2 || stats = "subscribe"); //for comp3 to be applied or not
bool IsActiveFalse = (LocationId != 0 && ActiveId == 2) || stats == "unsubscribe" || (ActiveId == 2 && stats == "subscribe"); //for comp3 to be false
bool DateApplied = LocationId == 0 || (LocationId != 0 && stats == "subscribe");
然后,除data.Where
和6
之外的所有案例9
都可以这样简化:
data.Where(x => (x.LocationId == LocationId || LocationIdNotApplied) //comp1
&& x.IsRemoved == false //comp2
&& ((x.IsActive == !IsActiveFalse) || IsActiveNotApplied) //comp3
&& (x.SubscribeDate >= DateTime.Now.AddDays(-7) || !DateApplied)) //comp4
对于12例成为1例,这是一个显着的简化,你只需要添加另外两个案例,总共3个案例,而不是原来的14个案例!
public DataTable GetCustomers(int LocationId, int ActiveId, string stats)
{
using (var context = new MyContext())
{
var data = from c in context.Customers
where c.Removed == false
select new
{
FullName = c.FullName,
c.CustomerID,
c._Location.Name,
c.IsActive,
c.SubscribeDate,
c.Removed
};
bool LocationIdNotApplied = LocationId == 0; //for comp1
bool IsActiveNotApplied = LocationId != 0 && (ActiveId < 0 || ActiveId > 2 || stats = "subscribe"); //for comp3 to be applied or not
bool IsActiveFalse = (LocationId != 0 && ActiveId == 2) || stats == "unsubscribe" || (ActiveId == 2 && stats == "subscribe"); //for comp3 to be false
bool DateApplied = LocationId == 0 || (LocationId != 0 && stats == "subscribe");
if(LocationId == 0 && ActiveId == 1 && stats == "all"){ //case 6
return MyContext.CopyToDataTable(
data.Where(x => (x.SubscribeDate >= DateTime.Now.AddDays(-7) || x.IsActive == true) || (x.Removed == false) || (x.SubscribeDate >= DateTime.Now.AddDays(-7) || x.IsActive == false)));
} else if (LocationId == 0 && ActiveId == 2 && stats == "all"){ //case 9
return MyContext.CopyToDataTable(
data.Where(x => (x.SubscribeDate >= DateTime.Now.AddDays(-7) || x.IsActive == false) && (x.Removed == false)));
} else { //other cases
return MyContext.CopyToDataTable(
data.Where(x => (x.LocationId == LocationId || LocationIdNotApplied) //comp1
&& x.IsRemoved == false //comp2
&& ((x.IsActive == !IsActiveFalse) || IsActiveNotApplied) //comp3
&& (x.SubscribeDate >= DateTime.Now.AddDays(-7) || !DateApplied))) //comp4
}
}
}
你的案子6实际上对我来说很奇怪:
data.Where(x => (x.SubscribeDate >= DateTime.Now.AddDays(-7) || x.IsActive == true) || (x.Removed == false) || (x.SubscribeDate >= DateTime.Now.AddDays(-7) || x.IsActive == false)));
请注意x.IsActive == true
同时拥有x.IsActive == false
和x.SubscribeDate >= DateTime.Now.AddDays(-7)
。然后将其与||
结合使用。这就像说:
(A || true) || (A || false)
无论如何,总是return true
。您可能想再次检查,甚至可以进一步简化/
因此,在没有PredicateBuilder
的情况下解决这个问题 - 它需要仔细和“系统地”(或者,我实际上是指逐步)分析所有可能的案例。
我必须为OP道歉,因为我无法完全测试我提出的缺乏完整测试资源的代码(与OP不同)。
但是如果OP发现我错过了处理或的情况,OP在原始问题中没有提出,至少,我上面的解决方案中提出的步骤应该是仍然有用让OP对自己的实际案例进行自己的仔细分析。
答案 1 :(得分:2)
您可以在GetCustomer方法中优化搜索条件。请看看Linqkit
此处为基本示例
http://www.albahari.com/nutshell/predicatebuilder.aspx https://www.nuget.org/packages/LinqKit/
使用PredicateBuilder
以下是使用PredicateBuilder解决上述示例的方法:
IQueryable<Product> SearchProducts (params string[] keywords)
{
var predicate = PredicateBuilder.False<Product>();
foreach (string keyword in keywords)
{
string temp = keyword;
predicate = predicate.Or (p => p.Description.Contains (temp));
}
return dataContext.Products.Where (predicate);
}
答案 2 :(得分:1)
首先,我不再使用DataTable
了,而是创建一个像这样的ViewModel:
public class CustomerVm
{
public string FullName { get; set; }
public int CustomerID { get; set; }
public string LocationName { get; set; }
public bool IsActive { get; set; }
public DateTime SubscribeDate { get; set; }
}
然后创建一个基本查询来抓住客户:
var data = from c in context.Customers
where c.Removed == false;
根据您的下拉值操纵查询的 where 条件:
if(ActiveId == 1)
{
data = data.Where(c => c.IsActive);
}
else if(ActiveId == 1)
{
data = data.Where(c => c.IsActive);
}
if (LocationId != 0)
{
data = data.Where(c => c.LocationId == LocationId);
}
if (stats == "subscribe")
{
data = data.Where(c => c.IsActive
&& c.SubscribeDate >= DateTime.Now.AddDays(-7));
}
else if (stats == "unbsubscribe")
{
data = data.Where(c => !c.IsActive
&& c.SubscribeDate >= DateTime.Now.AddDays(-7));
}
最后,执行查询并将结果作为ViewModels返回:
return data.Select(c => new CustomerVm {
FullName = c.FullName,
CustomerId = ...
});
答案 3 :(得分:1)
我发现了一种更具可扩展性的不同方法。性能方面真正的重大改进是在纯SQL中实际执行此过滤,并仅将匹配数据移动到应用程序逻辑。
我能够通过以下方式使这种查询速度提高100倍(使用Oracle):
有关https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:1669972300346534908
基础知识的更多信息数据库是为了在数以万计的记录上进行数据访问,过滤和数据加入非常好而构建的。我发现利用它可以提供超过任何ORM工具的竞争优势,这种工具具有严重的局限性(放弃数据库端处理的整个功能,你放弃所有高级SQL例如分区,你无法访问Oracle文本等技术,因为你无法控制查询放松。)