2

使用 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、名称和值。

过滤器查询应该类似于下面的示例。

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

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

4

3 回答 3

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 放在那里,那么它不知道您的意思。

于 2012-10-30T22:10:14.583 回答
0

我编写了一个过滤器引擎,它将属性和值作为字符串接收,并且能够将其用作 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 构建表达式,然后修改此代码以在此基础上构建它,以获得更有效的表达式树。

于 2012-11-01T20:51:04.387 回答
0

我找到了一种方法,它有效,但不是一个完全令人满意的方法。

“问题”(与我原来的问题相比)是它不使用 linq-to-sql 来过滤源。但它有效,目前对我来说很好。

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

在低级别,我有一个 IQueriable<Object> 并且对对象一无所知,除了 wat Reflection 可以告诉我。我生成了一个我想以特定于控件的格式使用的结果表。(记录类)。对于我的标准代码无法处理的更复杂的事情,我提供了一个委托,该委托必须产生一些对象列表,其中对象必须至少具有一个名为“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;
    }
}
于 2012-11-01T20:00:08.533 回答