Brook Must
Brook Must

Reputation: 33

Iterating all Entity Framework DbSets that implement an Interface using reflection

Some of my Entity Framework classes implement a certain interface, say IHasYearColumn, which means the table has a year column (int).

I would like to iterate over all of the tables with year columns and do some work.

I managed to get the relevant types using:

 var typesWithYear = AppDomain.CurrentDomain.GetAssemblies()
 .SelectMany(a=>a.GetTypes())
 .Where(t=>typeof(IHasYearColumn).IsAssignableFrom(t);

I verified this works, and indeed I get all the types I want.

Now, I would like to iterate over the respective table get the year values from all the records:

foreach (t in typesWithYear)
   myDbEntites.Set<t>().Select(x=>x.Year);

This obviously won't compile since t is not recognized as IHasYearColumn. So, I tried casting all types in typesWithYear into IHasYearColumn but this would not help. I got an InvalidCastException.

Any ideas will be most welcomed.

Edit Found the answer here.

Upvotes: 1

Views: 1248

Answers (3)

Ivan Koshelev
Ivan Koshelev

Reputation: 4280

Here is a solution that worked for me. IHaveSpecificIdNo is my analog to the initial questions IHasYearColumn

    var currentSpecificId = "123";

var DataContext = this;
var setProps = DataContext
            .GetType()
            .GetProperties(BindingFlags.Public | BindingFlags.Instance)
            .Where(x => 
            {
                return x.PropertyType.IsGenericType 
                //todo better check
                &&  x.PropertyType.Name.StartsWith("DbSet");
            })
            .ToArray();


            foreach (var setProperty in setProps)
            {
                var entityType = setProperty.PropertyType.GetGenericArguments().First();
                var set = DataContext.Set(entityType);

                if (typeof (IHaveSpecificIdNo).IsAssignableFrom(entityType))
                {
                    var items = ((IQueryable<IHaveSpecificIdNo>)set)
                                    .Where(x => x.SpecificIdNo == currentSpecificId).ToArray();
                    if(items.Length == 0)
                    {
                        continue;
                    }
                    //do something
                }
            }

Upvotes: 1

GDS
GDS

Reputation: 1387

This might work:

foreach (t in typesWithYear)    
  myDbEntites.Set(t).AsQueryable().Cast<IHasYearColumn>().Select(x=>x.Year);

Upvotes: 0

user2160375
user2160375

Reputation:

First of all, your code for getting types include interface itself, so you need to add additional predicate:

 var typesWithYear = AppDomain.CurrentDomain
                              .GetAssemblies()
                              .SelectMany(a=>a.GetTypes())
                              .Where(t=>typeof(IHasYearColumn)
                                          .IsAssignableFrom(t))
                              .Where(t => t.IsClass && !t.IsAbstract);

Next, use non-generic method for getting dbSet:

foreach (var type in typesWithYear)
{
    var set = myDbEntites.Set(type);

}

What's more, I'm not sure that EF will manage cast to an interface in order to produce SQL. If no (you can try it like GDS proposed), you can use Dynamic LINQ:

foreach (var type in typesWithYear)
{
    var set = myDbEntites.Set(type);
    yield return ((IQueryable)set).Select("Year")
                                  .Cast<int>(); // or sth else, your choice
}

EF supports casting to entity models or primitive type for sure.

Upvotes: 0

Related Questions