【问题标题】:Dynamic Func/Lambdas动态函数/Lambda
【发布时间】:2017-02-15 01:36:43
【问题描述】:

我有一个类似这样的方法,它遍历一组数据并使用第一个对象的值在第二组数据中找到一个对象:

private void someMethod(IQueryable<RemoteUser> source, IQueryable<LocalUser> targetData) {

    // Loop all records in source data
    foreach(var u in source) {

        // Get keyvalue from source data and use it to find the matching record in targetData
        var keyValue = u.id;
        var object = from data.Where(o => o.id == keyValue).FirstOrDefault();    
        ...
    }

}

我想通过传入 Func 或使用其他类型的 lambda 使其更可重用,然后将该方法转换为我可以以通用方式使用的方法,即:

private void someMethod<SourceT, TargetT>(IQueryable<SourceT> source, IQueryable<TargetT> targetData) {
    ....
}

我不太确定如何构建 Func/Predicate/etc 并将其传递给方法。请记住,“id”属性在所有 SourceT 和 TargetT 属性中都不相同。

为了进一步解释,我想要一些可以做到这一点的东西:

someMethod(RemoteUsers, LocalUsers, something here to say 'find the user using the userId property');

someMethod(RemoteProducts, LocalProducts, something here to say 'find the user using the productId property');

【问题讨论】:

    标签: c#-4.0 lambda


    【解决方案1】:

    这是someMethod 例程的最基本实现:

    private void someMethod<S, T, P>(
        IQueryable<S> source,
        IQueryable<T> target,
        Func<S, P> sourceSelector,
        Func<T, P> targetSelector)
    {
        foreach(var s in source)
        {
            var sp = sourceSelector(s);
            var @object = target
                .Where(t => targetSelector(t).Equals(sp)).FirstOrDefault();    
            //...
        }
    }
    

    此实现保留了原始代码的结构,但这是有代价的。您正在有效地对您的数据库进行source.Count() * target.Count() 查询。在使用IQueryable&lt;&gt; 时,您需要放弃使用foreach

    事实上,每当您开始使用 foreach 编写代码时,您都需要问自己是否可以使用 LINQ 查询来构建和过滤数据,并使 foreach 循环只执行“最简单”的任务。

    以下是如何使该方法更好地工作:

    private void someMethod2<S, T, P>(
        IQueryable<S> source,
        IQueryable<T> target,
        Expression<Func<S, P>> sourceSelector,
        Expression<Func<T, P>> targetSelector)
    {
        var query = source
            .GroupJoin(
                target,
                sourceSelector,
                targetSelector,
                (s, ts) => ts.FirstOrDefault());
    
        foreach(var @object in query)
        {   
            //...
        }
    }
    

    注意Expression&lt;Func&lt;,&gt;&gt; 的使用,而不仅仅是Func&lt;,&gt;。还要注意GroupJoin 方法调用。

    【讨论】:

    • 我的源和目标 IQueryable 数据不是来自同一个数据源,事实上,源甚至根本不是数据库——它来自 Web 服务。对 someMethod 的调用看起来如何(P 代表什么?)
    • @Chu - 由于它们来自不同的来源,您不需要使用 IQueryable&lt;&gt; 和表达式。我仍然会使用GroupJoin 方法并尽可能保持foreach 的轻便。 P 只是您要加入的属性(或更确切地说是它们的类型)。
    • 感谢您的澄清。只是为了我理解选择器部分,我应该将什么传递给 someMethod 作为我的sourceSelector
    • @Chu - 在您的问题中,您加入了id 属性。就是这样。someMethod(source, target, u =&gt; u.id, o =&gt; o.id)
    • 只是想指出此逻辑适用于实体框架查询,但不适用于 OData 查询。 OData 简单地将其翻译为 filter=true...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多