【问题标题】:Entity Framework - Child object is null in GetHashCode实体框架 - GetHashCode 中的子对象为空
【发布时间】:2015-03-20 14:58:24
【问题描述】:

实体框架6 MVC5

我是 EF 和 MVC 的新手,我有一个场景,我有一个覆盖 Equals 和 GetHashCode 的类:

EF 代码:

public partial class ProdCategory
{
    public long ProdID { get; set; }
    public int CategoryID { get; set; }
    public bool PermanentlyDelete { get; set; }
    public System.DateTime DateCreated { get; set; }

    public virtual Category Category { get; set; }
    public virtual Product Product { get; set; }
}

我的代码:

    public override bool Equals(object obj)
    {
        if (obj == null | (!object.ReferenceEquals(this.GetType(), obj.GetType())))
        {
            return false;
        }

        ProdCategory p = (ProdCategory)obj;

        if (this.Category == p.Category && this.Product == p.Product)
        {
            return true;
        }

        return false;
    }
    public override int GetHashCode()
    {
        unchecked
        {                
            var result = 0;
            result = (result * 397).GetHashCode() ^ this.Product.GetHashCode();
            result = (result * 397).GetHashCode() ^ this.Category.GetHashCode();
            return result;
        }
    }

此类成为产品的子对象,当我转到产品列表时,查看 GetHashCode 错误

Heisenberg.dll 中出现“System.NullReferenceException”类型的异常,但未在用户代码中处理

我认为这可能是延迟加载问题,但 GetHashCode 函数中存在 Product 对象,所以我假设如果存在,Category 应该是..?我尝试使用以下命令强制加载 prodCategory:

var products = db.Products.Include(p => p.ProdCategories);

但这并没有什么不同。因此,虽然我需要强制在 ProdCategory 中加载类别,但我无法为此制定语法。

作为测试,我注释掉了 GetHashCode 函数 - 页面加载正常,我可以转到产品的详细信息视图并查看 prodCategory 详细信息,因此它确实会在某些时候填充。这就是让我失望的原因!不知道还能尝试什么?

我希望有人能看出我哪里出错了。

更新: 如果我在 GetHashCode 中设置断点 - 第一次遇到它时,Product 已填充,Category 为空。如果我不运行 Category 行并继续,则会再次遇到断点。这次同时填充了产品和类别。

谁能阐明正在发生的事情以及最佳行动方案是什么?我可以检查 GetHashCode 中是否为 null,但它真的不应该为 null,我只是隐藏错误。

【问题讨论】:

  • ProdCategory 是做什么的?它看起来只是一个关联,在这种情况下,它不应该存在于您的域模型中。
  • 它包含其他属性,例如 MarkForDeletion
  • 第一个问题是:为什么需要Hashcode?您只能将它用于内存中的对象,而不能用于针对数据库的 LINQ 查询,而我认为它是在 LINQ 查询中进行排序和比较的。
  • 嗨,我正在从一个 winforms 项目中移动所有这些代码。在该项目中创建覆盖的 equals 时,我被告知您应该始终更改 GetHasCode 函数以匹配,因为“Equal”对象应该始终具有相同的哈希码。
  • 好吧,那么问题又回到了:为什么需要覆盖 Equals?是否有任何代码显式或隐式需要比较 ProdCategory 实例(隐式使用例如具有 Dictionary<ProdCategory,ValueType>>)。

标签: c# asp.net-mvc entity-framework


【解决方案1】:

无论如何,您应该根据外键执行GetHashCode

public override int GetHashCode()
{
    unchecked
    {                
        var result = this.ProdID.GetHashCode();
        result = (result * 397) ^ this.CategoryID;
        return result;
    }
}

永远不会设置外部 ID(假设该项目是从数据库中“获取”的),这是因为外部 ID 位于行本身(因此不需要连接)。为了解决第一次实例化对象时 HashCode 错误的问题,我会这样做。

public partial class ProdCategory
{
    protected ProdCategory() {} //protected default constructor is required by EF

    public ProdCategory(Category category, Product product)
    {
        this.Category = category;
        this.Product = product;
        this.ProdID = product.Id;
        this.CategoryID = category.Id;
    }

    public long ProdID { get; protected set; }
    public int CategoryID { get; protected set; }
    public bool PermanentlyDelete { get; set; }
    public System.DateTime DateCreated { get; set; }

    public virtual Category Category { get; protected set; }
    public virtual Product Product { get; protected set; }
}

【讨论】:

  • 好的 - 也许这是编写 GetHashCode 函数的更好方法(这是更好的原因是什么?)但它仍然无法解释为什么最初没有填充 Category。它只是隐藏了 ID 为 0 的事实。在我的模型中,0 不是有效值。
  • @Rick 未填充类别/产品的原因是因为您使用的是实际的ProdCategory,而不是ProdCategory 的EF 代理。所以 EF 无法将延迟加载代码注入到虚拟属性 getter 中。总的来说,依赖延迟加载导致的问题比它真正解决的问题要多。
  • 另外,使用可能涉及到数据库往返的GetHashCode 函数并不是很明智(GetHashCode 应该尽可能地提高性能,这就是重点)。我也不会使用极有可能可变的数据(如外键 ID)来定义 GetHashCode...我会将其保留在 ProdID,除非 ProdIDCategoryID 都是主键...再说一次,那个设计对我来说没有意义(为什么不是每个产品都有自己的 id,独立于类别?)
  • 哦,我看到这是一个关联表……那么同时使用ProdIDCategoryID 可能是有意义的……但是仅仅拥有那个关联表并没有多大用处有道理......它有什么需要的属性?为什么要“标记删除关联”?如果是这种情况,您可以在ProductCategory 中执行此操作,只需将关联设置为CategoryProduct 为null。使用 ORM 的要点之一是您可以摆脱这些“样板化”表。
  • @Aron 哦,是的,并没有真正评论你的答案,这是问题的正确答案(我现在要投票)......只是质疑设计决策OP。
猜你喜欢
  • 2011-12-21
  • 2011-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-12
  • 1970-01-01
相关资源
最近更新 更多