【问题标题】:Getting errors when using Equals on generic parameters with EF使用 EF 对泛型参数使用 Equals 时出错
【发布时间】:2026-02-24 15:40:01
【问题描述】:

我有以下两个通用类:

 public abstract class Entity<T> : IEntity<T>, IAuditableEntity,ISoftDeletable where T : struct
    {
        public T Id { get; set; }
        public DateTime CreatedDate { get; set; }
        public string CreatedBy { get; set; }
        public DateTime UpdatedDate { get; set; }
        public string UpdatedBy { get; set; }
        public bool Deleted { get; set; }
    }


    public abstract class Repository<TEntity,TKey> : IRepository<TEntity, TKey> where TEntity :Entity<TKey> where TKey : struct           
    {
        protected DbContext Context;
        protected readonly IDbSet<TEntity> Set;

        protected Repository(DbContext context)
        {
            Context = context;
            Set = Context.Set<TEntity>();
        }

        public TEntity Get(TKey key)
        {               
            var output = Set.FirstOrDefault(o => o.Id.Equals(key));
            return output ;
        }
    }

我的问题是 Get 方法。我不断收到此错误

无法创建“System.Object”类型的常量值。仅有的 在此上下文中支持原始类型或枚举类型。”

我尝试使用 == 但它不会编译并说

不能将 == 应用于操作数 TKey 和 TKey

为什么这不起作用?我的 TKey 应该是原始数据类型,不是 TKey:struct 正确的限制器吗?

为什么有Equals(int)的时候编译器还要用Equals(object),也就是这个key是什么?

【问题讨论】:

  • 您的IDbSet 不知道TKey 的任何内容,因此它不能将== 应用于属性Id(我怀疑它的类型是TKey)。
  • 怎么样,如果我保护了只读 IDbSet Set;哪里 TEntity :Entity where TKey : struct ??
  • 嗯,id 是 1,我不知道你为什么怀疑 Id 是 TKey 类型。需要详细说明吗?
  • o.Id 属于 T 类型(至少它在您的实体声明中说,key 属于 TKey 类型。
  • @DevilSuichiro:用作约束参数的名称与方法参数的名称一样没有意义。您不需要在两个单独的声明中使用相同的名称。在这种情况下,Repository 的声明表明第一个约束参数 TEntity 的类型必须为 Entity&lt;TKey&gt;,因此第二个约束参数 (TKey) 成为 Entity&lt;T&gt; 的第一个/唯一约束参数。

标签: c# entity-framework generics struct


【解决方案1】:

== 比较失败,因为该运算符只能用于预定义的值类型或引用类型,而您的 TKey 两者都不是。

您在评论中说TKeyint。在你的的情况下可能是这样,但是你的类的用户可以定义一个Entity&lt;List&lt;string&gt;.Enumerator&gt;,因为List&lt;T&gt;.Enumerator是一个struct。在这种情况下,“等于”的含义令人难以置信。

我的意思是编译器无法知道,在编译时,除了使用object.Equals之外,还能做什么。

我想知道您为什么将密钥类型限制为struct。我见过的最常见的 ID 类型是intstring。通过使用struct,您已经排除了string...

您真的需要支持非整数键的灵活性吗?如果没有,您的课程可能会更简单。


更新:您可以限制您的TKey,使其实现IComparable,因为所有数字类型都实现了它。然后,您可以在 Get 方法的实现中使用 CompareTo

但是,由于您传递给 FirstOrDefault 的 lambda 实际上将在数据库端执行,所以我不确定会发生什么,即 EF 是否能够正确地将其转换为 SQL 表达式。

public abstract class Entity<T> : IEntity<T>, IAuditableEntity,ISoftDeletable where T : struct, IComparable

public abstract class Repository<TEntity,TKey> : IRepository<TEntity, TKey> where TEntity :Entity<TKey> where TKey : struct, IComparable

【讨论】:

  • 我真的不在乎我是否使用 == 或 Equals,我需要的是一种将现有 ID 与给定参数进行比较的方法。我的实体具有 T:struct 约束的原因是我有相当数量的表具有字节键(查找表)和相当数量的长键。所以基本上我有一种均匀分布的 tinyints、ints 和 longs 作为数据库级别的键。我可以更改数据库,但不能更改密钥。
  • 我不认为 Repository.Enumerator> 是可能的,因为 List.Enumerator 不是从 Entity 派生的。可能是错的,但是...
  • 抱歉,我的意思是 Entity&lt;List&lt;string&gt;.Enumerator&gt;', the point being that since TKey` 只能是 struct,它允许各种疯狂的东西。
  • 最终使用 (object)o.Id == (object)key 但我怀疑地球表面是否存在更丑陋的黑客攻击
  • 我自己也有类似的情况,我很高兴找到了这个解决方法,但你有没有想出任何其他解决方案?
【解决方案2】:

您可以简单地使用IDbSet.Find 方法,该方法采用一个或多个object 类型的键值。

public TEntity Get(TKey key)
{               
    return Set.Find(key);
}

如果实体已经存在于DbContext 的本地缓存中,它还有一个额外的优势(因为它避免了不必要的数据库访问)。

但是,我怀疑您要做的不仅仅是检索单个项目,因此您以后可能会遇到同样的问题。

【讨论】:

  • 是的。我刚刚发布了 get 方法,但兔子洞做得更深了