If you own the consumer of IDbSet<T>
, which I assume that you do because you want to have access to FindAsync()
from within the consumer, then a simple solution is to create your own interface that includes IDbSet and contains whichever FindAsync()
method that you want to use:
public interface IAsyncDbSet<T> : IDbSet<T>
where T : class
{
Task<T> FindAsync(params Object[] keyValues);
}
This solves the problem of not having to cast to DbSet - which, by the way, blows away the abstractness benefit of contract coding. But this also introduces its own set of problems.
A better solution (imo) that requires a bit more work is to define an interface that contains only the members that you want to use in what would otherwise be your DbSet object, subclass DbSet while implementing the interface, then use that interface in your code:
public interface IMyAsyncDbSet<TEntity>
where TEntity : class
{
TEntity Add(TEntity entity);
TEntity Remove(TEntity entity);
// Copy other methods from IDbSet<T> as needed.
Task<Object> FindAsync(params Object[] keyValues);
}
public class MyDbSet<T> : DbSet<T>, IMyAsyncDbSet<T>
where T : class
{
}
This is an Adapter pattern, really. It decouples the interface that your code expects from the interface that Entity Framework provides. Right now, they are identical - which is why the implementation does nothing except inherit DbSet<T>
. But later on they might diverge. At that point you will still be able to use the latest DbSet without breaking your code.