I have the following two generic classes:
public abstract class Entity<T> : IEntity<T>, IAuditableEntity,ISoftDeletable where T : struct
{
public T Id { get; set; }
public DateTime CreatedDate { get; set; }
public string CreatedBy { get; set; }
public DateTime UpdatedDate { get; set; }
public string UpdatedBy { get; set; }
public bool Deleted { get; set; }
}
public abstract class Repository<TEntity,TKey> : IRepository<TEntity, TKey> where TEntity :Entity<TKey> where TKey : struct
{
protected DbContext Context;
protected readonly IDbSet<TEntity> Set;
protected Repository(DbContext context)
{
Context = context;
Set = Context.Set<TEntity>();
}
public TEntity Get(TKey key)
{
var output = Set.FirstOrDefault(o => o.Id.Equals(key));
return output ;
}
}
My problem is the Get method. I keep getting this error
Unable to create a constant value of type 'System.Object'. Only primitive types or enumeration types are supported in this context."
i tried to use == but it wont compile and says
cannot apply == to operands TKey and TKey
Why isnt this working? my TKey is supposed to be a primitive data type, isnt the limiter where TKey:struct the correct one?
Why is the compiler using Equals(object) when there is Equals(int), which is what this key is?
The ==
comparison fails because that operator can only be used for predefined value types or for reference types, and your TKey
is neither.
You say in your comment that TKey
is an int
. That may be true in your case, but a user of your class(es) could define an Entity<List<string>.Enumerator>
since List<T>.Enumerator
is a struct
. The mind boggles at what "equals" means in that case.
My point is that the compiler has no way to know, at compile time , how to do anything other than use object.Equals
.
I wonder why you're constraining the key type to be a struct
anyway. The most common types of ID that I've seen are int
and string
. By using a struct
you're already ruling out string
...
Do you really need the flexibility to support keys that are not integers? If not, your classes could be much simpler.
Update : you could constrain your TKey
such that it implements IComparable
, since all the numeric types implement that. That would then let you use CompareTo
within the implementation of your Get
method.
However, since the lambda you pass to FirstOrDefault
will actually be executed on the database side, I'm not sure what will happen, ie whether EF will be able to translate that to a SQL expression correctly.
public abstract class Entity<T> : IEntity<T>, IAuditableEntity,ISoftDeletable where T : struct, IComparable
public abstract class Repository<TEntity,TKey> : IRepository<TEntity, TKey> where TEntity :Entity<TKey> where TKey : struct, IComparable
You could simply use the IDbSet.Find
method, which takes one or more object
-typed key values.
public TEntity Get(TKey key)
{
return Set.Find(key);
}
This has the additional advantage that it's more efficient if the entity already exists in the DbContext
's local cache (since it avoids an unnecessary trip to the database).
However, I suspect you're going to want to do more than just retrieve individual items, so you'll probably run into the same problem later.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.