title: Generic class with Id c#
body: <p>I am not sure if this is possible, but I have started a new project and I am trying to clean up the way I work with the <strong>DbContext</strong>. I am trying to do this by using generics.
  The first part is easy. I create a <strong>Repository</strong> class like this:</p>
  
  <pre><code>public class Repository&lt;T&gt; where T : class
  {
      protected readonly DbContext _dbContext;
      protected readonly DbSet&lt;T&gt; _dbEntitySet;
  
      public Repository(InteractiveChoicesContext dbContext)
      {
          _dbContext = dbContext;
          _dbEntitySet = dbContext.Set&lt;T&gt;();
      }
  
      /// &lt;summary&gt;
      /// Gets all the entities
      /// &lt;/summary&gt;
      /// &lt;param name="includes"&gt;Option includes for eager loading&lt;/param&gt;
      /// &lt;returns&gt;&lt;/returns&gt;
      public IQueryable&lt;T&gt; List(params string[] includes)
      {
          IQueryable&lt;T&gt; query = _dbEntitySet;
          foreach (var include in includes)
              query = query.Include(include);
          return query;
      }
  
      /// &lt;summary&gt;
      ///     Creates an entity
      /// &lt;/summary&gt;
      /// &lt;param name="model"&gt;&lt;/param&gt;
      public void Create(T model) =&gt; _dbEntitySet.Add(model);
  
      /// &lt;summary&gt;
      ///     Updates an entity
      /// &lt;/summary&gt;
      /// &lt;param name="model"&gt;&lt;/param&gt;
      public void Update(T model) =&gt; _dbEntitySet.Update(model);
  
      /// &lt;summary&gt;
      /// Removes an entity
      /// &lt;/summary&gt;
      /// &lt;param name="model"&gt;&lt;/param&gt;
      public void Remove(T model) =&gt; _dbContext.Entry&lt;T&gt;(model).State = EntityState.Deleted;
  
      /// &lt;summary&gt;
      /// Saves the database context changes
      /// &lt;/summary&gt;
      /// &lt;returns&gt;&lt;/returns&gt;
      public async Task SaveChangesAsync()
      {
          await _dbContext.SaveChangesAsync();
      }
  </code></pre>
  
  <p>Now I want to create a generic <strong>Service</strong> class that can use all the CRUD methods. I tried to create it like this:</p>
  
  <pre><code>public class Service&lt;T&gt; : Service&lt;T, int&gt; where T : class
  {
      public Service(InteractiveChoicesContext dbContext) : base(dbContext)
      {
      }
  }
  
  public class Service&lt;T, TKey&gt;: Repository&lt;T&gt; where T: class
  {
      public Service(InteractiveChoicesContext dbContext) : base(dbContext)
      {
      }
      public virtual async Task&lt;IEnumerable&lt;T&gt;&gt; ListAsync() =&gt; await List().ToListAsync();
      public virtual async Task&lt;T&gt; GetAsync(TKey id) =&gt; await List().SingleOrDefaultAsync(m =&gt; m.Id == id);
      public virtual async Task&lt;T&gt; CreateAsync(T model)
      {
          Create(model);
          await SaveChangesAsync();
          return model;
      }
      public virtual async Task&lt;T&gt; UpdateAsync(T model)
      {
          Update(model);
          await SaveChangesAsync();
          return model;
      }
      public virtual async Task&lt;bool&gt; DeleteAsync(TKey id)
      {
          var model = await GetAsync(id);
          Remove(model);
          await SaveChangesAsync();
          return true;
      }
  }
  </code></pre>
  
  <p>I am using <strong>TKey</strong> to specify the primitive type, but the issue is with the <strong>GetAsync</strong> method. Obviously it doesn't know what the object is at this point, so it will not compile. I get this error:</p>
  
  <p>'T' does not contain a definition for 'Id' and no extension method 'Id' accepting a first argument of type 'T' could be found (are you missing a using directive or an assembly reference?)</p>
  
  <p>Now I know this is because I haven't said what <strong>T</strong> is.
  Looking at how <strong>IdentityFramework</strong> handle this, they use <strong>IUser</strong>, I could do the same, but it would mean ALL my entities would have to implement this interface.
  If I created an interface like this:</p>
  
  <pre><code>public interface IEntity&lt;out TKey&gt;
  {
      TKey Id { get; }
  }
  </code></pre>
  
  <p>And then updated all my entities to implement the interface like this:</p>
  
  <pre><code>public class Question : IEntity&lt;int&gt;
  {
      [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; }
      [Required, MaxLength(100)] public string Text { get; set; }
      public bool MultipleChoice { get; set; }
  
      public OutcomeGroup Group { get; set; }
      public IEnumerable&lt;Answer&gt; Answers { get; set; }
  }
  </code></pre>
  
  <p>That seems to work. I guess I only have one question. 
  Is this the best way to do it?</p>