0

I'm building my own reflection functions for certain types of searches.

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.

This is the same as using "IN()" in Linq-Entity framework. But I can't use a.objid.

return query.Where(a => ObjectsToFind.Contains(a.objid));

However, "a.objid" is causing errors because I use T Template.

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));
}

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.
}

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)));
}
4

1 回答 1

2

如果我对您的理解正确,您有许多关于不同类型的查询:

IQueryable<Person> personsQry = ...
IQueryable<Sale>   salesQry   = ...
IQueryable<Order>  ordersQry  = ...

并且您有一个生成 的方法List<int>,称为FillObjectsToFind

public List<int> FillObjectsToFind()
{
    //code here
}

您希望能够将上述每个查询限制为仅在返回列表中包含 id。作为一个额外的好处,这应该是一个扩展方法,所以你可以这样调用它:

var personsFiltered = personsQry.WhereFunctionContains(...);
var salesFiltered   = salesQry.WhereFunctionContains(...);
var ordersFiltered  = ordersQry.WhereFunctionContains(...);

问题是每个查询都是单独的类型,您更愿意编写一种方法来涵盖所有查询。


解决方案的第一部分是定义一个泛型方法:

public static IQueryable<T> WhereFunctionContains<T>(this IQueryable<T> query)
{
    //code here
}

但仍然存在一个问题:我们所知道的唯一类型是 type T,它不是真正的类型,而是实际类型的占位符。由于这些类型可以是任何东西—— string、、、ASP.NET System.Random、WPF——所以无法知道如何将每个对象与. SqlConnectionLabelTextBlockListints


最直接的解决方案是定义一个接口

interface IHasObjID
{
    int ObjID {get;set;}
}

那么每种类型都应该实现这个接口:

class Person : IHasObjID
{
    int objID;
    int ObjID
    {
        get {return objID;}
        set {objID = value;}
    }
}

//implement sales and orders similarly

完成后,您可以对方法允许的类型定义约束。现在该类型肯定有一个ObjID属性,我们可以查询它:

public static IQueryable<T> WhereFunctionContains<T>(this IQueryable<T> query) where T : IHasObjID
{
    var intsToFind = FillObjectsToFind();
    return query.Where(t => intsToFind.Contains(t.ObjID));
}

这就是King King这篇评论中告诉你的。


我建议在调用这个函数时,你还要传入如何从类型中获取整数:

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)));
}

但是,我还没有测试过这段代码,因为我们正在使用实体框架和表达式,我怀疑仍然存在一个问题:不能在表达式中“调用”表达式。 我想提出上述建议,但编译时出现以下错误 - 'converter' is a 'variable' but used like a 'method'

毕竟,解决方案很简单,使用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

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# Programming Guide中 的一些链接(按答案顺序):

此外,请参阅此处了解 LINQ 运算符(例如SelectWhere和.CountToList

于 2013-09-16T19:25:50.583 回答