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

I have this generic class that 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 objects that look 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 problem is that it does not compile. I get this error:

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

I tried changing it to x => Equals(x.Id, id) , but then EF will not be able to translate it. Anyway?

I know that instead of FirstOrDefault I can use Find() . But I need it more than the methods mentioned above. Is there any way to match EF TId with TId ? TId is currently only guid and int . I have already seen the questions below, but they do not cope with the problem of translating to SQL.

Is it impossible to apply the == operator to generic types in C #?

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

+7
generics c # entity-framework
source share
2 answers

This is a known issue with generics, which are usually handled using EqualityComparer<T>.Default instead of the == operator. However, this approach does not work with LINQ to Entities.

One way to solve it is to dynamically build a predicate 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 as follows:

 dbSet.FirstOrDefault(EqualsPredicate(id)); 

or

 dbSet.Any(EqualsPredicate(id)); 

and etc.

Update: Here is another way that works with EF.

Add the following restriction to the GenericRepository class

 where TId : IEquatable<TId> 

and then use the Equals method

 x => x.Id.Equals(id); 
+7
source share

This only compiles if you restrict the TId type TId reference type:

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

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

0
source share

All Articles