Introduction
I have a Client-Server architecture where the server stores its data in different databases using Entity Framework 5.0. Therefore, it has different DbContext classes. However, there are some entity types shared amongst them. So we can have:
public class MyFirstDataAccess : DbContext
{
//constructor omitted
public DbSet<SharedType> MyEntities { get; set; }
public DbSet<OtherType> MyOtherEntities { get; set; }
}
public class MySecondDataAccess : DbContext
{
public DbSet<SharedType> MyEntitiesWithSharedType { get; set; }
}
Of course, the actual entities are not shared.
The problem
Different parts of the program need only parts of the data. E.g. one part could use the entities of Type SharedType. So I created a generic interface that provides the data:
public interface IEntityProvider<TEntity> where TEntity : class
{
DbSet<TEntity> Elements { get; set; }
void Dispose();
int SaveChanges();
}
Implementing the interface in the DbContext I added the Elements property, which would replace e.g. the MyEntitiesWithSharedType property:
DbSet<SharedType> IEntityProvider<SharedType>.Elements { get; set; }
This actually works. Different parts of the program can now work with any DbContext as long as it provides the needed data.
I want to reuse some parts of the server in the client. Of course, the client does not have a DbContext, but retrieves its data via a WCF service from the server. Therefore, the DbSetcannot be used in the IEntityProvider interface, because this interface must be shared with the client. So I created another interface, which contains the DbSet's members I need:
public interface IEntityCache<TEntity> : IEnumerable<TEntity>, IQueryable<TEntity>
{
ObservableCollection<TEntity> Local { get; }
TEntity Add(TEntity entity);
TEntity Remove(TEntity entity);
}
The problem is to get the DbSet as an IEntityCache. It cannot be cast directly, because the DbSet does not implement the interface, although it contains all its members. I tried some different approaches, but neither really worked:
Attempt 1
Create a subclass of DbSet, which implements the needed interface (–> class adapter).
This does not work, because DbSet does not have any public or protected constructors. Therefore, the adapter class is not valid.
Attempt 2
Instead of using an adapter class, I tried to use an adapter interface, which derives from IDbSet and the necessary interfaces:
public ICustomDbSet<SharedType> MyEntities{ get; set; }
IEntityCache<SharedType> IEntityProvider<SharedType>.Elements { get { return MyEntities; } }
This does not work, because EntityFramework does not set the ICustomDbSet. Probably because the value it would set, is no IEntityCache.
Solution
While writing this question, I found a pretty good solution (which I will post as an answer). So why do I post this question, though? Firstly, I would like to know, if there are any other approaches that seem more reasonable and if my solution has some drawbacks. Secondly, the solution might help someone else. And thirdly, writing this question took a while and it would be a pity to throw it away 🙂
My solution is the consequent next step of the class adapter approaches: using an object adapter:
I used this adapter in my
DbContextas follows:This way, the
IEntityCachecan be implemented in a way that retrieves data from a service instead of a database. Furthermore, subparts of the program do not have to care about where their data come from.