我在我正在构建的ASP.NET网站上有一个功能,允许用户在特定事件中注册多个类别。网站管理员需要能够添加这些类别,并添加规则来管理哪些类别组别可以获得折扣。
我的解决方案是创建一个名为CategorySelectionRule的头表,如下所示:
CREATE TABLE CategorySelectionRules (
RuleId INTEGER IDENTITY PRIMARY KEY,
RuleDescription VARCHAR(100),
RuleMessage VARCHAR(255),
Discount DECIMAL
)
...还有一个名为CategorySelectionRuleDetail的详细信息表,如下所示:
CREATE TABLE CategorySelectionRuleDetail (
RuleCategoryId INTEGER IDENTITY PRIMARY KEY,
RuleId INTEGER REFERENCES CategorySelectionRules(RuleId),
CategoryId INTEGER REFERENCES EntryCategory(CategoryId)
}
因此,如果有人输入类别1和类别4,则代码会检查包含这些类别的规则(并且仅检查这些类别),然后返回适用的折扣和消息。我的问题是代码失控,因为我需要在规则中允许任意数量的类别,但是如果没有将详细信息表连接回自身的选择案例,我无法弄清楚如何做到这一点,像这样,它只适用于有两个类别的规则(哦,它实际上并不起作用):
categoryRuleId = (From r1 In _db.CategorySelectionRuleDetail _
Join r2In _db.CategorySelectionRuleDetail _
On r1.RuleId Equals r2.RuleId _
Where ((r1.CategoryId = matchCategory1 _
AndAlso r2.CategoryId = matchCategory2) _
OrElse (r1.CategoryId = matchCategory2 _
AndAlso r2.CategoryId = matchCategory1)) _
AndAlso !(From r3 In _db.CategorySelectionRuleDetail _
Group r3 By r3.RuleId Into g_
Where g.Count(RuleId) > 2).Contains(r1.RuleId) _
Select r1.RuleId).FirstOrDefault()
有没有更好的方法来做到这一点,我没有看到?我觉得数据结构是正确的,但我确信有一种更简单的方法来匹配我想要的数据,而不需要为每个类别分别使用单独的LINQ语句。
答案 0 :(得分:0)
这个Linq声明会做你想要的:
var rules = from r in db.CategorySelectionRules
where r.CategorySelectionRuleDetails.Count() == selectedCategories.Count &&
r.CategorySelectionRuleDetails.Where(cr => selectedCategories.Contains(cr.CategoryId.Value)).Count() == selectedCategories.Count
select r;
如果有两个不同的规则具有相同的类别要求,这也会传回多个规则。
完整计划
SQL用于生成数据库结构
CREATE TABLE EntryCategory (
CategoryId INTEGER IDENTITY PRIMARY KEY,
CategoryDescription VARCHAR(100)
)
CREATE TABLE CategorySelectionRules (
RuleId INTEGER IDENTITY PRIMARY KEY,
RuleDescription VARCHAR(100),
RuleMessage VARCHAR(255),
Discount DECIMAL
)
CREATE TABLE CategorySelectionRuleDetail (
RuleCategoryId INTEGER IDENTITY PRIMARY KEY,
RuleId INTEGER REFERENCES CategorySelectionRules(RuleId),
CategoryId INTEGER REFERENCES EntryCategory(CategoryId)
)
C#程序(控制台应用程序)
namespace StackOverFlowTesting
{
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main(string[] args)
{
//Load Data
using (var db = new DatabaseDataContext())
{
//Check to see if the data has already been loaded
if (db.EntryCategories.Count() == 0)
{
//Load in inital categories
db.EntryCategories.InsertOnSubmit(new EntryCategory() { CategoryDescription = "Category1" });
db.EntryCategories.InsertOnSubmit(new EntryCategory() { CategoryDescription = "Category2" });
db.EntryCategories.InsertOnSubmit(new EntryCategory() { CategoryDescription = "Category3" });
//Load in inital rules
db.CategorySelectionRules.InsertOnSubmit(new CategorySelectionRule() { RuleDescription = "Rule1", RuleMessage = "Rule1 Message", Discount = .5M });
db.CategorySelectionRules.InsertOnSubmit(new CategorySelectionRule() { RuleDescription = "Rule2", RuleMessage = "Rule2 Message", Discount = .5M });
db.CategorySelectionRules.InsertOnSubmit(new CategorySelectionRule() { RuleDescription = "Rule3", RuleMessage = "Rule3 Message", Discount = .5M });
db.CategorySelectionRules.InsertOnSubmit(new CategorySelectionRule() { RuleDescription = "Rule4", RuleMessage = "Rule4 Message", Discount = .5M });
db.CategorySelectionRules.InsertOnSubmit(new CategorySelectionRule() { RuleDescription = "Rule5", RuleMessage = "Rule5 Message", Discount = .5M });
db.CategorySelectionRules.InsertOnSubmit(new CategorySelectionRule() { RuleDescription = "Rule6", RuleMessage = "Rule6 Message", Discount = .5M });
//Link up the categories and rules
db.CategorySelectionRuleDetails.InsertOnSubmit(new CategorySelectionRuleDetail() { CategoryId = 1, RuleId = 1 });
db.CategorySelectionRuleDetails.InsertOnSubmit(new CategorySelectionRuleDetail() { CategoryId = 1, RuleId = 2 });
db.CategorySelectionRuleDetails.InsertOnSubmit(new CategorySelectionRuleDetail() { CategoryId = 2, RuleId = 2 });
db.CategorySelectionRuleDetails.InsertOnSubmit(new CategorySelectionRuleDetail() { CategoryId = 1, RuleId = 3 });
db.CategorySelectionRuleDetails.InsertOnSubmit(new CategorySelectionRuleDetail() { CategoryId = 2, RuleId = 3 });
db.CategorySelectionRuleDetails.InsertOnSubmit(new CategorySelectionRuleDetail() { CategoryId = 3, RuleId = 3 });
db.CategorySelectionRuleDetails.InsertOnSubmit(new CategorySelectionRuleDetail() { CategoryId = 1, RuleId = 4 });
db.CategorySelectionRuleDetails.InsertOnSubmit(new CategorySelectionRuleDetail() { CategoryId = 3, RuleId = 4 });
db.CategorySelectionRuleDetails.InsertOnSubmit(new CategorySelectionRuleDetail() { CategoryId = 2, RuleId = 5 });
db.CategorySelectionRuleDetails.InsertOnSubmit(new CategorySelectionRuleDetail() { CategoryId = 3, RuleId = 5 });
db.CategorySelectionRuleDetails.InsertOnSubmit(new CategorySelectionRuleDetail() { CategoryId = 3, RuleId = 6 });
//Populate the database with our temp records
db.SubmitChanges();
}
/** Different ways of testing **/
//var selectedCategories = new List<int>() { 1 }; //Rule 1
var selectedCategories = new List<int>() { 1, 2 }; //Rule 2
//var selectedCategories = new List<int>() { 1, 2,3 }; //Rule 3
//var selectedCategories = new List<int>() { 1, 3 }; //Rule 4
//var selectedCategories = new List<int>() { 2, 3 }; //Rule 5
//var selectedCategories = new List<int>() { 3 }; //Rule 6
var rules = from r in db.CategorySelectionRules
where r.CategorySelectionRuleDetails.Count() == selectedCategories.Count &&
r.CategorySelectionRuleDetails.Where(cr => selectedCategories.Contains(cr.CategoryId.Value)).Count() == selectedCategories.Count
select r;
if (rules.Count() == 0)
{
Console.WriteLine("No rule found");
}
else
{
Console.WriteLine(rules.Count().ToString() + " Rules Found");
foreach (var rule in rules)
{
Console.WriteLine(rule.RuleDescription);
}
}
Console.ReadLine();
}
}
}
}