如何在LINQtoSQL结果上应用过滤器?

时间:2012-10-30 20:24:09

标签: c# linq-to-sql attributes system.reflection

使用ListBox控件,可以为其提供一个DataSource,命名一个DisplayMember和一个ValueMember,通过一些魔术,它将显示DataSource中的一个字段并返回一个选定的ValueMember。它可以使用linq-to-sql结果,甚至不知道它所提供的表格的任何具体信息。

反射和属性不是在做一些魔术吗?它是如何工作的! 我需要做类似的事情,但我不知道从哪里开始。我是LINQtoSQL的初学者。

这就是我想要做的。 我有一个我想要过滤的源表。源表可以是任何东西,但都来自某些DataContext。

var MySourceTable =
    from MyRecord in Context.GetTable<MySourceTable>()
    select new
    {
        Value = MyRecord.ID,
        Display = MyRecord.Name,
        FilterValue = MyRecord.Value
    };

在我的控制中,我希望能够在某个给定值上过滤MySourceTable。控件不知道使用了什么表(上例中的MySourceTable),控件只知道它应该使用的记录中的三个名称,ID,Name和Value。

过滤器查询应如下所示。

var MyTable
    from Record in MySourceTable
    where FilterValue == GivenValue
    select new
    {
        Value = Record.ID,
        Display = Record.Name,
    };

有人可以告诉我从哪里开始吗?

3 个答案:

答案 0 :(得分:1)

看起来你缺少的是查询的where条件。它应该是这样的:

var MyTable = 
    from Record in MySourceTable
    where Record.FilterValue == GivenValue
    select new
    {
        Value = Record.ID,
        Display = Record.Name,
    };

GivenValue可能是一个局部变量或属性,包含您想要比较FilterValue的任何内容。但FilterValue是您在创建MySourceTable的第一个查询中创建的匿名类型的属性。在第二个查询中,Record是该匿名类型的实例,您必须在查询的所有其他部分中使用该实例的引用来引用要检查where子句或选择select子句的实例。如果您只是将FilterValue放在那里,那么它就不知道您的意思了。

答案 1 :(得分:0)

我找到了一种方法,它起作用但不是一种完全令人满意的方法。

'问题'(与我原来的问题相比)是它不使用linq-to-sql来过滤源。但它确实有效,而且现在对我来说还不错。

回顾: 在高级模块中,我准备一个LINQtoSQL语句,这将导致一些IQueriable&lt; Object&gt;,实际上是一个IQueriable对象。此对象与三个名称一起提供给下级模块,一个用于ID,一个用于显示,一个用于过滤。 当我需要对源进行更多控制时,例如对于将导致巨大结果的未过滤数据或复杂的LINQtoSQL查询,我将使用委托函数。这将给我所有我想要的控制权。

在低级别我有一个IQueriable&lt; Object&gt;并且根本没有关于物体的知识,除了wat反射可以告诉我。我生成一个结果表,我想以控件特定的格式使用。 (记录类)。对于我的标准代码无法处理的任何更复杂的事情,我提供了一个委托,该委托必须产生一些对象列表,其中对象必须至少具有名为“Display”的属性和名为“Value”的属性。其他属性是可能的,但不会被使用。

这是我最终开始工作的解决方案:

public partial class MySelector : UserControl
{
    class Record
    {
        public object Display { get; set; }
        public object Value { get; set; }
    }

    ....

    public string MyDisplayMember { get; set; }
    public string MyValueMember { get; set; }
    public string MyExternalMember { get; set; }

    ....

    static Object Filter(MySelector sender, Object criterium)
    {
        IQueryable source = sender.MySource as IQueryable;
        if (source == null) return null;

        List<Record> result = new List<Record>();

        // drawback: this foreach loop will trigger a unfiltered SQL command.
        foreach (var record in source)
        {
            MethodInfo DisplayGetter = null;
            MethodInfo ValueGetter = null;
            bool AddRecord = false;

            foreach (PropertyInfo property in record.GetType().GetProperties())
            {
                if (property.Name == sender.MyDisplayMember) 
                {
                    DisplayGetter = property.GetGetMethod();
                }
                else if (property.Name == sender.MyValueMember)
                {
                    ValueGetter = property.GetGetMethod();
                }
                else if (property.Name == sender.MyExternalMember)
                {
                    MethodInfo ExternalGetter = property.GetGetMethod();
                    if (ExternalGetter == null)
                    {
                        break;
                    }
                    else
                    {
                        object external = ExternalGetter.Invoke(record, new object[] { });
                        AddRecord = external.Equals(criterium);
                        if (!AddRecord)
                        {
                            break;
                        }
                    }
                }
                if (AddRecord && (DisplayGetter != null) && (ValueGetter != null))
                {
                    break;
                }
            }
            if (AddRecord && (DisplayGetter != null) && (ValueGetter != null))
            {
                Record r = new Record();
                r.Display = (DisplayGetter == null) 
                    ? null 
                    : DisplayGetter.Invoke(record, new object[] { });
                r.Value = (ValueGetter == null) 
                    ? null 
                    : ValueGetter.Invoke(record, new object[] { });
                result.Add(r);
            }
        }
        return result;
    }
}

答案 2 :(得分:0)

我编写了一个过滤引擎,它将Property和Value作为字符串,并且能够将其用作where子句。

IQueryable<T> FilterFunction<T>(IQueryable<T> query)
{
    ParameterExpression p = Expression.Parameter(typeof(T), "notused");

    Expression<Func<T, bool>> wherePredicate =
      Expression.Lambda<Func<T, bool>>(
          Expression.Equal(
            Expression.Call(Expression.Property(p, FilterProperty), "ToString", new Type[0]),
            Expression.Constant(FilterValue)), p);

    return query.Where(wherePredicate);
}

您应该可以将Expression<Func<T, TResult>>以类似的方式传入query.Select()

如果我正确理解你的问题,我相信这会有效:

string DisplayProperty = "Name";
string ValueProperty = "ID";

IQueryable<Record> SelectRecordProperties<T>(IQueryable<T> query)
{
    ParameterExpression p = Expression.Parameter(typeof(T), "notused");

    MethodInfo ctorMethod = typeof(Record).GetMethod("Create");

    Expression<Func<T, Record>> selectPredicate =
      Expression.Lambda<Func<T, Record>>(
        Expression.Call(ctorMethod,
            Expression.PropertyOrField(p, DisplayProperty),
            Expression.PropertyOrField(p, ValueProperty)), p);

    return query.Select(selectPredicate);
}
class Record
{
    public static Record Create(string display, string value)
    {
        return new Record() { Display = display, Value = value };
    }
    public object Display { get; set; }
    public object Value { get; set; }
}

因此,为了您的全部功能,您需要结合这两个想法,以便您的过滤工作。

顺便说一句,有很多种方法可以为此构建表达式树,我在一个点找到了一些可以显示我认为的表达式树的工具,所以你可以手动编写linq查询和看看.Net如何构建表达式,然后修改此代码以基于它构建它以获得更高效的表达式树。