在同一查询中联接表两次是否安全?



我需要为搜索页面编写一些linq(linq-to-sql(,允许用户搜索汽车,并选择性地包括汽车零件的搜索条件。 两个表分别是CARCAR_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;
}

如果用户决定要同时搜索CarPartNameCarPartCode,这个逻辑将导致CAR_PART表被连接两次。 这对我来说感觉不对,但这是处理这个问题的正确方法吗?

你会怎么写?

这样做是合法的,但是否有意义,取决于你的数据模型和你想要的结果。

通常,如果定义了partnamepartcode,则代码执行以下操作

  1. cars表与partname作为连接条件的parts表联接
  2. 再次使用parts表联接第一次联接的结果,并将partcode作为联接条件。

因此,这等于连接条件为car.partname = part.name and car.partcode = part.code的连接。我不知道,这是否是你想要的行为。

有一些情况需要区分

使用AND条件连接

案例 1.1:部件的名称和代码是部件表中的关键

在这种情况下,每个namecode在零件表中都是唯一的,因此对于每个name只有一个code。双重联接不是必需的,甚至可能导致错误的结果,因为

  1. 如果选择namecode识别相同的部分,则第一个联接将已经获得所需的结果

  2. 如果namecode识别不同的部分,则结果将为空,因为无法满足条件。

在这种情况下,我建议写如下

if (!string.IsNullOrEmpty(model.CarPartName)){
// your join on partname
} else if (!string.IsNullOrEmpty(model.CarPartCode)) {
// your join on partcode
}

案例 1.2:部件的名称和代码不是部件表中的键

在这种情况下,namecode都不可能是唯一的,对于一个name可能有不同的code,反之亦然。在这里,双联接是必需的,并且只会返回包含与namecode匹配的部分的结果

使用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 子句中。

最新更新