比较通用结构值

时间:2017-11-11 13:51:00

标签: c# entity-framework generics

我想比较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

1 个答案:

答案 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>。但是这种技术在其他情况下可能会有用。