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'
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With