Anonymous type and 'if' statements

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • =?Utf-8?B?UGFvbG8=?=

    Anonymous type and 'if' statements

    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.Va lue;
    qryEnd = dtmpickEnd.Valu e;

    var qryTrans =
    from trans in dataSet.Transac tion // 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_Categor y,
    trans.T_SubCate gory,
    trans.T_PayMeth od,
    trans.T_Amount
    };

    // if Payee and Category selected, get transactions for both
    if ((chkbxPayee.Ch ecked) && (chkbxCategory. Checked))
    {
    if (!string.IsNull OrEmpty(qryPaye eId))
    qryTrans =
    qryTrans.Where( trans =trans.T_PayeeI d ==
    qryPayeeId);
    if (!string.IsNull OrEmpty(qryCatI d))
    qryTrans =
    qryTrans.Where( trans =trans.T_Catego ry == qryCatId);
    }

    // if Category selected, get transactions for the Category only
    if (chkbxCategory. Checked)
    {
    if (!string.IsNull OrEmpty(qryCatI d))
    qryTrans =
    qryTrans.Where( trans =trans.T_Catego ry ==
    qryCatId);
    }

    // if Payee selected, get transactions for the Payee only
    if (chkbxPayee.Che cked)
    {
    if (!string.IsNull OrEmpty(qryPaye eId))
    qryTrans =
    qryTrans.Where( trans =trans.T_PayeeI d ==
    qryPayeeId);
    }

    dgvSearchResult s.DataSource = qryTrans.ToList ();
    }

  • Peter Duniho

    #2
    Re: Anonymous type and 'if' statements

    On Mon, 10 Nov 2008 20:15:00 -0800, Paolo
    <Paolo@discussi ons.microsoft.c omwrote:
    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.Ch ecked || string.IsNullOr Empty(qryPayeeI d) ||
    trans.T_PayeeId == qryPayeeId)
    && (!chkbxCategory .Checked || string.IsNullOr Empty(qryCatId) ||
    trans.T_Categor y == 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

    Comment

    • =?Utf-8?B?UGFvbG8=?=

      #3
      Re: Anonymous type and 'if' statements

      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 (dgvSearchResul ts.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.dgvSearch Results.DataSou rce =qryTrans.ToLis t();

      Is this possible?

      "Peter Duniho" wrote:
      On Mon, 10 Nov 2008 20:15:00 -0800, Paolo
      <Paolo@discussi ons.microsoft.c omwrote:
      >
      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.Ch ecked || string.IsNullOr Empty(qryPayeeI d) ||
      trans.T_PayeeId == qryPayeeId)
      && (!chkbxCategory .Checked || string.IsNullOr Empty(qryCatId) ||
      trans.T_Categor y == 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
      >

      Comment

      • Peter Duniho

        #4
        Re: Anonymous type and 'if' statements

        On Mon, 10 Nov 2008 23:13:00 -0800, Paolo
        <Paolo@discussi ons.microsoft.c omwrote:
        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 (dgvSearchResul ts.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.dgvSearch Results.DataSou rce
        =qryTrans.ToLis t();
        >
        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

        Comment

        • =?Utf-8?B?UGFvbG8=?=

          #5
          Re: Anonymous type and 'if' statements

          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_C lick(object sender, EventArgs e)
          {
          Form2 frm2 = new Form2();
          frm2.FormClosed += new FormClosedEvent Handler(frm2_Fo rmClosed);
          frm2.Show();
          this.Hide();

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

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

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

          "Peter Duniho" wrote:
          On Mon, 10 Nov 2008 23:13:00 -0800, Paolo
          <Paolo@discussi ons.microsoft.c omwrote:
          >
          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 (dgvSearchResul ts.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.dgvSearch Results.DataSou rce
          =qryTrans.ToLis t();

          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
          >

          Comment

          • Peter Duniho

            #6
            Re: Anonymous type and 'if' statements

            On Mon, 10 Nov 2008 23:48:01 -0800, Paolo
            <Paolo@discussi ons.microsoft.c omwrote:
            [...]
            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

            Comment

            • =?Utf-8?B?UGFvbG8=?=

              #7
              Re: Anonymous type and 'if' statements

              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
              <Paolo@discussi ons.microsoft.c omwrote:
              >
              [...]
              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
              >

              Comment

              • Peter Duniho

                #8
                Re: Anonymous type and 'if' statements

                On Tue, 11 Nov 2008 12:26:03 -0800, Paolo
                <Paolo@discussi ons.microsoft.c omwrote:
                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<Tim plementors 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...ag ain, 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<obj ect>().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<obj ecton 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

                Comment

                • =?Utf-8?B?UGFvbG8=?=

                  #9
                  Re: Anonymous type and 'if' statements

                  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
                  <Paolo@discussi ons.microsoft.c omwrote:
                  >
                  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<Tim plementors 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...ag ain, 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<obj ect>().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<obj ecton 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
                  >

                  Comment

                  Working...