tags:

views:

88

answers:

2

Hey All, Im trying to query the Netflix OData feed. I have the following query that works fine in LinqPad:

from g in Genres
from t in g.Titles
where g.Name == "Horror" && t.AverageRating == 2 && t.ReleaseYear == 2004
select t

But, when I move it over to my Silverlight app, the user selects what to search on so I may or may not have all of the params. That being the case, I need to construct the query at runtime. Ive looked the the Dynamic Query stuff and that will do fine...the issue that I have is that I need an initial acceptable query to append to and this doesn't fly:

from g in Genres
from t in g.Titles
select t;

Any additional thoughts would be appreciated. Thanks in advance

+1  A: 

How about appending filters the old-style, non-dynamic way?

var genresQuery = from g in Genres select g;
if ( !string.IsNullOrEmpty( name ) ) genresQuery = genresQuery.Where( g => g.Name == name );

var titlesQuery = from t in genresQuery select t;
if ( !string.IsNullOrEmpty( rating ) ) titlesQuery = titlesQuery.Where( t => t.AverageRating == rating );
if ( !string.IsNullOrEmpty( year ) ) titlesQuery = titlesQuery.Where( t => t.ReleaseYear == year );
Fyodor Soikin
+1  A: 

You do not have to do this using dynamic query syntax, but you can.

You really have two queries taking place, so you should probably build them as such. You could do it all at once, but you are going to need two specific where clauses in different places either way, one for Genres and one for Titles. Consider the following example that mimics your situation.

class Foo
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<Bar> Bars { get; set; }
}

class Bar
{
    public string Color { get; set; }
}

...

List<Foo> foos = new List<Foo>()
{
    new Foo() { Id = 1, Name = "Apple", Bars = new List<Bar> () { new Bar() { Color = "Red"}, new Bar() { Color="Green"}} },
    new Foo() { Id = 2, Name = "Orange", Bars = new List<Bar> () { new Bar() { Color = "Orange"},new Bar() { Color="Red Orange"}} },
    new Foo() { Id = 3, Name = "Banana",Bars = new List<Bar> () { new Bar() { Color = "Yellow"},new Bar() { Color="Green"}} },
    new Foo() { Id = 4, Name = "Pear",Bars = new List<Bar> () { new Bar() { Color = "Green"},new Bar() { Color="Yellow"}} }
};

string fooName = "Apple";
string barColor = "Green";

var fooQuery = foos.AsQueryable();

if (!string.IsNullOrEmpty(fooName))
{
    string filter = string.Format("Name = \"{0}\"", fooName);
    fooQuery = fooQuery.Where(filter);
}

var barQuery = fooQuery.SelectMany(f => f.Bars);

if (!string.IsNullOrEmpty(barColor))
{
    string filter = string.Format("Color = \"{0}\"", barColor);
    barQuery = barQuery.Where(filter);
}

The first thing is that it builds a query for Foos. If it requires filtering (based upon the fooSearchName variable), it generates a Where clause and adds it to the query. Afterwards, it becomes the base for the query that returns Bar elements. Same thing, if it requires filtering, a Where clause is generated and added to the query.

Unfortunately, you can't build a single query over steps because of type inference being evaluated on the first step. If you were to say var query = foos.AsQueryable();, the query has already been evaluated to IQueryable<Foo>, so adding a SelectMany in a later statement to select Bars fails because that requires that query be an IQueryable<Bar>.

In that vein, your hand is forced and you must either build the query all at once, or at least to the portion up to and including SelectMany, the final Where clause can be appended later. So you could do this:

var query = foos.AsQueryable().Where(fooSearchClause).SelectMany(f => f.Bars);
// later
query = query.Where(barSearchClause);

Or you build the query as one that uses another, as my example shows.

At any rate, with the terms I've provided, iterating over final query yields one result. Altering the contents of either search variable will cause the query to yield different results.

Anthony Pegram
Anthony...here is what I ended up doing....thanks for the helpIQueryable<Title> query = Context.Genres.AsQueryable().Where(g => g.Name == qp.Genre).SelectMany(f => f.Titles); if (!string.IsNullOrEmpty(qp.Rating)) qry = qry.Where("AverageRating == @0", rating); if (!string.IsNullOrEmpty(qp.Year)) qry = qry.Where("ReleaseYear == @0", year);
Alex