let’s say I have an generic data access layer, that is supposed to support different implementations. To realize this I have an base interface IPeristenceService that defines all methods to access data inside the persistence tier. The interface could look something like this:
public interface IPersistence
{
TData GetData<TData, TKey>(TKey Key);
}
So the persistence service cannot create instances of objects that are only defined by their interfaces, so it is a good decision to restrict the implementation of the GetData-method:
public interface IPersistence
{
TData GetData<TData, TKey>(TKey Key) where TData : class, new();
}
This is pretty straightforward, because developers who are implementing this interface will not be able to compile their code when they try to return an interface pointer. The data inside the persistence layer needs to be represented by implemented objects, not by interfaces!
Now assume we are creating an persistence service, that persists data inside an relational database, represented by entities. All entities are inheriting from one base interface: IEntity. Passing non-entity objects must fail, because the persistence service can only persist entities. So we need to validate if the passed object instance inherits from IEntity. Using the where-notation this would be possible during compile-time, but simply implementing an interface like this:
public class DbPersistence : IPersistence
{
public TData IPersistence.GetData<TData, TKey>(TKey Key)
where TData : IEntity, class, new();
{
// fancy data access code
}
}
does not work, does it?!
Is there any way to realize what I am trying to do?
Thank you in advance 🙂
Type constraints on
TDataare inherited from the interface method you are implementing, that’s why it isn’t possible to modify them on the implementation. There are two typical solutions that I can think of:Move the
IEntityconstraint up to the interface definitionThis is appropriate in lots of cases because a certain amount of common ground between all persisted objects is both reasonable and desired.
Implement the original interface explicitly and a specialized interface publicly
This way you get to use your custom persistence layer as a more richly specified
IDbPersistencewhile at the same time retaining compatibility with those parts of the data access layer that only know aboutIPersistence.