object == object而不是object.id == object.id潜在的问题

时间:2014-06-02 15:27:19

标签: c# optimization

我继承了一个非常草率的项目,我的任务是解释为什么它不好。我已经注意到他们已经完成了比较这些代码的所有代码

(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是否存在特殊错误。

2 个答案:

答案 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代码。