EF - 无法应用运营商' =='对于类型' TId'的操作数和' TId'

时间:2016-10-19 13:11:16

标签: c# entity-framework generics

我有这个泛型类,它使用Entity Framework 6.x。

public class GenericRepository<TEntity, TId> where TEntity, class, IIdentifyable<TId>
{
    public virtual TEntity GetById(TId id)
    {
        using (var context = new DbContext())
        {
            var dbSet = context.Set<TEntity>();
            var currentItem = dbSet.FirstOrDefault(x => x.Id == id);
            return currentItem;
        }
    }

    public virtual bool Exists(TId id)
    {
        using (var context = new DbContext())
        {
            var dbSet = context.Set<TEntity>();
            var exists = dbSet.Any(x => x.Id == id);
            return exists ;
        }
    }
}

这些接口:

public interface IIdentifyable : IIdentifyable<int>
{
}

public interface IIdentifyable<out TId>
{
    TId Id { get; }
}

看起来像这样的实体:

public class CustomerEntity : IIdentifyable<int>
{
    public string Name { get; set; }
    public int Id { get;set; }
}

public class ProductEntity : IIdentifyable<Guid>
{
    public string Name { get; set; }
    public Guid Id { get;set; }
}

我的问题是它没有编译。我收到此错误:

  

无法应用运营商&#39; ==&#39;对于&#39; TId&#39;类型的操作数和&#39; TId&#39;

我尝试将其更改为x => Equals(x.Id, id),但EF无法翻译它。有什么方法吗?

我知道我可以使用Find()代替FirstOrDefault。但我需要的不仅仅是上面提到的方法。 我有什么方法可以让EF将TIdTId进行比较? TId目前只有guidint。我已经看过下面的问题,但他们没有处理有关转换为SQL的问题。

Can't operator == be applied to generic types in C#?

How to solve Operator '!=' cannot be applied to operands of type 'T' and 'T'

2 个答案:

答案 0 :(得分:10)

更新:这是一种简单易用的方式,适用于EF。

将以下约束添加到GenericRepository

where TId : IEquatable<TId>

然后使用Equals方法

x => x.Id.Equals(id);

原始回答:

这是泛型的已知问题,通常使用EqualityComparer<T>.Default而不是==运算符来处理。但是,这种方法不适用于LINQ to Entities。

解决它的一种方法是使用System.Linq.Expressions命名空间中的Expression类动态构建谓词,如下所示:

public class GenericRepository<TEntity, TId> where TEntity: class, IIdentifyable<TId>
{
    protected static Expression<Func<TEntity, bool>> EqualsPredicate(TId id)
    {
        Expression<Func<TEntity, TId>> selector = x => x.Id;
        Expression<Func<TId>> closure = () => id;
        return Expression.Lambda<Func<TEntity, bool>>(
            Expression.Equal(selector.Body, closure.Body),
            selector.Parameters);
    }
}

并像这样使用它:

dbSet.FirstOrDefault(EqualsPredicate(id));

dbSet.Any(EqualsPredicate(id));

答案 1 :(得分:-1)

This only compiles if you constrain the TId type to be a reference type:

public class GenericRepository<TEntity, TId> 
    where TEntity: class, IIdentifyable<TId> 
    where TId: class

However, this may not be suitable in your case, so you would have to create different classes to support GUID, int or long id values.