views:

82

answers:

5

I have a multi-select checkbox. Depending on which one is checked, I want to combine the results into a single query. Sort of like:

if (Checkbox1.Checked)
{
    var query1 = from t in table1 ...
}

if (Checkbox2.Checked)
{
    var query2 = from t in table2 ...
}

DataGridView1.DataSource = query1.Union(query2); // obviously doesnt
      // work since query1 and query2 are not defined in this scope.

Any idea how to combine these selectively?

A: 

If you make sure that they return the same anonymous type, you can do

query1.Concat(query2)
or query1.Union(query2)

depending on the behavior you want.

If the two results have the exact same property names/types then the compiler will generate one type for both and these will work.

Mike
+2  A: 

Assuming the queries are of the same type, you could define the queries outside of the conditional statements.

First, a helper method that creates an empty enumerable of the same type as the parameter:

static IEnumerable<T> CreateEmptyEnumerable<T>(IEnumerable<T> templateQuery)
{
    return Enumerable.Empty<T>();
}

Then, the new code:

var query1 = from t in table1 ...
var query2 = from t in table2 ...
var finalQuery = CreateEmptyEnumerable(query1);

if (Checkbox1.Checked)
{
    finalQuery = query1;
}

if (Checkbox2.Checked)
{
    finalQuery = finalQuery.Union(query2);
}

DataGridView1.DataSource = finalQuery.ToList(); // to avoid re-enumeration

This performs just fine because the queries aren't actually executed until they're enumerated over, as in the call to ToList().

Ben M
You can't assign null to an implicitly-typed local variable.
esac
Ben M - I remember that now, but I still can't do "var finalQuery = null;"
esac
I'm basing my answer on the premise that your query enumerates over an anonymous type. If not, you could just replace `var` with `IEnumerable<QueryType>`.
Ben M
yes, it is from an anonymous type. Your example is close, but still one issue. In the second checkbox, if finalQuery is null, you can't call finalQuery.Union(query2), so you need another if statement.
esac
Man. That'll teach me to do two things at once. I've added a helper function that makes things a lot cleaner -- it returns an empty enumeration of the type (in this case anonymous) of the specified query. So, no more null checks.
Ben M
For what it's worth, you *can* do this: `var foo = (SomeType)null;`
Daniel Schaffer
Thanks Ben. You more than deserve the points for your effort, it works flawlessly for my needs.
esac
Glad to be of help!
Ben M
A: 

If you would not use var but a known type, then you could initialize query1 and query2 via

Enumerable.Empty<T> in case of the checkbox not being checked.

Frank
A: 

I just tried this in a console application, as I was curious, and it just worked....

class Program
{
    private class Data1
    {
        public int Code1 { get; set; }
        public string Value1 { get; set; }
    }

    private class Data2
    {
        public int Code2 { get; set; }
        public string Value2 { get; set; }
    }

    static void Main(string[] args)
    {
        var data1 = new[] { new Data1 { Code1 = 1, Value1 = "one" }, new Data1 { Code1 = 2, Value1 = "two" }, new Data1 { Code1 = 3, Value1 = "three" } };
        var data2 = new[] { new Data2 { Code2 = 101, Value2 = "aaa" }, new Data2 { Code2 = 102, Value2 = "bbb" }, new Data2 { Code2 = 103, Value2 = "ccc" } };

        var query1 = from d1 in data1 select new { code = d1.Code1, text = d1.Value1 };
        var query2 = from d2 in data2 select new { code = d2.Code2, text = d2.Value2 };

        var datasource = query1.Union(query2);

        foreach (var d in datasource)
        {
            Console.WriteLine(d);
        }
    }
}

However, if I change the fieldnames in the anonymous types to be different I get a compiler error, so it looks like the key is to have the names the same in your anonymous types. Or just have the result in a defined type :)

Antony Scott
hmm, have i missed what the problem is?!
Antony Scott
Yeah, I already know that Union works. The issue is that my anonymous types were declared in the if statements, which makes it so that you can't do Union outside of the if statements.
esac
if the anonymous types are the same, why not just make a private class and use that?
Antony Scott
A: 

Using Rx you can do something like this:

public partial class _Default : System.Web.UI.Page
{
    IEnumerable<int> table1;
    IEnumerable<int> table2;

    protected void Page_Load(object sender, EventArgs e)
    {
        table1 = Enumerable.Range(0, 10);
        table2 = Enumerable.Range(10, 10);
    }

    protected void Button1_Click(object sender, EventArgs e)
    {
        var query = 
            Observable.If(() => CheckBox1.Checked,
                (from t in table1 select t).ToObservable(), Observable.Empty<int>())
            .Concat(
                Observable.If(() => CheckBox2.Checked,
                (from t in table2 select t).ToObservable(), Observable.Empty<int>())
            );

        query.Subscribe(i => Response.Write(i));
    }
}
Richard Hein