EF-не может применить оператор '= = 'к операндам типа' TId ' и 'TId'

у меня есть этот общий класс, который использует 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; }
}

моя проблема в том, что он не компилируется. Я получаю эту ошибку:

не удается применить оператор'==' к операндам типа 'TId' и 'TId'

я попытался изменить его на x => Equals(x.Id, id), но тогда EF не может перевести его. Как это обойти?

я знаю, что могу использовать Find() вместо FirstOrDefault. Но мне это нужно больше, чем упомянутые выше методы. есть ли способ, которым я могу позволить EF сравнить TId С TId? TId времени guid и int. Я уже видел вопросы ниже, но они не обрабатывают проблему, касающуюся перевода на SQL.

не может operator == применяться к общим типам в C#?

как решить оператор '!= 'не может быть применен к операндам типа' T ' и 'T'

2 ответов


это известная проблема с дженериками, которая обычно обрабатывается с помощью EqualityComparer<T>.Default вместо == оператора. Однако этот подход не работает с LINQ для сущностей.

один из способов его решения-динамически построить предикат с помощью Expression класс System.Linq.Expressions пространство имен, как это:

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));

etc.

обновление: вот еще один способ, который работает с EF.

добавьте следующее ограничение в GenericRepository класс

where TId : IEquatable<TId>

и затем использовать Equals метод

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

это компилируется только если вы ограничиваете TId введите тип ссылки:

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

однако это может быть не подходит в вашем случае, поэтому вам придется создавать разные классы для поддержки значений GUID, int или long id.