Reply: How not to use Linq

Recently (well, today) the CodeProject Insider featured a blog entry - How not to use Linq.  It was a short but interesting artile that prompted me to write a comment but alas no comments can be written on the site.  So I thought I'd better answer it here instead.  Go have a read if you haven't already done so...

While I appreciate the intent and points of the article, I think it's also important to note the benefits of .Select() and .FirstOrDefault() as they have a place when used well.

With the former, it allows one to transform the results into another kind of object or even an anonymous object if required.  With the latter, the second form of the extension method is very powerful in that you can supply an object to use as the default if there are no results from the query.

So for example:

var product = products
     .Where(p => p.Id == 42)
    .Select(p => new SelectListItem{ Value = p.Id, Text = p.Name });

will produce an IEnumerable<SelectListItem> that can be used for example to populate a Select List in ASP.Net MVC templates.

var product = products
     .Where(p => p.Id == 42)
    .FirstOrDefault(new Product());

could be used to return a default product instance instead of a null.

var id = products
     .Where(p => p.Available)
     .Select(p => p.Id)
    .FirstOrDefault(-1);

would either return -1 if no available products are found, or the first product Id.

Very powerful stuff ths Linq with Extension Methods...

Any comments? Feel free to post your views on this topic...

Conditionally disabling Ajax in MVC3 Forms

While working with MVC3 and Razor views recently, we came across the need to disable the Ajax behaviour in a form when the user pressed a certain submit button.  To give you some background, we were working on a Shopping Cart whereby the user had the following possible actions:

  • Edit an item,
  • Delete an item
  • Update the quantity of an item,
  • Submit the cart to Checkout
  • Clear the cart
  • Refresh the cart.

Now, for most cases, we wanted the cart to be refreshed without the user having to see a post back, so Ajax was the best way to handle this, of course.  Although the application is based on the Umbraco CMS, we developed the e-Commerce side of things in MVC3 from scratch and integrated it with Umbraco using the excellent MVCBridge add-on.  This allowed us to take advantage of all MVC has to offer.  However, the solution that follows is not dependant on Umbraco or MVCBridge at all.

Because this is a new project and has no legacy MVC code in it, we are able to take full advantage of the new Unobtrusive Ajax style for binding Ajax to the form.  This means that under the hood we are using jquery's ajax engine only, and not the legacy Microsoft one.  Here's the basic form:

@using (Ajax.BeginForm(new AjaxOptions
{
    OnSuccess = "updateCart"
}))
{

    @Html.RenderFormToken();
<section id="shoppingCart">
    <h1>Items in your Shopping Cart</h1>
    <table cellpadding="0" cellspacing="0">
    @foreach (var item in Model.Items.Values)
    {
        // Render the items, including the Edit, Delete and Update submit buttons...
    }
    <tr>
        <td colspan="3" class="totalValue">Total Value:</td>
        <td class="totalValue">@Html.DisplayFor(model => model.TotalIncTax)
        @Html.HiddenFor(model => model.TotalItemCount)
        @Html.HiddenFor(model => model.TotalIncTax)</td>
        <td class="totalValue"></td>
    </tr>
    </table>
    <span class="submit"><input type="submit" value="Refresh Cart" name="refresh" id="refresh" />
    <input type="submit" value="Clear" name="reset" id="reset" />
    <input type="submit" value="Checkout" name="checkout" id="checkout" /></span>
</section>
}


Now, when a user presses any of the submit buttons, the form will be posted back to the server, and the new page content will be returned to the updateCart function so that we can update the user's view without having to refresh the page.

But we want the item Edit and the Checkout buttons to re-direct to a new page instead of submitting back to the shopping cart.  For that to happen, we need to do two things (let's focus on the Checkout button, which we want to re-direct to the Checkout page):

  1. In the ShoppingCartController we need to check which submit button was pressed by inspecting the form elements, and do a Resonse.Redirect() to the appropriate page if the user pressed the Checkout button, for example; and
  2. Disable the Ajax behaviour when the user presses the Checkout button.

The code to handle the second step is as follows:

   // These variables are defined here as they may be referenced in other code blocks.
   // The $().ready function is used to populate them.
    var cartSection = null;
    var eShopCartForm = null;

    $(document).ready(function () {
        cartSection = $("#shoppingCart");
        eShopCartForm = cartSection.closest("form");

        // Disable the ajax behaviour if the checkout button is pressed.  We want the form
        // to submit normally so that the page can be redirected. 
        var checkoutSubmit = cartSection.find("#checkout");

        // We supply our own handler for this button to remove the form's ajax submit handler.
        checkoutSubmit.live("click", function (evt) {
            // Setting this attribute to false means the ajax form submit handler won't be triggered...
            eShopCartForm.attr("data-ajax", "false");
        });
    });

If you care to dig deeper, then I recommend taking a look through the jquery.unobtrusive-ajax.js file that is bundled with the MVC3 projects.  Basically though we are changing the data-ajax attribute that is generated on the form element when the user clicks the checkout button so that the ajax submit handler doesn't trigger.

There you have it.  Any questions, suggestions, remarks, please leave a comment...

I love NuGet!

It's official.  NuGet is now my all-time favourite Visual Studio Add-on.

The other day I was trying to upload a simple website to my hosting platform, but was having trouble with the dependencies.  As it happens, the hosting environment doesn't have MVC3 installed, or SqlServer Ce 4 for that matter.  MVC wasn't much of a problem, but the Ce database server on the other hand - well.  That's a different story.

Since the website was very simple in functionality - it provides a form that users can fill out to be notified of developments for a piece of real-estate in Melbourne's Yarra Valley region - and we only had a day to build it, I thought the combination of MVC3 with SqlServer Ce (we used the Code-First data model approach, which worked out really well) would be a no-brainer.

That was until I proceeded to upload the database, and started having to hunt around for all the necessary dll's.  It wasn't enough to just mark the dependencies so they would be copied to the output directory, I actually had to go and manually hunt around for additional dll's as well.

Enter NuGet.

Nuget allows you to download and automatically set up additional packages and libraries with ease.  It takes care of the dependencies, and updates your configuration files as well so you can just start using the new functionality without having to worry about web.config settings, for example.

So.  I added the package for EFCodeFirst.SqlServerCompact to the project, which resulted in all the dependencies being added as well in one easy step, and then all I had to do was make a few tweaks here and there and upload the changes.  Too easy!

Thank you, NuGet, you've just made my life that much easier.

Using refactored.email

Overview

Refactored.Email is a dotNet 2.0 Library that provides functionality for sending templated emails with mail merge functionality in both HTML and Plain Text formats from within dotNet websites.

Features:

  • Any images embedded in the HTML format are converted to attachments and referenced correctly so that message format is not compromised.
  • Mail Merge capabilities - with customisable field placeholders and fields
  • Full HTML Email and Plain Text alternative support
  • Support for BCC email addresses allows masking of recipients list in the final email.

This library takes advantage of the System.Net.Mail framework, and is essentially a wrapper.

Downloads

Download it from here.

Sending Mail

After including the Refactored.Email dll, you can send an email in Html format using the following code:

// Set the Mail Merge Field Pattern:
Email.FieldPattern = "[{0}]";
// Set up our HTML and Plain Text templates:
string html = @"<html>
<head>
<title>Simple Html Email</title>
</head>
<body>
<p>Hello World!</p>
<p>This email contains a link to the <a href="""">Refactored Website</a></p>
<p>It also contains a mail-merge field delimited by [ and ]: [date]</p>
</body>
</html>";

string text = @"Hello World!
This email contains a link to the Refactored Website:
It also contains a mail-merge field delimited by [ and ]: [date]";

// Create a new parameters collection to hold our mail-merge fields:
NameValueCollection parameters = new NameValueCollection();
parameters.Add("date", DateTime.Today.ToString());
string subject;

// We now want to parse the message templates, inserting the mail merge data and extracting the subject:
string htmlContent = Email.ParseMessageTemplateContent(html, parameters, out subject);
string textContent = Email.ParseMessageTemplateContent(text, parameters);

Email.SendEmail("no-reply@refoster.com.au", "info@refoster.com.au", subject, htmlContent, textContent);

This is a basic example that will send html formatted email with it's plain text equivalent.  It contains a field delimited by '[' and ']' and the html will link back to the website.  The default field pattern however uses '{' and '}', so we need to tell the library to use our chosen delimiters.  To do that, we use the line :

Email.FieldPattern = "[{0}]";

The {0} part must be present for it is the placeholder in the field pattern for the field names.  Having the ability to change the field pattern means that the user can write email templates that are compatible with the templates used by Microsoft's ASP.Net MailDefinition class such as the CreateUserWizard control.  These controls use the delimiters '<%' and '%>'.

Note that calling Email.ParseMessageTemplateContent and including the subject parameter will try to extract the subject from within any <title></title> tags in the content.

Using Email Template files

The ability to use files containing the html and plain text templates is a powerful way of managing the content of the emails.

Refactored.Email has the ability to parse templates by file name or by AppSettings key name if the template filename has been set in the AppSettings section of the web.config configuration file.

// Set the Mail Merge Field Pattern:
Email.FieldPattern = "[{0}]";

// Set the Base URL for hyperlinks found in the message templates
Email.WebBaseUrl = "";

// Set the directory containing the message templates
Email.MailTemplateDirectory = @"C:\Mail Templates";

// Create a new parameters collection to hold our mail-merge fields:
NameValueCollection parameters = new NameValueCollection();
parameters.Add("date", DateTime.Today.ToString());
string subject;

// We now want to parse the message templates, inserting the mail merge data and extracting the subject:
string htmlContent = Email.ParseMessageTemplate("htmlTemplate.htm", parameters, out subject);
string textContent = Email.ParseMessageTemplate("textTemplate.txt", parameters);

// Send the email with both html and plain text content.
Email.SendEmail("no-reply@refoster.com.au", "info@refoster.com.au", subject, htmlContent, textContent);

Note we have introduced 2 new Properties: MailTemplateDirectory and WebBaseUrl.  MailTemplateDirectory is set to allow us to just name the templates without having to set the full path.  WebBaseUrl allows us to have relative urls embedded in the HTML mail.  More on that later.

The string passed into Email.ParseMessagTemplate may be a direct file name, or it could be a Web.Config AppSettings key name, where the value of that key is the actual file containing the template.  This means that we can have the AppSettings key specified in the code, and if we decide to use a different file later on, we can just go and change the configuration instead of recompiling.

That's about it really, take a look at the API documentation found on the download page, and feel free to post any questions and suggestions, and I'll try to answer them as quickly as I can.

Enjoy!

Getting Umbraco Gallery to work...

Issue # 1 - Trying to render Display folders containing Non-Image items

If you've used the Gallery addon for Umbraco from Designit, and tried to display a Media folder that contains other folders, no doubt you've come across this message before:

Object reference not set to an instance of an object.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.NullReferenceException: Object reference not set to an instance of an object.


The reason for this is that the code assumes that all Media items in the selected folder are Images, and doesn't do anything to filter out other items.

The solution is to add the following code snippet in the  the repImages_ItemDataBound event handler method:

            if (currentImage.ContentType.Alias != "Image")
            {
                e.Item.Visible = false;
                return;
            }

right after

            var currentImage = (Media)e.Item.DataItem;

Issue # 2 - Javascript include files are not automatically included in the page

The most common mistake that results in the Gallery not working as expected (ie, clicking on an image brings up the Lightbox) is to do with missing server-side form tags from your Master page.

Make sure you include the form element on as it's required by ASP.Net to register client side scripts properly:

  <form runat="server" id="form1">
    ..
  </form>

More information about this problem can be found here:

"The client-side script is emitted just after the opening tag of the Page object's <form runat= server> element. The script block is emitted as the object that renders the output is defined, so you must include both tags of the <script> element."

While you are at it, do yourself a favour and make sure your head tag includes the runat="server" attribute as well.  The Gallery package looks for the head tag and creates a new css link to include the lighbox css file.

Hope this helps you out, the second issue in particular was causing me grief before I finally turned to Microsoft's documentation, - I had some sites that would work, but others wouldn't.  It finally clicked that those that worked without me adding the necessary javascript links were using the form tag as mentioned above!