3

I have the following generic method inside my class that works as a repository pattern:

public DbSet<T> GetAll<T>() where T : class
{
  return dbContext.Set<T>();
}

Now, i would like to get a list of all entities in the database that belong to an entity class that implements a specific interface (IChangeTrackingEntity). So currently there are around 10 specific tables/classes that conform to this, but i don't want to add 10 hardcoded calls to these tables, so I would like to do it using reflection instead (it might also be that the classes that implement this interface change in the future and I don't want to have to remember to change here as well and make the code dependant on each other).

Example of code that works, but that i don't want:

var result = new List<IChangeTrackingEntity>();
using ( var repository = new DbRepository())
{
  result.AddRange( repository.GetAll<FirstTypeThatImplementsInterface>() );
  result.AddRange( repository.GetAll<SecondTypeThatImplementsInterface>() );
  result.AddRange( repository.GetAll<ThirdTypeThatImplementsInterface>() );
  result.AddRange( repository.GetAll<FourthTypeThatImplementsInterface>() );
  result.AddRange( repository.GetAll<FifthTypeThatImplementsInterface>() );
}
return result;

I am quite close, but I can't get the last part to work of casting the result of the Invoke back to the correct type. Waht i got currently is this:

var result = new List<IChangeTrackingEntity>();
var method = typeof (DbRepository).GetMethod("GetAll");
using ( var repository = new DbRepository())
{
  foreach (var p in typeof(AnchorDbContext).GetProperties())
  {
    if (p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>))
    {
      var pType =  p.PropertyType.GetGenericArguments()[0];
      if (pType.GetInterface("IChangeTrackingEntity") != null)
      {
        var genericMethod = method.MakeGenericMethod(new[] {pType});
        result.AddRange(genericMethod.Invoke(repository, null) as DbSet<IChangeTrackingEntity>);
      }
    }
  }
  return result;
}

The problem above it that the Invoke call return a object and I need to cast it to basically DbSet<pType>.

In my current code genericMethod.Invoke(repository, null) as DbSet<IChangeTrackingEntity> returns null indicating that I can't cast the return value as I want, so I need the specific return value type and not the interface I think.

Any idea of how to accomplish this?

2
  • Hm your code does not make sense to me. As implemented you are fetching all entities matching this interface from the database. Are you aware that this is a perf problem? Commented May 30, 2012 at 13:32
  • usr - Yes, I want to fetch all these entries from the database, as they are going to be mirrored in another database, but not all the other entries in the database. Commented May 30, 2012 at 13:36

3 Answers 3

3

Try casting to IEnumerable<IChangeTrackingEntity>. This should work due to co/contravariance.

Sign up to request clarification or add additional context in comments.

4 Comments

That did the trick. Seems I need to read up on covariance/contravariance. So it works in IEnumerable because it's an interface, but not on DbSet since it's not an interface? Thanks for your effort.
Has nothing to do with interfaces. It works on IEnumerable because you can only get elements out. A DbSet allows you to put stuff in including some random class happening to derive from IChangeTrackingEntity. That is why it's disallowed.
But isn't this due to covariance? I mean, I have class A that inherit B. Then I can't set DbSet<B> = DbSet<A>, but I can set IEnumerable<B> = IEnumerable<A>. How does that matter if I can just get elements out of the IEnumerable?
Yes it is due to covariance. I suggest you read a little on the topic. It comes up rarely in practice.
1

I don't know much about this specific issue, but you seem to be casting from DbSet<T> where T : IChangeTrackingEntity to DbSet<IChangeTrackingEntity>. This is called covariance or contravariance (I always get confused between them...) and it only works if DbSet<> is an interface. So, casting won't work here. Use an equivalent interface if you can, or make a generic method that accepts DbSet where T: IChangeTrackingEntity and returns DbSet<IChangeTrackingEntity> somehow. I'll try to work out how to do that, and post an answer, if no one has answered before me (unlikely on this site :P)

3 Comments

Yes, I guess this is a covariance/contravariance problem. The actual list returned on the first round of the foreach loop is DbSet<MobileUnit>, and even though MobileUnit implements IChangeTrackingEntity, the covariance (or contravariance, i get them confused to :) ) does not kick in, and the cast is deemed invalid.
Seems that you had the right idea, and usr confirmed it. It works fine when i cast to IEnumerable instead of DbSet because then covariance/contravariance kicks in. So now it works :D. Thanks for your effort as well.
I'm glad :) I'm afraid I didn't know what DbSet<> was :P
0

I'm thinking you need to see this question:

MethodInfo method = typeof(Sample).GetMethod("GenericMethod");
MethodInfo generic = method.MakeGenericMethod(myType);
generic.Invoke(this, null);

1 Comment

It's not the parameters in that is the problem, and those i have handled, it's the return type. The return type is DbSet<SomeType>, but I can't see how I can make that cast.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.