Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EF - Cannot apply operator '==' to operands of type 'TId' and 'TId'

I have this generic class, which uses 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 ;
        }
    }
}

And these interfaces:

public interface IIdentifyable : IIdentifyable<int>
{
}

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

And entities that looks like this:

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

My issue is that it doesn't compile. I get this error:

Cannot apply operator '==' to operands of type 'TId' and 'TId'

I tried to change it to x => Equals(x.Id, id), but then EF cannot translate it. Any way around it?

I know that I can use Find() instead of FirstOrDefault. But I need this for more than the methods mentioned above. Is there any way I can let EF compare TId with TId? TId is currently only guid and int. I've already seen the questions below, but they don't handle the issue regarding translation to 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'

like image 579
smoksnes Avatar asked Oct 19 '16 13:10

smoksnes


1 Answers

Update: Here is a simple concise way that works with EF.

Add the following constraint to the GenericRepository class

where TId : IEquatable<TId>

and then use Equals method

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

Original answer:

This is a known issue with generics which normally is handled by using EqualityComparer<T>.Default instead of the == operator. However this approach doesn't work with LINQ to Entities.

One way to solve it is to build the predicate dynamically using the Expression class from the System.Linq.Expressions namespace like this:

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

and use it like this:

dbSet.FirstOrDefault(EqualsPredicate(id));

or

dbSet.Any(EqualsPredicate(id));

etc.

like image 156
Ivan Stoev Avatar answered Oct 23 '22 12:10

Ivan Stoev