manu3d
manu3d

Reputation: 1081

How to swap the data source associated with a Linq query?

I have read in other answers how the paramters of a linq query can be changed at runtime. But is it possible to change the data source that the query browses through after the query has been created? Can perhaps the query be given an empty wrapper in which data sources can be plugged in and out, unbeknownst to the query?

For example, let's assume this situation:

// d1 is a dictionary
var keys =  from entry in d1
            where entry.Value < 5
            select entry.Key;

Now, let's assume rather than modifying d1 I want myQuery to stay the same, except I'd like it to process an entirely new dictionary, d2.

The reason for this is that I'd like to decouple who provides the query from who provides the data source. I.e. think the dictionaries as metadata associated with services and the query as the mean for the consumers of services to discover which set of services match their criteria. I need to apply the same query to the metadata of each service.

I guess one parallel that I'm (maybe erroneously) making is with Regular Expression: one can compile a regular expression and then apply it to any string. I'd like to do the same with queries and dictionaries.

Upvotes: 2

Views: 371

Answers (1)

Reed Copsey
Reed Copsey

Reputation: 564631

A LINQ query is really nothing other than a method call. As such, you can't "reassign" the object to which you've already called a method after it's been setup.

You could simulate this by creating a wrapper class that would allow you to change what actually gets enumerated, ie:

public class MutatingSource<T> : IEnumerable<T>
{
     public MutatingSource(IEnumerable<T> originalSource)
     {
         this.Source = originalSource;
     }

     public IEnumerable<T> Source { get; set; }

     IEnumerator IEnumerable.GetEnumerator()
     {
         return this.GetEnumerator();
     }

     public IEnumerator<T> GetEnumerator()
     {
        return Source.GetEnumerator();
     }
}

This would allow you to create the query, then "change your mind" after the fact, ie:

var someStrings = new List<string> { "One", "Two", "Three" };

var source = new MutatingSource<string>(someStrings);

// Build the query
var query = source.Where(i => i.Length < 4);

source.Source = new[] {"Foo", "Bar", "Baz", "Uh oh"};

foreach(var item in query)
    Console.WriteLine(item);

This will print Foo, Bar, and Baz (from the "changed" source items).


Edit in response to comments/edit:

I guess one parallel that I'm (maybe erroneously) making is with Regular Expression: one can compile a regular expression and then apply it to any string. I'd like to do the same with queries and dictionaries.

A query is not like a regular expression, in this case. The query is translated directly into a series of method calls, which will only work against the underlying source. As such, you can't change that source (there isn't a "query object", just the return value of a method call).

A better approach would be to move the query into a method, ie:

IEnumerable<TKey> QueryDictionary<TKey,TValue>(IDictionary<TKey,TValue> dictionary)
{
    var keys =  from entry in dictionary
        where entry.Value < 5
        select entry.Key;
    return keys;
}

You could then use this as needed:

var query1 = QueryDictionary(d1);
var query2 = QueryDictionary(d2);

Upvotes: 4

Related Questions