Steve Moseley

"To err is human. To really screw up takes a computer." - Dilbert

Extension Methods

clock October 9, 2008 13:33 by author Steve

Extension Methods are new to Visual Studio 2008 and they basically let you bolt on additional functionality to the side of an existing type or function.  You can see the offical definition here.  This functionality is especially useful with LINQ which I will show in a sample of shortly, but first let's look at a simple sample to see how you can use it.

Basic Sample

I am going to create a simple console application that takes a number and doubles it, tripples it, splits it, and divides it into thirds.  With one line of code I am going to call all of these functions.  Below is the class that contains the Extension Methods.

    6 namespace ExtensionMethodSample

    7 {

    8     public static class Extension

    9     {

   10 

   11         public static decimal Double(this decimal amount)

   12         {

   13             return amount*2m;

   14         }

   15 

   16         public static decimal Tripple(this decimal amount)

   17         {

   18             return amount*3m;

   19         }

   20 

   21         public static decimal Split(this decimal amount)

   22         {

   23             return amount/2;

   24         }

   25 

   26         public static decimal Thirds(this decimal amount)

   27         {

   28             return amount/3;

   29         }

   30     }

   31 }

Notice that for the method to be an extension, you have to define it as a static method, and you have to define the first parameter as "this" which will be the type value or method return value you are bolting this extension method on to.  You can have more parameters following but the first parameter is always the calling instance.

I can then call the methods above like this:

    8     class Program

    9     {

   10         static void Main(string[] args)

   11         {

   12             var amount = 5m;

   13 

   14             var result = amount.Tripple().Split().Double().Thirds();

   15 

   16             Console.Write(result);

   17             Console.Read();

   18 

   19         }

   20     }

 The crazy calculation above actually return the original value.

 




Linq Sample

You are most likely going to be calling pre-defined Extensions rather than writing you own especially when using LINQ.  For example I can abreviate the followng LINQ statement: 

   10             var db = new NewsContextDataContext();

   11             return from n in db.NewsPosts

   12                    where n.IsPublished

   13                    select n;

 I can use extension methods and abbreviate it like this (thanks Resharper!):

   10             var db = new NewsContextDataContext();

   11             return db.NewsPosts.Where(n => n.IsPublished);

 The MVC Storfront Sample

On the ASP.net web site there is a bunch a screencasts and code samples mainly dedicated to implimenting a real world MVC web site, but one of the interesting approaches they have taken is that when they use LINQ to talk to the database, they actually have a methods that returns a basic query of data and then they have a set of Extension Methods that take the initial data set and filters it further depending on what is needed.  So for example if you look at my code in the sample of above, I can take that data set and return it as the type IQueryable<NewsPost>.  I can then take that type and if I want to Query it further, I can use an extension method to return a smaller data set.  Also, notice here I have more than one argument I am passing in.

   12         public static IQueryable<NewsPost> WithTopFrontPage(this IQueryable<NewsPost> posts, int take)

   13         {

   14             return (posts.Where(p => p.IsFrontPage)).Take(take);

   15         }

The above extension method takes the original data set and gets only the records that have the FrontPage indicator set to true, and then takes the top 5 records.  I then have a test that checks to see if I have indeed returned 5 records.

   70             var actual = respository.GetAllActive().WithTopFrontPage(5).ToList();

   71             var expected = 5;

   72             Assert.AreEqual(actual.Count, expected, "Expected result does not equal 5");

Yey it passed!  This is a pretty neat approach that should make retrieving data simpler.

Downside:

Now that I got us all excited about using this feature just a word of warning.  It seems to me that in using Extesnsion Methods we are violating the philosophy of "not talking to strangers."  In other words we are pretty tightly coupled here and if you do not have control over the object that you are bolting to your extension to, there could be problems down the road.  Actually even if you do have control, you still could be entering the "zone of pain."  So with that said, happy coding and use carefully.



Using ASP.Net AJAX To Auto Update From Date and To Date textboxes

clock August 16, 2008 09:15 by author Steve

Introduction

If you have ever had to make a hotel reservation on line you may have seen this functionality. Basically there is a field for a check-in date and there is also a field for a check-out date. Typically the websites have cool popup windows with a calendar in it that when selected, the text field is automatically updated with the chosen date. It then gets a little bit more complicated because usually when you select the check in date, the check out-date is automatically updated also.

 Let's take a look at how to a this with ASP.AJAX and the Ajax Control Toolkit CanderExtender.

 The Mark Up

   32 <asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">

   33         <ContentTemplate>

   34             <div class="DatePanel">

   35                 <table>

   36                     <tr>

   37                         <td>

   38                             from date:

   39                             <asp:TextBox runat="server" ID="txtFromDate" onFocus="javascript:this.blur();" Width="80"

   40                                 autocomplete="off" AutoPostBack="true" OnTextChanged="txtFromDate_TextChanged" />

   41                             <asp:ImageButton runat="Server" ID="Image1" ImageUrl="~/images/Calendar_scheduleHS.png"

   42                                 AlternateText="Click to show calendar" /><br />

   43                             <cc1:CalendarExtender ID="calendarButtonExtender" runat="server" TargetControlID="txtFromDate"

   44                                 PopupButtonID="Image1" />

   45                             <asp:RangeValidator ID="RangeValidatorFromDate" runat="server" ErrorMessage="Please enter a date a greater than or equal today's date"

   46                                 ControlToValidate="txtFromDate" Type="Date"></asp:RangeValidator>

   47                         </td>

   48                         <td>

   49                             &nbsp;&nbsp;&nbsp; to date:

   50                             <asp:TextBox runat="server" ID="txtToDate" onFocus="javascript:this.blur();" Width="80"

   51                                 autocomplete="off" OnTextChanged="txtToDate_TextChanged" AutoPostBack="true"/>

   52                             <asp:ImageButton runat="Server" ID="Image2" ImageUrl="~/images/Calendar_scheduleHS.png"

   53                                 AlternateText="Click to show calendar" /><br />

   54                             <cc1:CalendarExtender ID="CalendarExtender1" runat="server" TargetControlID="txtToDate"

   55                                 PopupButtonID="Image2" />

   56                             <asp:RangeValidator ID="RangeValidatorToDate" runat="server" ErrorMessage="Please enter a date a greater than or equal today's date."

   57                                 ControlToValidate="txtToDate" Type="Date"></asp:RangeValidator>

   58                         </td>

   59                     </tr>

   60                 </table>

   61                 <br />

   62                 <br />

   63             </div>

   64         </ContentTemplate>

   65         <Triggers>

   66             <asp:AsyncPostBackTrigger ControlID="txtFromDate" EventName="TextChanged" />

   67             <asp:AsyncPostBackTrigger ControlID="txtToDate" EventName="TextChanged" />

   68         </Triggers>

   69     </asp:UpdatePanel>

The textbox could be wired many different ways, but here is what I did.

  • First off, I set the OnFocus property to blur. This blocks the user's from entering a date in the texbox directly.  I did not want the user to have the ability to enter an incorrectly formated date, so I am only allowing them to enter the date only by using the calendar control.
  • Because I want to update the "to date" when the text changes in the "from date", I have set the OnTextChange event to fire which will map to our function that will do the update.  I have also set the AutoPostback property to true, so it knows to make a postback.  Because we also have this event mapped to UpdatePanel trigger, it will do an asynchronous call to the function causing only this section of the page to update.
  • The ImageButton will fire the popup calendar.
  • The CalendarExtender control them maps the Textbox and the ImageButton so they work in tandom.  Because I am using an ImageButton, the calendar will automatically disappear when the date is selected.
  • I have a RangeValidator that basically checks to make sure the date is not before today's date.
  • The same thing is then done for the "to date" controls.
  • All of this is then wrapped by an UpdatePanel (note: make sure you set the ToolScriptManager to enable partial rendering and you also set the UpdatePanel's UdateMode to "Conditional" for better performance).
  • The triggers are then set to asynchronously post back when text changes.

The Page_Load Event

I would definatelly refactor this code later but for simplicity sake I am just puttong the code right on the page load event.

   19 protected void Page_Load(object sender, EventArgs e)

   20         {

   21             if (!Page.IsPostBack)

   22             {

   23                 RangeValidatorFromDate.MinimumValue = DateTime.Today.ToShortDateString();

   24                 RangeValidatorFromDate.MaximumValue = DateTime.MaxValue.ToShortDateString();

   25                 RangeValidatorToDate.MinimumValue = DateTime.Today.ToShortDateString();

   26                 RangeValidatorToDate.MaximumValue = DateTime.MaxValue.ToShortDateString();

   27                 txtFromDate.Text = DateTime.Today.ToShortDateString();

   28                 txtToDate.Text = DateTime.Today.AddDays(30).ToShortDateString();

   29 

   30             }

   31 

   32 

   33         }

Here all I am doing is setting the RangeValidator exception rules, and I also setting the default date to be today and 30 days from now.

The Text Change Events

   35         protected void txtFromDate_TextChanged(object sender, EventArgs e)

   36         {

   37             DateTime fromDate = Convert.ToDateTime(txtFromDate.Text);

   38 

   39             if (fromDate >= DateTime.Today)

   40                 txtToDate.Text = fromDate.AddDays(30).ToShortDateString();

   41             else

   42             {

   43                 txtFromDate.Text = DateTime.Today.ToShortDateString();

   44                 RangeValidatorFromDate.IsValid = false;

   45 

   46             }

   47         }

   48 

   49         protected void txtToDate_TextChanged(object sender, EventArgs e)

   50         {

   51             DateTime toDate = Convert.ToDateTime(txtToDate.Text);

   52             DateTime fromDate = Convert.ToDateTime(txtFromDate.Text);

   53 

   54             if (toDate < fromDate && toDate >= DateTime.Today)

   55                 txtFromDate.Text = toDate.ToShortDateString();

   56             else

   57             {

   58                 if (toDate < DateTime.Today)

   59                 {

   60                     txtFromDate.Text = DateTime.Today.ToShortDateString();

   61                     txtToDate.Text = DateTime.Today.AddDays(30).ToShortDateString();

   62                     RangeValidatorToDate.IsValid = false;

   63                 }

   64             }

   65 

   66         }

  • When the "from date" is selected, I want to change the "to date" to be 30 days from now.  If the date is less than today, I want to set the "to date" back to today.
  • When the "to date" is selected, if it is less than the "from date", then I want to set the "from date" to be the same date.  If the "to date" is less than today's date, then I want warn the user and set the dates back to the default dates.
 Not too difficult.

 

 

 



Where did CopyAsHTML go?

clock August 10, 2008 12:03 by author Steve

I was asked how I was copying my code to this blog, and I was going to reply that you can get this great free tool at the address below but if you click on it (at the time of this writing anyway), the link is dead.

www.jtleigh.com/people/colin/blog/archives/2006/06/copysourceashtm_9.html 

I heard about CopyAsHtml tool from MSDN Magazine a couple years ago in its aricle titled, "Visual Studio Add-Ins Every Developer Should Download Now."  Even though it was written in 2005, much of the tools on this page are still pretty useful.  If you find this tool some where you can still get it to work for Visual Studio 2008.  Go to your VS 2005 addins folder (C:\Users\<user>\Documents\Visual Studio 2005\Addins) and copy the CopyAsHtml files to your Visual Studio 2008 folder.  Then Open the CopySourceAsHtml.AddIn file and where ever you see 8.0, change it to 9.0.

Now as far as settings, Scott Hanselman has a nice post on the different themes out there that you can use if you do not want to go through all the toils of setting all the fonts yourself.  You can now Import themes which is quite cool.  I picked Oren Ellenbogen's theme, because I really like the soft dark colors.  I only changed a couple of colors to make my User Object and key words stand out a bit more.  I started my IT life in front of a green screen, and even when I was developing in Visual Basic 6.0 and Visual Interdev, I changed my background settings to black.

 If you know where we can get CopyAsHtml let us know.  It's a great tool and it would be a shame if it disappeared.



Sorting a List Object

clock August 9, 2008 08:28 by author Steve

Introduction

When Generics came out in Visual Studio 2005, the new List object was introduced.  This feature was a new collection object that also gave you the ability to sort records based on whatever criteria you wanted.  There was also a new coding feature that came out called “anonymous functions” which basically allowed you to plop a blob of code in a parameter of another function.   The Sort function was a big step in sorting collections, much simpler than in Visual Studio 2003 but it still a bit jankie.

Sorting in Visual Studio 2005

So let's take a look at how to do this old way and then look at some new ways we can do the same thing in Visual Studio 2008.

   30         public enum SortDirection

   31         {

   32             Ascending = 1,

   33             Descending = -1

   34         }

   35 

   36 

   37         public List<GuestDto> SortLastNameUsingAnonymousFunction()

   38         {

   39             if (guests == null)

   40                 return null;

   41 

   42             guests.Sort(new Comparison<GuestDto>(delegate(GuestDto guest1, GuestDto guest2)

   43                 {

   44                     return Convert.ToInt32(SortDirection.Ascending) * guest1.LastName.CompareTo(guest2.LastName);

   45                 }));

   46 

   47             return guests;

   48         }

  Even after having done this many times, I still need to refer to an example before doing it because it is just not that intuitive.

Using Lambda Expressions in the Same Example

Now let's look at a cleaner way to do this that even I can remember.

   50         public List<GuestDto> SortLastNameUsingLambdaExpressions()

   51         {

   52             if (guests == null)

   53                 return null;

   54 

   55             guests.Sort((guest1, guest2) =>

   56                 Convert.ToInt32(SortDirection.Ascending) * guest1.LastName.CompareTo(guest2.LastName));

   57 

   58             return guests;

   59         }

Essentially the way it works is the parameters are placed on the left side of the => statement and the body of the code you want to execute is on the right.   All the types (including guest1, guest2, the new Comparison object , and the delegate and return statements) are automatically determined by the compiler so you do not need to specify it.  So the Lambda Expression is a nice way to abbreviate anonymous methods and make the code more readable.  

 Adding Linq to the Equasion

When I mentioned to my team the neat way sorting which is easier to code, a co-worker and friend Mike Bosch (great blog here) showed me an even better approach by just referencing the Linq namespace.

 

   62         public List<GuestDto> SortLastNameUsingLinqLambdaExpressions()

   63         {

   64             if (guests == null)

   65                 return null;

   66 

   67             List<GuestDto> sortedGuests = guests.OrderBy(g => g.LastName).ToList();

   68 

   69             return sortedGuests;

   70 

   71         }

To which in the immortal words of Bill and Ted's Excellent Adventure, I say "PARTY ON DUDES!"

 

 



Tracing SOAP Messages in WCF

clock July 25, 2008 05:26 by author Steve

This is a nice feature that comes with WCF (Windows Communication Foundation) with regards to debugging SOAP messages.  With VS 2005, out of the box there is no way to trace message when your message is not well formed and cannot be parsed.  You have to download WSE 3 or another third party tool like Contract First to assist in debugging.

 

WCF in VS 2008 comes with a neat tool out of the box called SvcTraceViewer.exe.  To use it, you configure your service to log events when a message is made.  To do that you use the following configuration:

 

<system.serviceModel>
    <!-- add trace logging -->
    <diagnostics wmiProviderEnabled="true">
      <messageLogging
           logEntireMessage="true"
           logMalformedMessages="true"
           logMessagesAtServiceLevel="true"
           logMessagesAtTransportLevel="true"
           maxMessagesToLog="3000"
       />
    </diagnostics>     
  </system.serviceModel>
  <!-- define trace logging -->
  <system.diagnostics>
    <sources>
      <source name="System.ServiceModel"
              switchValue="Information, ActivityTracing"
              propagateActivity="true" >
        <listeners>
          <add name="xml"/>
        </listeners>
      </source>
      <source name="System.ServiceModel.MessageLogging">
        <listeners>
          <add name="xml"/>
        </listeners>
      </source>
    </sources>
    <sharedListeners>
      <add name="xml"
           type="System.Diagnostics.XmlWriterTraceListener"
           initializeData="C:\logs\CommandLineSTS.svclog" />
    </sharedListeners>
  </system.diagnostics>

 

Once your service is configured to log messages you can then use the tool to see the SOAP message and whatever exception were captured.  Below is a sample SOAP call to the QuickSell message logged.

  

 



Calendar

<<  September 2010  >>
MoTuWeThFrSaSu
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

View posts in large calendar

Sign in