我需要为搜索页面编写一些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
表被连接两次。这对我来说是错的,但这是处理此问题的正确方法吗?
你怎么写的?
答案 0 :(得分:1)
这样做是合法的,但是是否有意义,取决于您的数据模型和所需的结果。
通常,如果定义了partname
和partcode
,则您的代码会执行以下操作
cars
作为连接条件,将parts
表与partname
表一起连接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相同。
CASE 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;
}
答案 1 :(得分:1)
实际上存在什么错,有了适当的关系,您根本不需要联接。添加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.CAR.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
如果用户使用model.CarName =“ Volvo”和model.PartName =“ HeadLights”询问,您将获得相同的Volvo 3次。在第二种方法中,您将获得一辆沃尔沃汽车。
HTH
答案 2 :(得分:-1)
我对流利的语法感到更自在,但是我确信类似于以下内容的内容将对您有用。我将在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();
这确实意味着如果填写名称,则将忽略代码。如果需要相反的处理,则将其反转。或者,如果您想同时使用这两个字段,则可以将字符串空检查放在“ Where”子句中。