我继承了一个非常草率的项目,我的任务是解释为什么它不好。我已经注意到他们已经完成了比较这些代码的所有代码
(IQueryable).FirstOrDefault(x => x.Facility == facility && x.Carrier == shipCode.Carrier
上面示例中的 x.Facility
来自数据库,设施来自shipment.facility
,它被映射为nhibernate中的复杂对象。
我原本希望看到.FirstOrDefault(x => x.Facility.ID == facility.ID
起初我认为比较整个对象可能会导致问题,如果在数据库中更改了设施记录,那么存储在运输中的设施显然会有所不同。
在考虑之后,我意识到shipping.facility是从id填充的,所以它应该匹配,即使该设施记录发生了变化。
我觉得这对我来说感觉不对,我觉得这很麻烦而且难以追踪?比较整个对象与id是否存在特殊错误。
答案 0 :(得分:2)
将我之前的评论扩展为答案:
我认为当ORM允许时,这实际上是良好的练习。我对NHibernate没有经验,所以对于其余的答案我会假设它确实以合理的方式实现了相等(例如比较主键)。你应该确保这种情况,否则代码不仅会很糟糕,而且可能会出错。
作为类比,暂时忘记SQL,想象你正在处理一个不属于任何ORM的POCO。您想要在以下选项中进行选择:
方法1:IEquatable
public class Facility : IEquatable<Facility>
{
public int Id {get; private set;}
//The rest of the properties
public bool Equals(Facility other)
{
return other.Id == Id;
}
}
(您还想覆盖Object.Equals
,但为了简洁,我将其排除在外)
方法2:IEqualityComparer
public class Facility
{
public int Id {get; private set;}
//The rest of the properties
}
public class FacilityIdsMatchEqualityComparer : IEqualityComparer<Facility>
{
public bool Equals(Facility x, Facility y)
{
return x.Id == y.Id;
}
}
(GetHashCode
也为了简洁而被排除在外。)
这两种方法中的哪一种更好?好吧,我会明确表示方法1 。它遵循方法2 没有的两个重要原则:
不要重复自己。在第二种方法中,任何试图比较设施的代码都必须使用FacilityIdsMatchEqualityComparer
。这个事实,即需要使用特定的相等比较逻辑,将在整个解决方案中喷涂,每次要比较它们时都会重复。
单一责任原则。每个进行比较的类不仅需要重复代码,而且代码承担的责任不属于类。它应该由Facility
来表达平等的实现,而不是每个想要用它来表示通过比较Id
来完成它的类。 (是的,我意识到你可以做到,比如,FacilityEqualityComparer
,这样调用类仍然不知道如何进行相等比较,但是这个目的与OP中的代码类比,其中确切的比较逻辑被硬编码到每个比较中)
所以,把它带回实际问题,这个类比非常接近你在这里的情况。它并不像Facility
实现IEquatable
那么简单,但原则完全相同:ORM负责实现等式检查的方式,而不是将责任推到代码中它。
这意味着如果我正在编写代码,比如说在数据访问层之外,我可以说“我想检查这两个对象是否相等,所以我会写object1 == object2
”,而不是“我想检查这两个对象是否相等,这两个对象是实体,由于我们实现持久性的方式,这意味着当我检查将转换为SQL查询的相等性时,所以我需要写这个检查好像我在编写SQL,这意味着我需要比较他们的主键,通过检查属性或者通过我对数据访问层中的约定的知识,我知道比较他们的Id
属性。所以我'我会写object1.Id == object2.Id
“。
ORM并不完美,你不能总是完全抽象出底层的SQL数据库,但是当你可以时,你应该这样做!
答案 1 :(得分:0)
我不确定您使用的是什么,但是如果您比较对象,通常Entity Framework会给您一个错误:
无法创建类型&#39; ...&#39;的常量值。只有原始类型 或者在此上下文中支持枚举类型。
因为您发送的类需要转换为SQL。在任何情况下,如果您尝试向SQL Server发送查询,则比较对象不适合SQL代码。