SventoryMang
SventoryMang

Reputation: 10479

Reflection, get types that implement an interface, but don't implement another one

I've got two types of repository classes in my API:

Those with both IRepository and IReportRepository:

internal class BuildingRepository : IBuildingRepository
public interface IBuildingRepository : IRepository<Building>, IReportRepository<BuildingReport>

And those with just IRepository:

internal class AppointmentRepository : IAppointmentRepository
public interface IAppointmentRepository : IRepository<Appointment>

How can I return all repositories that only implement IRepository and not IReportRepository. I thought it would be something like this:

var repoInterfaceType = typeof(IRepository<>);
var reportRepo = typeof(IReportRepository<>);
var repoTypes = asm.GetTypes().Where(x =>
            !x.IsInterface && !x.IsAbstract && x.IsClass && 
            !x.IsGenericType && x.GetInterfaces().Any(y =>
                y.IsGenericType &&
                repoInterfaceType.IsAssignableFrom(y.GetGenericTypeDefinition()) && 
      !reportRepo.IsAssignableFrom(y.GetGenericTypeDefinition()))).ToList();

But it is still returning me both. What am I missing?

Upvotes: 1

Views: 69

Answers (2)

John Wu
John Wu

Reputation: 52250

You can greatly shorten the code using GetInterface(string):

var results = asm.GetTypes()
    .Where
    (
        t => t.GetInterface("IRepository`1") != null
          && t.GetInterface("IReportRepository`1") == null
    );

This LINQ will iterate over all types and attempt to retrieve the two interfaces of interest. If the interface can't be found, only null will be returned, so we just need to check that the right one is null and the other one isn't.

In case you are wondering where I got the string "IReportRepository`1", this is known as the mangled name, which is the string the CLR uses internally (you may recognize it from stack dumps). If you don't feel comfortable using a string literal, you can get it at runtime from the type's Name property, e.g.

var mangledName = typeof(IReportRepository<>).Name;

Upvotes: 1

Adem Catamak
Adem Catamak

Reputation: 2009

Your .Any(y => y.IsGenericType && repoInterfaceType.IsAssignableFrom(y.GetGenericTypeDefinition()) && !reportRepo.IsAssignableFrom(y.GetGenericTypeDefinition())) parts must be split two queries. Because, BuildingRepository class is implementation of IRepository<Building>. This interface is assignable from IRepository<> and not assignable from IReport<> interface. Your Any condition return true in this case.

Your Linq can be modified like:

var repoTypes = asm.GetTypes().Where(x =>
                                         !x.IsInterface && !x.IsAbstract && x.IsClass &&
                                         !x.IsGenericType &&
                                         x.GetInterfaces().All(y => !y.IsGenericType || !reportRepo.IsAssignableFrom(y.GetGenericTypeDefinition())) &&
                                         x.GetInterfaces().Any(y => y.IsGenericType &&
                                                                    repoInterfaceType.IsAssignableFrom(y.GetGenericTypeDefinition())))
                   .ToList();

Upvotes: 1

Related Questions