动态构建Linq查询指定表字段时出现ContainsGenericParameters错误
Hey there, let's walk through fixing that error in your dynamic LINQ code while keeping things safe from SQL injection (since you want to stick with LINQ, which is the right call here).
First, let's recap the error you're hitting:
Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true.
What's Causing This?
The problem comes down to how you're grabbing the Where method from System.Linq.Enumerable. When you use .Where(x => x.Name == "Where").First(), you're getting the generic method definition—this is the blueprint for the method, but it hasn't been told what type it should operate on (the T in Where<T>). These unbound generic methods have ContainsGenericParameters = true, which .NET blocks late-bound calls to for safety.
Fixed Code with Explanations
Here's the adjusted version of your code that fixes the error and keeps your dynamic filtering working:
public JsonResult GetRelationShips(string linkingTable, string idField, int[] ids) { // Get the DbSet property from your context (replace YourDbContext with your actual context type) var tableType = typeof(YourDbContext).GetProperty(linkingTable); var entityTable = tableType.GetValue(context) as IQueryable; // Grab the actual entity type (e.g., if linkingTable is "Orders", this is typeof(Order)) var entityType = tableType.PropertyType.GetGenericArguments()[0]; // Build the expression for checking if the ID is in our array var containsMethod = typeof(List<int>).GetMethod("Contains"); var entityParam = Expression.Parameter(entityType, "entity"); var idPropertyExpr = Expression.Property(entityParam, idField); var containsCall = Expression.Call(Expression.Constant(ids.ToList()), containsMethod, idPropertyExpr); // Create the lambda expression: entity => entity.IdField.Contains(ids) var lambdaType = typeof(Func<,>).MakeGenericType(entityType, typeof(bool)); var filterLambda = Expression.Lambda(lambdaType, containsCall, entityParam); // Get the correct generic version of the Where method for our entity type var whereMethod = typeof(System.Linq.Enumerable) .GetMethods() // Pick the overload that takes IEnumerable<T> and Func<T, bool> .Where(m => m.Name == "Where" && m.GetParameters().Length == 2) .First() // Bind the generic method to our specific entity type .MakeGenericMethod(entityType); // Execute the Where query (pass null since Enumerable is static) var results = whereMethod.Invoke(null, new object[] { entityTable, filterLambda }); return Json(results); }
Key Changes:
- Explicit Entity Type: We first extract the actual entity type from the DbSet property (the
TinDbSet<T>). This is critical for binding the genericWheremethod correctly. - Bound Generic Where Method: Instead of using the unbound generic definition, we call
MakeGenericMethod(entityType)to create a version ofWherethat knows exactly what type it's filtering. - Correct Invocation: For static methods like those in
Enumerable, we passnullas the first argument toInvoke(since there's no instance to target).
Why This Is Safe From SQL Injection
Since we're using expression trees to build our filter, Entity Framework will translate this logic into parameterized SQL automatically. No raw string concatenation means no risk of SQL injection—this aligns perfectly with your goal of using LINQ for safety.
内容的提问来源于stack exchange,提问作者Tod

