我想比较2个通用值,为了达到这个目的,我使用了这样的东西:
public TEntity Get(TKey key) where TKey: struct
{
if (!key.GetType().IsPrimitive)
{
throw new Exception($"Repository->Get: given type {key.GetType().Name} is not supported");
}
return _dbSet.Single(x=> x.Id.ToString().Equals(key.ToString()));
}
如果我只是使用
x.Id.Equals(key)
抛出异常
无法创建类型' System.Object'的常量值。只要 在此上下文中支持原始类型或枚举类型
如果不使用toString()
修改
在评论中得到了使用Find
方法的答案,但它只是答案的一部分,如果我想使用Where
方法怎么办?
EDIT2:
接受的答案完全解决了我的问题,只是将struct
限制更改为
IEquatable
答案 0 :(得分:1)
在您当前的版本中,当您这样做时:
_dbSet.Single(x=> x.Id.Equals(key));
由于键的类型可以是任意的,因此使用了object.Equals(object)重载,因此您的key
应该转换为object
。 EF有一个问题,因为它看到你使用Expression.Constant
类型的常量(如object
),并且你不能在SQL中使用任意类型,因此它立即说它将无法使用将object
常量转换为sql。
您可以通过强制TKey
实施IEquatable<TKey>
:
where TKey : IEquatable<TKey>
然后当你做
_dbSet.Single(x=> x.Id.Equals(key));
将使用 IEquatable<TKey>.Equals(TKey)
版本的等号,不需要将key
转换为object
。 EF并不特别关心那个界面。在解析您的查询时,它会看到名称为“Equals”和兼容签名的方法,这就是它所关心的全部内容。因此,你也可以做这样的事情,而不是要求IEquatable<TKey>
:
public static class EfHelpers {
public static bool Equals<TKey>(TKey key, TKey other) {
// not intended to call directly
throw new NotImplementedException();
}
}
public TEntity Get(TKey key) {
// method has name "Equals", compatible signature and does not require
// casting to object, so we are fine
return _dbSet.Single(x => EfHelpers.Equals(x.Id, key));
}
但我不建议在这种特殊情况下这样做,因为您关心的所有类型都已实现IEquatable<T>
。但是这种技术在其他情况下可能会有用。