By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
448,483 Members | 1,054 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 448,483 IT Pros & Developers. It's quick & easy.

Anonymous type and 'if' statements

P: n/a
Is there any way I can avoid the ugly and repetitive 'if' construct shown
below?

I can't refactor into a separate method because qryTrans is an anonymous type.

I don't think I can use a switch statement either.

It works fine but is not aesthetically pleasing!

private void btnSearch_Click(object sender, EventArgs e)
{
qryStart = dtmpickStart.Value;
qryEnd = dtmpickEnd.Value;

var qryTrans =
from trans in dataSet.Transaction // get data for all
transactions
where ((trans.T_Date >= qryStart) // between start and end
dates
&& (trans.T_Date <= qryEnd))
select new
{
trans.T_Date,
trans.T_PayeeId,
trans.T_Category,
trans.T_SubCategory,
trans.T_PayMethod,
trans.T_Amount
};

// if Payee and Category selected, get transactions for both
if ((chkbxPayee.Checked) && (chkbxCategory.Checked))
{
if (!string.IsNullOrEmpty(qryPayeeId))
qryTrans =
qryTrans.Where(trans =trans.T_PayeeId ==
qryPayeeId);
if (!string.IsNullOrEmpty(qryCatId))
qryTrans =
qryTrans.Where(trans =trans.T_Category == qryCatId);
}

// if Category selected, get transactions for the Category only
if (chkbxCategory.Checked)
{
if (!string.IsNullOrEmpty(qryCatId))
qryTrans =
qryTrans.Where(trans =trans.T_Category ==
qryCatId);
}

// if Payee selected, get transactions for the Payee only
if (chkbxPayee.Checked)
{
if (!string.IsNullOrEmpty(qryPayeeId))
qryTrans =
qryTrans.Where(trans =trans.T_PayeeId ==
qryPayeeId);
}

dgvSearchResults.DataSource = qryTrans.ToList();
}

Nov 11 '08 #1
Share this Question
Share on Google+
8 Replies


P: n/a
On Mon, 10 Nov 2008 20:15:00 -0800, Paolo
<Pa***@discussions.microsoft.comwrote:
Is there any way I can avoid the ugly and repetitive 'if' construct shown
below? [...]
Well, the most obvious improvement would be to remove your first if() test
(checking for a payee and category together), since the individual tests
will accomplish the same exact thing, and in fact do a redundant re-query
of the results.

Beyond that, I would just put all your conditions into the "where" clause
for your original query. For example:

where ((trans.T_Date >= qryStart) && (trans.T_Date <= qryEnd))
&& (!chkbxPayee.Checked || string.IsNullOrEmpty(qryPayeeId) ||
trans.T_PayeeId == qryPayeeId)
&& (!chkbxCategory.Checked || string.IsNullOrEmpty(qryCatId) ||
trans.T_Category == qryCatId)

If you're for some reason concerned about the minimal overhead of
examining the CheckBox and filter string states for each element in the
enumeration, you could factor those out of the query, using one of four
different lambda expressions in your where clause depending on the state
of those filters. But I think that that's probably more trouble than it's
worth.

Pete
Nov 11 '08 #2

P: n/a
Peter: thank you. Yes, I see now that the first 'if' is redundant and I will
remove it - that will 'unclutter' the code.

I'm not so sure about the big 'where' statement - I think doing that will
not aid readability.

Another question if I may. At the moment the results of the search are
displayed in a DataGridView (dgvSearchResults.DataSource =
qryTrans.ToList();) which is on the same Win form as the form which captures
the search criteria. My original thinking was to display the search result in
a DataGridView on a separate form.

What I'm not sure of is whether I can pass the results of the query to a
second form i.e. to make Form2.dgvSearchResults.DataSource =qryTrans.ToList();

Is this possible?

"Peter Duniho" wrote:
On Mon, 10 Nov 2008 20:15:00 -0800, Paolo
<Pa***@discussions.microsoft.comwrote:
Is there any way I can avoid the ugly and repetitive 'if' construct shown
below? [...]

Well, the most obvious improvement would be to remove your first if() test
(checking for a payee and category together), since the individual tests
will accomplish the same exact thing, and in fact do a redundant re-query
of the results.

Beyond that, I would just put all your conditions into the "where" clause
for your original query. For example:

where ((trans.T_Date >= qryStart) && (trans.T_Date <= qryEnd))
&& (!chkbxPayee.Checked || string.IsNullOrEmpty(qryPayeeId) ||
trans.T_PayeeId == qryPayeeId)
&& (!chkbxCategory.Checked || string.IsNullOrEmpty(qryCatId) ||
trans.T_Category == qryCatId)

If you're for some reason concerned about the minimal overhead of
examining the CheckBox and filter string states for each element in the
enumeration, you could factor those out of the query, using one of four
different lambda expressions in your where clause depending on the state
of those filters. But I think that that's probably more trouble than it's
worth.

Pete
Nov 11 '08 #3

P: n/a
On Mon, 10 Nov 2008 23:13:00 -0800, Paolo
<Pa***@discussions.microsoft.comwrote:
Peter: thank you. Yes, I see now that the first 'if' is redundant and I
will
remove it - that will 'unclutter' the code.

I'm not so sure about the big 'where' statement - I think doing that will
not aid readability.
I suppose that depends on how comfortable one is with boolean
expressions. I find it quite readable, much moreso than the procedural
if() statements.

That said, IMHO the biggest potential advantage isn't of readability, but
of performance. The code you posted enumerates through the current
results again for each additional filter. The change I suggest allows all
of the filtering to be done in a single enumeration of the original data.

For small data sets, this wouldn't matter, but for larger data sets it
could be significant.
Another question if I may. At the moment the results of the search are
displayed in a DataGridView (dgvSearchResults.DataSource =
qryTrans.ToList();) which is on the same Win form as the form which
captures
the search criteria. My original thinking was to display the search
result in
a DataGridView on a separate form.

What I'm not sure of is whether I can pass the results of the query to a
second form i.e. to make Form2.dgvSearchResults.DataSource
=qryTrans.ToList();

Is this possible?
I don't see why it wouldn't be, but without seeing the code you might use
to attempt that, it's impossible to say whether the specific approach you
have in mind would work. Generally speaking though, the query should not
have any specific ties to the original form, and so having some other form
use the results shouldn't be a problem.

Pete
Nov 11 '08 #4

P: n/a
Peter: I see what you are saying about extra/unnecessary iterations. My data
set is not going to be of 'industrial' proportions and, in that sense,
performance is not an issue. Nevertheless I do like to adhere to 'best
practice' or, at least, efficient coding practice so I will revisit that
'where' statement.

As far as using a second form is concerned I'd use something like this. I'm
struggling with how I can pass an anonymous type (qryTrans) as a parameter:

Form1 code:
private void searchbutton1_Click(object sender, EventArgs e)
{
Form2 frm2 = new Form2();
frm2.FormClosed += new FormClosedEventHandler(frm2_FormClosed);
frm2.Show();
this.Hide();

// pass query result to Form2
frm2.doSearch(transQry);
}

void frm2_FormClosed(object sender, FormClosedEventArgs e)
{
this.Show();
}

Form2 code:
public void doSearch(?????) // how express parameters for anonymous
type?
{
dgvSearchResults2.DataSource = qryTrans.ToList();
}

"Peter Duniho" wrote:
On Mon, 10 Nov 2008 23:13:00 -0800, Paolo
<Pa***@discussions.microsoft.comwrote:
Peter: thank you. Yes, I see now that the first 'if' is redundant and I
will
remove it - that will 'unclutter' the code.

I'm not so sure about the big 'where' statement - I think doing that will
not aid readability.

I suppose that depends on how comfortable one is with boolean
expressions. I find it quite readable, much moreso than the procedural
if() statements.

That said, IMHO the biggest potential advantage isn't of readability, but
of performance. The code you posted enumerates through the current
results again for each additional filter. The change I suggest allows all
of the filtering to be done in a single enumeration of the original data.

For small data sets, this wouldn't matter, but for larger data sets it
could be significant.
Another question if I may. At the moment the results of the search are
displayed in a DataGridView (dgvSearchResults.DataSource =
qryTrans.ToList();) which is on the same Win form as the form which
captures
the search criteria. My original thinking was to display the search
result in
a DataGridView on a separate form.

What I'm not sure of is whether I can pass the results of the query to a
second form i.e. to make Form2.dgvSearchResults.DataSource
=qryTrans.ToList();

Is this possible?

I don't see why it wouldn't be, but without seeing the code you might use
to attempt that, it's impossible to say whether the specific approach you
have in mind would work. Generally speaking though, the query should not
have any specific ties to the original form, and so having some other form
use the results shouldn't be a problem.

Pete
Nov 11 '08 #5

P: n/a
On Mon, 10 Nov 2008 23:48:01 -0800, Paolo
<Pa***@discussions.microsoft.comwrote:
[...]
As far as using a second form is concerned I'd use something like this.
I'm
struggling with how I can pass an anonymous type (qryTrans) as a
parameter:
You can't. The compiler only knows the anonymous type in the method where
it was declared.

Now, you should be able to return the query result as a non-generic
IEnumerable, and depending on how the other form is using it, that could
be enough. But if not, you should probably consider just not using an
anonymous type. It's simple enough to create an appropriate named type
that you can then reference in both classes. It would be possible to use
reflection, but that's an awful solution for something dealt with so
trivially through conventional techniques.

Pete
Nov 11 '08 #6

P: n/a
Pete: I thought as much. I suppose I could just transfer the query to the
second form and initiate it there when I 'show' that form.

As I'm still learning C#/LINQ, I'm not sure how I'd "return the query result
as a non-generic IEnumerable". Could you give me an example?

Thanks

"Peter Duniho" wrote:
On Mon, 10 Nov 2008 23:48:01 -0800, Paolo
<Pa***@discussions.microsoft.comwrote:
[...]
As far as using a second form is concerned I'd use something like this.
I'm
struggling with how I can pass an anonymous type (qryTrans) as a
parameter:

You can't. The compiler only knows the anonymous type in the method where
it was declared.

Now, you should be able to return the query result as a non-generic
IEnumerable, and depending on how the other form is using it, that could
be enough. But if not, you should probably consider just not using an
anonymous type. It's simple enough to create an appropriate named type
that you can then reference in both classes. It would be possible to use
reflection, but that's an awful solution for something dealt with so
trivially through conventional techniques.

Pete
Nov 11 '08 #7

P: n/a
On Tue, 11 Nov 2008 12:26:03 -0800, Paolo
<Pa***@discussions.microsoft.comwrote:
Pete: I thought as much. I suppose I could just transfer the query to the
second form and initiate it there when I 'show' that form.

As I'm still learning C#/LINQ, I'm not sure how I'd "return the query
result
as a non-generic IEnumerable". Could you give me an example?
Sure:

IEnumerable Method()
{
var result = ...; // some LINQ query

return result;
}

The whole point of LINQ queries is that they generate types that are
enumerable. As such, they implement IEnumerable as well as whatever
strongly-typed IEnumerable<Tis appropriate for the query (all
IEnumerable<Timplementors have to implement IEnumerable as well).

As I mentioned, this may or may not actually suit your needs. You still
won't have type information for the objects in the collection, and so
without reflection there's not much you can do with it. You can call
ToString(), and if the default output from ToString() for anonymous types
is okay for your purposes, you can even use that directly. Ironically, it
might be faster to call ToString() and parse the output instead of using
reflection...again, depending on what you're actually doing.

In other words, you'll be very limited in what you can do. But you can in
fact do it that way if you decide that works for you. Given that the
DataSource property isn't going to be able to use the compile-time typing
of your anonymous type anyway (i.e. internally it's going to have to use
reflection anyway), I suspect that this would work just fine. To get an
IList to assign to your DataSource, you'll probably have to do something
like this:

IEnumerable result = Method(); // a method that returns IEnumerable,
e.g. as above

dgv.DataSource = result.Cast<object>().ToList();

You can't call ToList<T>() on the non-generic IEnumerable, but you can
easily use the Cast<T>() extension method to convert to an
IEnumerable<objecton which you _can_ call ToList<T>().

Note that, depending on the design of your program, you might find it's
appropriate to pass a reference of your second form, or the DataGridView
instance itself, to the method that is actually generating the anonymous
type. That's a completely different approach that might work fine,
depending on what you're really trying to do here.

Pete
Nov 11 '08 #8

P: n/a
Pete: many thanks - that's brilliant. I'll need to cogitate on it a bit (as I
said, I'm still learning!) but I think, together with the various books I'm
using to learn C#/LINQ, I'll be able to develop a workable solution based on
your example.

Paul

"Peter Duniho" wrote:
On Tue, 11 Nov 2008 12:26:03 -0800, Paolo
<Pa***@discussions.microsoft.comwrote:
Pete: I thought as much. I suppose I could just transfer the query to the
second form and initiate it there when I 'show' that form.

As I'm still learning C#/LINQ, I'm not sure how I'd "return the query
result
as a non-generic IEnumerable". Could you give me an example?

Sure:

IEnumerable Method()
{
var result = ...; // some LINQ query

return result;
}

The whole point of LINQ queries is that they generate types that are
enumerable. As such, they implement IEnumerable as well as whatever
strongly-typed IEnumerable<Tis appropriate for the query (all
IEnumerable<Timplementors have to implement IEnumerable as well).

As I mentioned, this may or may not actually suit your needs. You still
won't have type information for the objects in the collection, and so
without reflection there's not much you can do with it. You can call
ToString(), and if the default output from ToString() for anonymous types
is okay for your purposes, you can even use that directly. Ironically, it
might be faster to call ToString() and parse the output instead of using
reflection...again, depending on what you're actually doing.

In other words, you'll be very limited in what you can do. But you can in
fact do it that way if you decide that works for you. Given that the
DataSource property isn't going to be able to use the compile-time typing
of your anonymous type anyway (i.e. internally it's going to have to use
reflection anyway), I suspect that this would work just fine. To get an
IList to assign to your DataSource, you'll probably have to do something
like this:

IEnumerable result = Method(); // a method that returns IEnumerable,
e.g. as above

dgv.DataSource = result.Cast<object>().ToList();

You can't call ToList<T>() on the non-generic IEnumerable, but you can
easily use the Cast<T>() extension method to convert to an
IEnumerable<objecton which you _can_ call ToList<T>().

Note that, depending on the design of your program, you might find it's
appropriate to pass a reference of your second form, or the DataGridView
instance itself, to the method that is actually generating the anonymous
type. That's a completely different approach that might work fine,
depending on what you're really trying to do here.

Pete
Nov 11 '08 #9

This discussion thread is closed

Replies have been disabled for this discussion.