我需要为搜索页面编写一些linq(linq-to-sql(,允许用户搜索汽车,并选择性地包括汽车零件的搜索条件。 两个表分别是CAR
和CAR_PARTS
。 这是我到目前为止所拥有的:
var query = db.CAR;
//if the user provides a car name to search by, filter on car name (this works)
if(model.CarName != "")
{
query = from c in query
where c.Name == model.CarName
select c;
}
//if the user provides a car part name to filter on, join the CAR_PART table
if(model.CarPartName != "")
{
query = from c in query
join parts in db.CAR_PARTS on c.ID equals parts.CarID
where parts.PartName == model.CarPartName
select c;
}
//if the user provides a car part code to filter on, join the CAR_PART table
if(model.CarPartCode != "")
{
query = from c in query
join parts in db.CAR_PARTS on c.ID equals parts.CarID
where parts.PartCode == model.CarPartCode
select c;
}
如果用户决定要同时搜索CarPartName
和CarPartCode
,这个逻辑将导致CAR_PART
表被连接两次。 这对我来说感觉不对,但这是处理这个问题的正确方法吗?
你会怎么写?
这样做是合法的,但是否有意义,取决于你的数据模型和你想要的结果。
通常,如果定义了partname
和partcode
,则代码执行以下操作
- 将
cars
表与partname
作为连接条件的parts
表联接 - 再次使用
parts
表联接第一次联接的结果,并将partcode
作为联接条件。
因此,这等于连接条件为car.partname = part.name and car.partcode = part.code
的连接。我不知道,这是否是你想要的行为。
有一些情况需要区分
使用AND条件连接
案例 1.1:部件的名称和代码是部件表中的关键
在这种情况下,每个name
和code
在零件表中都是唯一的,因此对于每个name
只有一个code
。双重联接不是必需的,甚至可能导致错误的结果,因为
-
如果选择
name
并code
识别相同的部分,则第一个联接将已经获得所需的结果 -
如果
name
和code
识别不同的部分,则结果将为空,因为无法满足条件。
在这种情况下,我建议写如下
if (!string.IsNullOrEmpty(model.CarPartName)){
// your join on partname
} else if (!string.IsNullOrEmpty(model.CarPartCode)) {
// your join on partcode
}
案例 1.2:部件的名称和代码不是部件表中的键
在这种情况下,name
和code
都不可能是唯一的,对于一个name
可能有不同的code
,反之亦然。在这里,双联接是必需的,并且只会返回包含与name
和code
匹配的部分的结果
使用OR条件连接
另一方面,如果您希望连接条件类似于car.partname = part.name and car.partcode = part.code
则必须考虑以下情况
案例 2.1 名称和代码是键
此处适用于上述情况 1.1
案例 2.2 名称和代码不是键
在这里,您不能使用逐步方法,因为第一个联接的结果将仅包含name
匹配的汽车。可能有些部分只有code
条件匹配,但如果它们不包含在第一个匹配的结果中,则它们永远不会包含在最终结果中。因此,在这种情况下,您必须定义如下所示的查询
if (!string.IsNullOrEmpty(model.CarPartName) && !string.IsNullOrEmpty(model.CarPartCode)) {
query = from c in query
join parts in db.CAR_PARTS on c.ID equals parts.CarID
where parts.PartName == model.CarPartName || parts.PartCode == model.CarPartCode
select c;
} else if (!string.IsNullOrEmpty(model.CarPartName)) {
query = from c in query
join parts in db.CAR_PARTS on c.ID equals parts.CarID
where parts.PartName == model.CarPartName
select c;
} else if (!string.IsNullOrEmpty(model.CarPartCode)) {
query = from c in query
join parts in db.CAR_PARTS on c.ID equals parts.CarID
where parts.PartCode == model.CarPartCode
select c;
}
那里的错误在于,在适当的关系中,你根本不需要连接。添加LinqToSQL的行为,你可以把它写成:
var query = db.CAR
.Where( c =>
( string.IsNullOrEmpty(model.CarName)
|| c.Name == model.CarName ) &&
( string.IsNullOrEmpty(model.CarPartName)
|| c.Parts.Any( p => p.PartName == model.CarPartName )) &&
( string.IsNullOrEmpty(model.CarPartCode)
|| c.Parts.Any( p => p.PartCode == model.CarPartCode )));
只要查询是 IQueryable (db.汽车。AsQueryable(((。两种 Linq 方法相似但不相同。根据您的真正需要,您的可能是正确的或错误的。你的将产生两个内部连接,而这个只是创建 2 个存在检查。假设您有:
Car, Id:5, Name: Volvo
以及以下部分:
CarID:5, PartName:HeadLights, PartCode:1 ... other details
CarID:5, PartName:HeadLights, PartCode:2 ... other details
CarID:5, PartName:HeadLights, PartCode:3 ... other details
如果用户询问模型。CarName = "Volvo" and model。零件名称="大灯",你会得到同一辆沃尔沃3次。在第二种方法中,您可以返回一辆沃尔沃。
呵呵
我对流畅的语法感觉更舒服,但我相信类似于以下内容的东西会适合您。我会检查模型中的字段作为 Select 语句的一部分,然后使用一个字段或另一个字段有条件地连接。如果两者都未设置,则将其保留为空。
var query = db.CAR;
if (!string.IsNullOrWhitespace(model.CarName))
{
query = query.Where(car => car.Name == model.CarName);
}
var items = query.Select(car => new
{
Car = car, // maybe better to split this up into different fields, but I don't know what the car object looks like
// I assume your Car entity model has a navigation property to parts:
CarPart = !string.IsNullOrWhitespace(model.CarPartName)
? car.Parts.FirstOrDefault(part => part.PartName == model.CarPartName)
: !string.IsNullOrWhitespace(model.CarPartCode)
? car.Parts.FirstOrDefault(part => part.PartCode == model.CarPartCode)
: null
})
.ToList();
这确实意味着如果填写了名称,代码将被忽略。如果需要相反,请将其逆转。或者,如果要同时使用这两个字段,则可以将字符串 null 检查放在 Where 子句中。