我继承了一个非常草率的项目,我的任务是解释为什么它不好。我注意到他们在整个代码中都进行了这样的比较
(IQueryable).FirstOrDefault(x => x.Facility == facility && x.Carrier == shipCode.Carrier
在上面的示例中,x.Facility
来自数据库,而 facility 来自 shipment.facility
,它在 nhibernate 中被映射为一个复杂对象。
我希望看到 .FirstOrDefault(x => x.Facility.ID == facility.ID
起初我认为比较整个对象可能会导致问题,如果数据库中的设施记录发生更改,那么存储在 shipment 中的设施显然会有所不同。
经过更多考虑后,我意识到 shipment.facility 是从 id 填充的,因此即使该设施记录发生变化,它也应该匹配。
我仍然感觉不对,我看到这有很多错误并且很难追踪?将整个对象与一个 ID 进行比较是否有什么特别错误的地方。
将我之前的评论扩展为答案:
我认为当 ORM 允许时,这实际上是好的做法。我没有使用 NHibernate 的经验,所以对于其余的答案,我将假设它实际上确实以一种明智的方式实现了平等(例如比较主键)。您应该确保是这种情况,否则代码不仅糟糕而且可能存在错误。
打个比方,暂时忘掉 SQL,假设您正在处理一个不属于任何 ORM 的 POCO。您想要在以下选项之间进行选择:
方法一:IEquatable
public class Facility : IEquatable
{
public int Id {get; private set;}
//The rest of the properties
public bool Equals(Facility other)
{
return other.Id == Id;
}
}
(您还想覆盖 Object.Equals
,但为了简洁起见,我将排除它)
方法二:IEqualityComparer
public class Facility
{
public int Id {get; private set;}
//The rest of the properties
}
public class FacilityIdsMatchEqualityComparer : IEqualityComparer
{
public bool Equals(Facility x, Facility y)
{
return x.Id == y.Id;
}
}
(为简洁起见,也排除了GetHashCode
)。
这两种方法哪种更好?好吧,我会说它是方法 1 的明显优势。它遵循方法 2 不遵循的两个重要原则:
不要重复自己。在第二种方法中,任何试图比较设施的代码都必须使用 FacilityIdsMatchEqualityComparer
。这个事实,即需要使用特定的相等比较逻辑,会在整个解决方案中喷洒,每次你想比较它们时都会重复。
单一职责原则。不仅每个进行比较的类都需要重复代码,而且该代码承担了不属于该类的责任。应该由Facility
来表达相等性的实现,而不是每个想用它的类都说是通过比较Id
来完成的。 (是的,我知道你可以做到,比如说,FacilityEqualityComparer
,这样调用类就不知道相等比较是如何完成的,但这样做的目的是类比OP,其中确切的比较逻辑被硬编码到每个比较中)
因此,回到实际问题上来,这个类比非常接近地反射(reflect)了您在这里遇到的情况。它不像实现 IEquatable
củaFacility
那样简单,但原理是完全一样的:ORM 自己负责如何实现相等性检查,而不是推送使用它编写代码的责任。
这意味着如果我正在编写代码,比如说在数据访问层之外,我可以说“我想检查这两个对象是否相等,所以我会写 object1 == object2
”,而不是“我想检查这两个对象是否相等,这两个对象是实体,由于我们实现持久性的方式,这意味着当我检查是否相等时将转换为 SQL 查询,所以我需要像编写 SQL 一样编写此检查,这意味着我需要比较它们的主键,这要么通过检查属性,要么通过我对数据访问层约定的了解,我知道这意味着比较它们的 Id
属性。所以我将编写 object1.Id == object2.Id
”.
ORM 并不完美,您不能总是完全抽象出底层 SQL 数据库,但如果可以,您应该这样做!
Tôi là một lập trình viên xuất sắc, rất giỏi!