如何通过EF Core动态查询方法加载嵌套实体?
Great question! The issue you’re hitting is because your custom Query method returns a non-generic IQueryable, while EF Core’s built-in Include is a generic extension method designed exclusively for IQueryable<T>. Since you’re working with dynamic entity names/types, you can’t use the strongly-typed Include overloads directly—but luckily, since you’re already using System.Linq.Dynamic.Core, there are straightforward solutions to fix this.
Solution 1: Use System.Linq.Dynamic.Core’s Built-In Dynamic Include
The System.Linq.Dynamic.Core library includes a non-generic Include extension made exactly for this scenario. It lets you specify navigation paths as strings, which is perfect for dynamic use cases.
First, ensure you have the System.Linq.Dynamic.Core NuGet package installed, and add the namespace to your code file:
using System.Linq.Dynamic.Core;
Then you can call Include directly on your non-generic IQueryable, passing string paths for nested entities:
// Load a single nested entity IQueryable dbSet = _db.Query(entityName); var queryWithInclude = dbSet.Include("YourNestedEntity"); // Load multi-level nested entities (e.g., Parent.Child.Grandchild) var queryWithDeepInclude = dbSet.Include("YourNestedEntity.SubNestedEntity"); // Load multiple nested entities at once var queryWithMultipleIncludes = dbSet .Include("FirstNestedEntity") .Include("SecondNestedEntity");
This is the simplest approach because it leverages the library you’re already using, with no extra custom code needed.
Solution 2: Create a Custom Non-Generic Include Extension
If you prefer not to rely on the Dynamic.Core extension, you can build your own using reflection to invoke EF Core’s generic Include method under the hood.
Here’s a reusable extension method:
using System.Reflection; using System.Linq.Expressions; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Query; public static class NonGenericQueryableExtensions { // Get EF Core's generic Include method via reflection private static readonly MethodInfo _genericIncludeMethod = typeof(EntityFrameworkQueryableExtensions) .GetMethods(BindingFlags.Public | BindingFlags.Static) .Where(m => m.Name == nameof(EntityFrameworkQueryableExtensions.Include) && m.GetParameters().Length == 2) .First(m => m.GetGenericArguments().Length == 2); public static IQueryable Include(this IQueryable source, string navigationPath) { if (source == null) throw new ArgumentNullException(nameof(source)); if (string.IsNullOrWhiteSpace(navigationPath)) throw new ArgumentNullException(nameof(navigationPath)); var entityType = source.ElementType; // Build the expression tree for the navigation path var parameter = Expression.Parameter(entityType, "entity"); var navigationExpression = navigationPath.Split('.') .Aggregate((Expression)parameter, Expression.PropertyOrField); var lambda = Expression.Lambda(navigationExpression, parameter); // Invoke the generic Include method var includeMethod = _genericIncludeMethod.MakeGenericMethod(entityType, navigationExpression.Type); return (IQueryable)includeMethod.Invoke(null, new object[] { source, lambda }); } // Overload for multiple includes public static IQueryable Include(this IQueryable source, params string[] navigationPaths) { foreach (var path in navigationPaths) { source = source.Include(path); } return source; } }
Use it just like the Dynamic.Core version:
var dbSet = _db.Query(entityName); var queryWithIncludes = dbSet.Include("NestedEntity", "NestedEntity.SubEntity");
Solution 3: Convert to Generic IQueryable<T> (If Type Is Known)
If you can resolve the entity type at runtime and use reflection to invoke a generic method, you can convert the non-generic IQueryable to IQueryable<T> and use the standard Include:
var entityType = _db.Model.FindEntityType(entityName).ClrType; var dbSet = _db.Query(entityType); // Use reflection to call Cast<T> and Include<T> var castMethod = typeof(Queryable).GetMethod(nameof(Queryable.Cast)).MakeGenericMethod(entityType); var genericQuery = (IQueryable)castMethod.Invoke(null, new object[] { dbSet }); var includeMethod = typeof(EntityFrameworkQueryableExtensions) .GetMethod(nameof(EntityFrameworkQueryableExtensions.Include)) .MakeGenericMethod(entityType, typeof(object)); // Build the expression for the navigation path var parameter = Expression.Parameter(entityType, "x"); var navigation = Expression.PropertyOrField(parameter, "NestedEntity"); var lambda = Expression.Lambda(navigation, parameter); var queryWithInclude = (IQueryable)includeMethod.Invoke(null, new object[] { genericQuery, lambda });
This is more verbose, so it’s only useful if you need fine-grained control over the expression tree.
Final Recommendation
Since you’re already using System.Linq.Dynamic.Core, Solution 1 is the best choice—it’s clean, maintainable, and integrates seamlessly with your existing dynamic query setup.
内容的提问来源于stack exchange,提问作者Guerrilla

