-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Closed
Milestone
Description
In the older version of EF you were able to do something like, which is useful for dynamic scenarios.
var query = db.CampaignCreatives.AsQueryable();
foreach (string include in includes)
{
query = query.Include(include);
};
Edited July-18 by @rowanmiller
Workaround
Here is some code that adds two string based overloads of Include.
- One allows you to do EF6 style string includes - such as
context.Blogs.Include("Posts")andcontext.Blogs.Include("Posts.Author.Photo"). - The second overload allows you to make use of the C# 6
nameoffeature when including multiple levelscontext.Blogs.Include(nameof(Post.Blog), nameof(Blog.Owner), nameof(Person.Photo))
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace ConsoleApplication5
{
public static class Extensions
{
private static MethodInfo _include = typeof(EntityFrameworkQueryableExtensions)
.GetMethod("Include");
private static MethodInfo _thenIncludeReference = typeof(EntityFrameworkQueryableExtensions)
.GetMethods()
.Where(m => m.Name == "ThenInclude")
.Single(m => m.Name == "ThenInclude" &&
m.GetParameters()
.Single(p => p.Name == "source")
.ParameterType
.GetGenericArguments()[1].Name != typeof(ICollection<>).Name);
private static MethodInfo _thenIncludeCollection = typeof(EntityFrameworkQueryableExtensions)
.GetMethods()
.Where(m => m.Name == "ThenInclude")
.Single(m => m.Name == "ThenInclude" &&
m.GetParameters()
.Single(p => p.Name == "source")
.ParameterType
.GetGenericArguments()[1].Name == typeof(ICollection<>).Name);
public static IQueryable<T> Include<T>(this IQueryable<T> query, string include)
{
return query.Include(include.Split('.'));
}
public static IQueryable<T> Include<T>(this IQueryable<T> query, params string[] include)
{
var currentType = query.ElementType;
var previousNavWasCollection = false;
for (int i = 0; i < include.Length; i++)
{
var navigationName = include[i];
var navigationProperty = currentType.GetProperty(navigationName);
if (navigationProperty == null)
{
throw new ArgumentException($"'{navigationName}' is not a valid property of '{currentType}'");
}
var includeMethod = i == 0
? _include.MakeGenericMethod(query.ElementType, navigationProperty.PropertyType)
: previousNavWasCollection
? _thenIncludeCollection.MakeGenericMethod(query.ElementType, currentType, navigationProperty.PropertyType)
: _thenIncludeReference.MakeGenericMethod(query.ElementType, currentType, navigationProperty.PropertyType);
var expressionParameter = Expression.Parameter(currentType);
var expression = Expression.Lambda(
Expression.Property(expressionParameter, navigationName),
expressionParameter);
query = (IQueryable<T>)includeMethod.Invoke(null, new object[] { query, expression });
if (navigationProperty.PropertyType.GetInterfaces().Any(x => x.Name == typeof(ICollection<>).Name))
{
previousNavWasCollection = true;
currentType = navigationProperty.PropertyType.GetGenericArguments().Single();
}
else
{
previousNavWasCollection = false;
currentType = navigationProperty.PropertyType;
}
}
return query;
}
}
}