反射Linq实体"IN()";用于“包含"功能的功能包括:搜索整数组 [英] Reflection Linq-Entity "IN()" function for "contains" searching group of integers
问题描述
我正在为某些类型的搜索构建自己的反射函数.
I'm building my own reflection functions for certain types of searches.
问题是我想在ID列表中搜索一组ID,并过滤搜索/选择查询以仅具有这些特定对象.
The problem is that I want to search a group of IDs within a list of IDs and filter my search/select query to have only these specific objects.
这与在Linq-Entity框架中使用"IN()"相同.但是我不能使用a.objid.
return query.Where(a => ObjectsToFind.Contains(a.objid));
但是,因为我使用T模板,所以"a.objid"导致了错误.
However, "a.objid" is causing errors because I use T Template.
所以a是"T a"而不是"MyTable a",因此我可以将其称为"objid"属性.
So a is "T a" instead of "MyTable a" so that I can call it's "objid" property.
我知道有一种使用参数表达式执行此操作的方法.但是,我无法弄清楚.
I know there is a way to do this with parameter expressions. However, I can't figure it out.
这是我尝试将上面的内容替换为:
Here's what I tried to replace that above line with:
public static IQueryable<T> WhereFunctionContains<T>(this IQueryable<T> query, string contains)
{
var ObjectsToFind = new List<int>(); // I want to search IN() this function that gets filled in here.
ObjectsToFind = FillObjectsToFind(); // just the object id integers I want to filter
var parameter = Expression.Parameter(typeof(T), "type");
var propertyExpression = Expression.Property(parameter, "objid"); // I look for this
MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(int) }); // int type
var vars = Expression.Variable(List<int>,); // I can't use "constant", but what do I use here?
var containsExpression = Expression.Call(propertyExpression, method, vars);
return query.Where(Expression.Lambda<Func<T, bool>>(containsExpression, parameter));
}
用实际的表实体替换"T"会引起很多问题,因此我决定保留T.
Replacing "T" with the actual table entity, causes a lot of problems, so I decided to keep the T.
else if (s.ST == "function")
{ // search func
myTable a;
DC2 = MyUtility.WhereFunctionContains(DC2, a => a.objid, s.S);
// s.S is my search query string.
// s.ST means I'm searching for functions (from another associated table)
// I don't understand how a.objid gets called, I wanted to use Template/Reflections.
}
这就是我所谓的Zev函数的方式.
Here's how I call Zev's function.
public static IQueryable<T> WhereFunctionContains<T>(this IQueryable<T> query, Expression<Func<T, int>> converter, string contains)
{
FunctionsClass fc = new FunctionsClass();
var ObjectsToFind = new List<int>();
ObjectsToFind = fc.SearchContainFunction(contains); // I grabbed my list of IDs to search
return query.Where(t => ObjectsToFind.Contains(converter(t)));
}
推荐答案
如果我对您的理解是正确的,那么您会有许多关于不同类型的查询:
If I understand you correctly, you have a number of queries on different types:
IQueryable<Person> personsQry = ...
IQueryable<Sale> salesQry = ...
IQueryable<Order> ordersQry = ...
,您有一个生成List<int>
的方法,称为FillObjectsToFind
:
and you have a method that generates a List<int>
, called FillObjectsToFind
:
public List<int> FillObjectsToFind()
{
//code here
}
您希望能够将上述每个查询限制为仅在返回列表中包含ID.另外,这应该是扩展方法,因此您可以这样称呼它:
You want to be able to limit each of the above queries to only have the id in the returned list. As an added bonus, this should be an extension method, so you can call it like this:
var personsFiltered = personsQry.WhereFunctionContains(...);
var salesFiltered = salesQry.WhereFunctionContains(...);
var ordersFiltered = ordersQry.WhereFunctionContains(...);
问题是每个查询属于单独的类型,您可能希望编写一种涵盖所有查询的方法.
解决方案的第一部分是定义一个通用方法:
The problem is each query is of a separate type, and you would prefer to write one method that covers all of them.
The first part of the solution is to define a generic method:
public static IQueryable<T> WhereFunctionContains<T>(this IQueryable<T> query)
{
//code here
}
,但是仍然存在一个问题:我们知道的唯一类型是类型T
,它不是真实类型,而是实际类型的占位符.由于这些类型可以是任何类型-string
,System.Random
,SqlConnection
,ASP.NET Label
,WPF TextBlock
-无法知道如何将每个对象与
最直接的解决方案是定义界面:
but there is still a problem: the only type we know of is type T
which is not a real type, but a placeholder for actual types. Since these types could be anything -- string
, System.Random
, SqlConnection
, an ASP.NET Label
, a WPF TextBlock
-- there is no way of knowing how to compare each object to a List
of ints
.
The most straightforward solution is to define an interface:
interface IHasObjID
{
int ObjID {get;set;}
}
然后每种类型都应实现此接口:
Then each type should implement this interface:
class Person : IHasObjID
{
int objID;
int ObjID
{
get {return objID;}
set {objID = value;}
}
}
//implement sales and orders similarly
完成后,您可以在方法允许的类型上定义约束.现在,该类型肯定具有ObjID
属性,我们可以对此进行查询:
Once that is done, you can define a constraint on the types allowed by the method. Now that the type definitely has an ObjID
property, we can query on that:
public static IQueryable<T> WhereFunctionContains<T>(this IQueryable<T> query) where T : IHasObjID
{
var intsToFind = FillObjectsToFind();
return query.Where(t => intsToFind.Contains(t.ObjID));
}
This is what King King was telling you in this comment.
public static IQueryable<T> WhereFunctionContains<T>(this IQueryable<T> query, Expression<Func<T,int>> converter)
{
var intsToFind = FillObjectsToFind();
return query.Where(t => intsToFind.Contains(converter(t)));
}
但是,我尚未测试此代码,并且由于我们正在使用Entity Framework和表达式,因此我怀疑仍然存在问题:不能在表达式内调用"表达式.
我想提出以上建议,但它不会与以下错误一起编译-'converter'是'variable',但像'method'一样使用.
However, I haven't tested this code, and since we are working with Entity Framework and expressions I suspect there is still an issue: An expression cannot be "called" within an expression.
I wanted to suggest the above, but it doesn't compile with the following error -- 'converter' is a 'variable' but used like a 'method'.
毕竟,使用Join
解决方案非常简单:
After all that, the solution is straightforward, using Join
:
public static IQueryable<T> WhereFunctionContains<T>(this IQueryable<T> query, Expression<Func<T,int>> converter)
{
var ints = new List<int>() { 1, 2, 3, 4, 5 };
return query.Join(ints,converter,i=>i,(t,i) => t);
}
要这样称呼:
var filteredPersons = query.WhereFunctionContains(p => p.PersonID);
如果仅用于单一类型
MyTable
:
If this is only used with a single type
MyTable
:
public static IQueryable<MyTable> WhereFunctionContains(this IQueryable<MyTable> query)
{
var ints = new List<int>() { 1, 2, 3, 4, 5 };
return query.Join(ints, mt=>mt.objid, i=>i, (t,i) => t);
}
C#编程指南中的某些链接(按答案):
Some links from the C# Programming Guide (in order of the answer):