【问题标题】:Comparing Two Lists of Same User Defined Type比较相同用户定义类型的两个列表
【发布时间】:2017-01-28 11:59:54
【问题描述】:

我有一个这样的数据集

cpeid                                cveid             LastEdited
cpe:/o:microsoft:windows_8.1:-      CVE-2015-0001   2017-01-28 17:03:21.197
cpe:/o:microsoft:windows_8:-        CVE-2015-0001   2017-01-28 17:03:21.197
cpe:/o:microsoft:windows_rt:-:gold  CVE-2015-0001   2017-01-28 17:03:21.197

还有一个像这样的支持实体:

public partial class cve_cpe
{
    [StringLength(250)]
    public string cpeid { get; set; }

    [Key]
    [Column(Order = 0)]
    [StringLength(250)]
    public string cveid { get; set; }

    [Key]
    [Column(Order = 1, TypeName = "smalldatetime")]
    public DateTime LastEdited { get; set; }
}

我正在从一个简单的 c# 控制台应用程序中使用 entityframework 对数据库进行基本的 crud。在代码中的某处我这样做:

var mappings_to_add =  distinct_mappings_from_file
                      .Except(all_mappings, new cve_cpeComparer())
                      .ToList();

我的IEqualityComparer 实现是:

    class cve_cpeComparer : IEqualityComparer<cve_cpe>
    {
        public bool Equals(cve_cpe x, cve_cpe y)
        {
            if (object.ReferenceEquals(x, y))
                return true;
            if (x == null || y == null)
                return false;
            return x.cpeid == y.cpeid && x.cveid == y.cveid;
        }

        public int GetHashCode(cve_cpe obj)
        {
            return obj.cveid.GetHashCode() ^ obj.cpeid.GetHashCode();
        }
    }

但它不会简单地工作。我尝试了各种关于实现 GetHashCode、在实体和数据库中设置正确键、手动从数据库中删除重复项的建议,但它根本不起作用。我在这里错过了什么?

所以基本上,我希望 linq .Except 给我列表中那些不存在于 db 中的条目,即忽略那些在 db 数据列表中已经存在 cveidcpeid 的相同组合的条目。

有趣的是,这个东西可以与我的许多其他用户定义类型一起使用,它们具有IEqualityComparer 的风格(逻辑实现相同),所以想知道我做错了什么?是不是跟这些带符号的字符串的值有关?

更新

所以它现在可以工作了,事实证明,这是因为实体和模型构建器中的 pk 配置错误,修复它可以解决问题。 但是现在我想知道为什么LINQ .Except 甚至担心主键,特别是当我给它喂一个自定义的IEqualityComparer 时?

【问题讨论】:

  • 您是要在数据库中执行 except(使用 EF 查询)还是在内存中已经物化的列表中执行?
  • 它正在内存中的materialized list中执行
  • 您的相等比较器认为{ cpeid, cveid } 对定义了唯一性。但是实体 PK(唯一键)是{ cveid, LastEdited }。显然其中一个是错误的,必须纠正。
  • 是的,就是这样,错误的 pk 配置。现在可以了。谢谢,但你能帮我理解一下,为什么它(linq-to-entity)甚至会为 PK 烦恼?因为我正在处理内存中的分离列表?我的意思是在 IEqualityComparer 中,我从不关心 PK
  • 它适用于您的分离列表。但问题是它对mappings_to_add 做出了错误的假设,所以我想问题是当您真正尝试将它们添加到数据库时,您会遇到唯一约束违规。甚至在将它们添加(附加)到上下文时,因为 EF 在内部维护内存中的唯一键索引。

标签: c# performance linq


【解决方案1】:

我不小心进入了这个页面,IEqualityComparer.GetHashCode 的实现引起了我的注意。想象一下下面的代码:

var list = new List<cve_cpe>
{
    new cve_cpe { cpeid = "1", cveid = "2" },
    new cve_cpe { cpeid = "2", cveid = "1" }
};

var result = list.Except(list, new cve_cpeComparer()).ToList();

如果执行上述代码,Equals 方法将被调用 4 次,因为 GetHashCode 将为两个对象返回相同的值。通过更改GetHashCode的实现,可以减少到2倍:

public int GetHashCode(cve_cpe obj)
{
    return obj.cveid.GetHashCode() * 11 /*Any prime number*/ ^ obj.cpeid.GetHashCode();
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-11-12
    • 2019-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-01
    相关资源
    最近更新 更多