How to implement generic GetById() where Id can be of various types

15,576

Solution 1

You should remove the constraint on TId from your Repository class

public abstract class Repository<TEntity, TId> : IRepository<TEntity, TId>
where TEntity : class, IEntity<TId>
{
    public virtual TEntity GetById(TId id)
    {
        return context.Set<TEntity>().Find(id);
    }
}

Solution 2

public interface IEntity<TId> where TId : class
{
    TId Id { get; set; }
}

The where TId : class constraint requires every implementation to have a Id which derives from object which is not true for value types like int.

Thats what the error message tells you: The type 'int' must be a reference type in order to use it as parameter 'TId' in the generic type of method IEntity

Just remove the constraint where TId : class from IEntity<TId>

Solution 3

To your question:
I am trying to implement a generic GetById(T id) method which will cater for types which may have differing ID types. In my example, I have an entity which has an ID of type int, and one of type string.

    public virtual TEntity GetById<TId>(TId id)
    {
        return context.Set<TEntity>().SingleOrDefault(x => x.Id == id);
    }

For generic parameter, just make a generic method like above

Share:
15,576

Related videos on Youtube

Tomuke
Author by

Tomuke

Updated on June 22, 2022

Comments

  • Tomuke
    Tomuke almost 2 years

    I am trying to implement a generic GetById(T id) method which will cater for types which may have differing ID types. In my example, I have an entity which has an ID of type int, and one of type string.

    However, I keep getting an error and I have no idea why:

    The type 'int' must be a reference type in order to use it as parameter 'TId' in the generic type of method IEntity

    Entity Interface:

    To cater for my domain models which can have Id's of type int or string.

    public interface IEntity<TId> where TId : class
    {
        TId Id { get; set; }
    }
    

    Entity Implementations:

    public class EntityOne : IEntity<int>
    {
        public int Id { get; set; }
    
        // Other model properties...
    }
    
    public class EntityTwo : IEntity<string>
    {
        public string Id { get; set; }
    
        // Other model properties...
    }
    

    Generic Repository Interface:

    public interface IRepository<TEntity, TId> where TEntity : class, IEntity<TId>
    {
        TEntity GetById(TId id);
    }
    

    Generic Repository Implementation:

    public abstract class Repository<TEntity, TId> : IRepository<TEntity, TId>
        where TEntity : class, IEntity<TId>
        where TId : class
    {
        // Context setup...
    
        public virtual TEntity GetById(TId id)
        {
            return context.Set<TEntity>().SingleOrDefault(x => x.Id == id);
        }
    }
    

    Repository Implementations:

     public class EntityOneRepository : Repository<EntityOne, int>
        {
            // Initialise...
        }
    
        public class EntityTwoRepository : Repository<EntityTwo, string>
        {
            // Initialise...
        }
    
    • D Stanley
      D Stanley about 8 years
      Why are you restricting TId to class types? That won't work with int (the error message makes that pretty clear). class means "reference type" in generic constraints.
  • Tomuke
    Tomuke about 8 years
    This suggestion resolves the reference type error, however, I am now getting an Operator == cannot be applied to operands of type TId and TId error when trying to perform the match by Id
  • myhrmans
    myhrmans about 6 years
    for the .SingleOrDefault(x=>x.Id == id) error, you could also use .SingleOrDefault(x=>x.Id.Equals(id))