开发者

Dynamic Func/Lambdas

开发者 https://www.devze.com 2023-03-27 11:56 出处:网络
I\'ve got a method similar to this, which loops through one set of data and uses a value of the first object to find an object in a second set of data:

I've got a method similar to this, which loops through one set of data and uses a value of the first object to find an object in a second set of data:

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

}

I'd like to开发者_开发百科 make it more re-usable by passing in Func or using some other type of lambda and then convert the method to something I can use in a generic manner, i.e.:

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

What I'm not exactly sure on is how I can build a Func/Predicate/etc and pass it into the method. Keeping in mind that the "id" property will not be the same across all SourceT & TargetT properties.

To further explain, I'd like something where I can do this:

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


Here's the most basic implementation of your someMethod routine:

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();    
        //...
    }
}

This implementation keeps the structure of your original code, but this comes at a cost. You are effectively doing source.Count() * target.Count() queries against your database. You need to drop the use of foreach when working with IQueryable<>.

In fact, whenever you start writing code with foreach, you need to ask yourself if you can use a LINQ query to build and filter your data and make the foreach loop only do the "simplest" tasks.

Here's how to make the method work better:

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)
    {   
        //...
    }
}

Note the use of Expression<Func<,>> and not just Func<,>. Also note the GroupJoin method call.

0

精彩评论

暂无评论...
验证码 换一张
取 消