views:

201

answers:

3

I expose an IQueryable method from my business layer for use in other layers. I would like to execute a function against each of the items in the enumeration, once the query has executed down-level.

It seems like there should be an event that is raised after the query executes, so that I can then operate on the results from this common layer.

Something like:

public IQueryable<User> Query() 
{
    return _Repository.Query<User>().ForEachDelayed(u=> AppendData(u));
}

I want the ForEachDelayed function to return the IQueryable without executing the query. The idea is, once the query is executed, the results are passed through this delegate.

Is there something like this out there? If not, is there an event like "IQueryable.OnExecute" that I can subscribe to?

Any help would be awesome -- thanks!

EDIT:

I thought I had the answer with this:

var users = from u in _Repository.Query<User>()
            select AppendData(u);
return users;

But now, I get the following error:

Method 'AppendData(User)' has no supported translation to SQL.

I really need a delegate to run AFTER the query has executed.

+1  A: 

The Easy Way

Another option that is much simpler would be to append what you need using an Iterator, see the example below. The downside of this approach is that everything would have been pulled into memory while the iteration is performed so that AppendData is only executed on the returned data. This is similar to the option by @Chris but you are not forced to create a new Func and Select query every time you want to use the method.

static void Main(string[] args)
{
    var data = new List<int> { 1, 2, 3, 4, 5 }.AsQueryable().AppendData();

    Console.WriteLine("Before execute");

    Console.Write("{ ");
    foreach (var x in data)
       Console.Write("{0}, ", x);

    Console.WriteLine( "}");

    Console.WriteLine("After execute");

    Console.Read();
}

// Append extra data. From this point on everything is in memory
// This should be generic, i.e. AppendData<T>, but I an easy way to "append"
static IEnumerable<int> AppendData(this IEnumerable<int> data)
{
    Console.WriteLine("Adding");
    foreach (var x in data)            
        yield return x * 2;
}

The Hard Way

I do not believe there are any built in events like this but you could probably add your own by implementing your own IQueryable wrapper.

Here is a series on writing a complete IQueryable provider which will also show you where you could fit your wrapper, most likely around that IQueryProvider.

Good luck and Hope this helps.

smaclell
No worries, I totally missed it too and am clearly over complicating it.
smaclell
This is starting to look like the best answer -- just REALLY hoping I am missing something simple (for reals).
Logrythmik
@smaclell Thank you for this, it isn't exactly what I am looking for, but the closest yet!
Logrythmik
No worries. I am still unclear as to what exactly you are trying to accomplish. This is very similar to your original post except for returning the `IQueryable`. It runs the append data when after the query is executed when you first try to use the results. Could you perhaps clarify more?
smaclell
A: 

Right now you are using some ORM, correct? I'm pretty sure you can achieve this using the lambda expression form by explicitly using the IEnumerable version of select, instead of IQueryable.

var users = (_Repository.Query<User>() as IEnumerable<User>).Select(u => AppendDate(u)); 
return users; 

When the query is actually executed it will fetch all of the data returned by Query() and then do the select in memory.

Chris
Yes, but I need to return IQueryable as there may be more filters added down-line. I will give this a shot, but it seems to me like this would enumerate and therefor execute this query as a List All. I'll get back to ya.Thanks for the help!
Logrythmik
@Logrythmik: you can add whatever parts of the query you want to an IEnumerable, the only difference is that it will all be performed in memory (and quite probably less efficiently). If you want to be able to use an arbitrary function in the select, there is really no other choice. If you can express AppendData as a Linq expression, then you could do this with an IQueryable.
Chris
Yeah - that isn't what I am looking for. It would be great if there was a way to apply a POST Expression/Translation/Execution delegate to be performed on the results, rather than creating the results from a place that allows for further down-level querying. Is this making sense?
Logrythmik
Mmm that does not make sense to me ...
smaclell
+1  A: 

Have you had a look at ToExpandable()?

http://tomasp.net/blog/linq-expand.aspx

You could always call ToList() before executing your code - that will invoke the SQL, then you can run your code on the result set -

var users = from u in _Repository.Query<User>().ToList();

//this will invoke AppendData() for each u
var retVal = from u in users
            select AppendData(u);

return retVal;
Frank Tzanabetis
This is great stuff, but still not what I am looking for. I need to execute ACTUAL C# code against the results AFTER the SQL query. I doubt it exists, but it sure would come in handy --
Logrythmik
I've modified my answer above. If all you want is to execute code after the SQL, then all you need to do is invoke the SQL (Enumerating over it will do this - you can use ToList() for that), and then call AppendData() for each record in the result set.
Frank Tzanabetis
@Frank Thanks again, but you are still missing my original request. Perhaps I should rephrase the question.I need to append a delegate at this level. The call to "AppendData" is extremely expensive and I only want to call it on the RESULTS from the QUERYABLE that will be filtered and refined in consuming classes.So at this layer, I expose the "IQUERYABLE". I want to make sure that the results, AFTER further filtering are appended with data. Make sense?
Logrythmik