Saturday, 6 August 2011

Passing Objects between Silverlight Applications

This week I have been making some prototype applications using Silverlight, which has been awesome, and I decided that I wanted to pass a few objects between these applications. If this wasn't a quick prototype and didn't have to run off of a Sales person's laptop I would have whipped together a quick bit of RIA Services and got them talking that way. However these are just two out of browser applications.


Enter Local Messaging.

Silverlight has a great little feature, that I'm not sure is too often used, Local Messaging. Local Messaging allows you to send simple string messages between two Silverlight applications running on a local computer. This can be within the same Web Page or two Out of Browser Applications


Local messaging works by having a LocalMessageReciever listening for messages in one application and then a LocalMessageSender sending messages from the other. If you want two way communication you simply have a listener and sender in each application.



// In the receiving application:
LocalMessageReceiver messageReceiver = new LocalMessageReceiver("receiver");

// In the sending application:
LocalMessageSender messageSender = new LocalMessageSender("receiver");

I'm not going to explain how you send messages between the applications as MSDN does a far better job of doing so. However MSDN only talks about simple string messages, I wanted to send objects.

Serialising the objects

The obvious solution I thought was to just serialise the objects to a string and then back at the other end. Silverlight doesn't support binary serialisation and as we have to send the message as a string the Serialisation could be XML, which is common practice and I'm sure most people know instinctively how to do so, however XML is bloated. Alot of our message string would be information we aren't interested in. Now in most applications we don't worry about this and XML is fine however this local app messaging has a maximum string size of 40 kilobytes. We don't want to fill this up with XML notation, instead we want something alot leaner.


Although Silverlight doesn't support Binary Serialisation it does however support JSON. JSON is a far less verbose markup which will mean our messages will be more about the object data and structure rather than syntax which is a good thing


When it comes to JSON serialisation there are a few options, the built in DataContractJsonSerializer , JSON.Net etc. For the ease of setup I have gone for using the build in DataContractJsonSerializer. Here i take a generic object and serialise it to a stream which we can then use in our message.


DataContractJsonSerializer serialiser = new DataContractJsonSerializer(message.GetType());
var stream = new System.IO.MemoryStream();
serialiser.WriteObject(stream, message);

With this string we can simply use a streamreader to get the string that our sendasync message sender method requires.


 new System.IO.StreamReader(stream).ReadToEnd())

But there's a problem, we cant just send the json object, as at the other end we won't know the type. What we have to do then is send the type and the serialised form. This leaves us with the following:


stream.Position = 0;
sender.SendAsync(String.Format("{0}|!|{1}", message.GetType(), new System.IO.StreamReader(stream).ReadToEnd()));

Note: I have used |!| as a way to know where the type name ends and the stream begins, I could have said {type}={jsonserilaisedobject} but I chose not too, for neatness though this should be possible I just chose to use a delimiter I knew wouldn't appear in my serialised object.

Receiving the message and getting the object back

To get the object back in the other application, after receiving the message from the sender we have to split the string based on the delimeter and then deserialise the object.


var type = e.Message.Substring(0, e.Message.IndexOf("|!|"));
var data = e.Message.Substring(e.Message.IndexOf("|!|") + 3);

The DataContractJsonSerializer requires a stram not a string to deserialise an object so next take the data string and turn it into a stream. Then just deserialise.


var stream = new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(data));
var serialiser = new DataContractJsonSerializer(Type.GetType(type));
if(type == "OurCustomType"){
    var result = (OurCustomType)serialiser.ReadObject(stream);
}

And that's it, obviously handling lots of types would mean not using an if statement, maybe a switch or whatever but the principle is the same.


I hope you find this of use, it certainly made my prototype far more functional and proved a neat way of doing it

Friday, 22 July 2011

linq to lucene magic unicorn edition

This week I started to look at using Lucene.Net again in a project. Previously I have used it directly however whilst on the new look Lucene.Net website I noticed the linq to lucene project. Which at first look seemed pretty good. However upon looking at the source I found it only works with XML and LinqToSql, this prototype project however was in .Net 4 and used Sql CE4 with Entity Framework 4.1 Code First.


Rather than abandon linq to lucene I decided it should be easy enough to get it working with EF4.1. DatabaseIndexSet contains the LinqToSql specific code so for the purpose of producing a proof of concept this is where all the changes go.


The first item to change is to rescope the generic where clause to be DbContext rather than the linq context.


 public class DatabaseIndexSet : IndexSet
        where TDataContext : DbContext

Next the Write Method needs altering, the LinqToSQL implementation uses the GetTable method, however E.F doesn't really have the same methods. Instead with E.F we can use the Set method with our entity as the generic, this will allow us to obtain all of the records


Finally the GetTableTypes method needs "tweaking", I say tweak as so far its a bit of bodge. The GetTableTypes method is used to find all of the "tables" within our database context which are then later used to create lucene index files. The original LinqToSql method ensured that each property was a generic type and that it is assignable from the base type. For our initial run as our entity classes don't inherit anything I have removed that check, just ensuring the properties are generic. This will be added back in for the final release.


With those changes made we can then use LinqToLucene as follows:


 var index = new EnquiriesIndexContext();
            index.Write();
            var query = from p in index.Parts
                        where p.PartNumber == "ghjtyugfhj-61"
                        select p;
           Console.WriteLine("Simple Query:");
           ObjectDumper.Write(query);
           Console.WriteLine("Query Output: {0}", query.ToString());
           
            var results = query.ToList();
            Console.WriteLine("SimpleDemo returned {0} results", results.Count);


Have a go of the prototype code that is attached, I'll be aiming to tidy it up and get it pushed back to the main project on codeplex over the next week. Enjoy

Saturday, 7 May 2011

Using the Razor Engine to create PDFs

I've been using MVC 3 and the Razor syntax for a while now, and a while ago whilst reading how people were using Razor for their email templates I had the idea to use Razor to help create PDF templates.


Now sadly it's been a while since I played around with this but I have only just started getting back on top of things so here's how I've done it.


Components Used

My solution is to write HTML templates using the Razor syntax and then to make use of the Razor View Engine for filling in the dynamic data. Then for the PDF creation to make use of iTextSharp library to generating the PDF documents from the generated HTML.

iTextSharp is a free library that allows you to create PDF's using C#, unfortunatley its API is a bit of a pain to use natively so I have made use of Hugo Bonacci's HtmlToPdfBuilder.cs class to simply my interaction with the library.


The first thing to do is to generate the HTML markup for out PDF. So fire up a new RazorViewEngine and load the view (template) you wish to use. We load the view by using the view engines FindView method. This method requires a ControllerContext so if you are using in an MVC site you can pass the current context, which is how for my protoype I used it, or you need to pass a custom one.


 var frontPageData = new RazorViewEngine().FindView(this.ControllerContext, "Newsletter", "", false);

In my example I wanted my Newsletter template, I have decided to not use a master view or caching but in a real application you may want to enable view caching.


Next you need a view context, a view context also needs a controller context, the view you found previously, a viewdatadictionary, a tempdatadictionary and a textwriter. The viewdatadictionary should be used to pass a model to your view or viewdata items, these will then be used within the view to populated your dynamic data items. The textwriter will be the object that the final generated HTML will be written to and this is what we will use to generate the PDF from.


  var textwriter = new StringWriter();
        var dataDict = new ViewDataDictionary<NewsletterData>()
        {
            Model = new NewsletterData{ Name = "Mike" }
        };
        var context = new ViewContext(this.ControllerContext, frontPageData.View, dataDict, new TempDataDictionary(), textwriter);

The final thing we do to get our generated HTML is call the Render method on our view, this render method requires our viewcontext and the text writer.


frontPageData.View.Render(context, textwriter);

Now the HTML is generated we can make use of the HtmlToPdfBuilder class to take our HTML and generate a PDF document. This is as simple as creating a new builder with a provided pagesize, calling add page and then appending our HTML. Hugo's HtmlToPdfBuilder class expects an array of values to use in the HTML, as it expects the HTML to have placeholders in the String.Format fashion {0} {1} etc. We don't need to do this as we have already finalised our HTML, if this was going to be production code I would seriously consider not using the HtmlToPdfBuilder class and instead write one that only uses iTextSharp and the RazorViewEngine, however for this simple prototype it makes things alot simpler.


var myHtmlPdfBuilder = new HtmlToPdfBuilder(PageSize.A4_LANDSCAPE);
//add a new page
myHtmlPdfBuilder.AddPage();
//using the current page take our html data from our view and write it
myHtmlPdfBuilder[0].AppendHtml(data, new object[]{ });


Now the PDF has been generated we can call the RenderPDF method on our HtmlPdfBuilder class to get an array of bytes back that represent the PDF. We can then use this array and write it to disk as a PDF document or to a response stream to have a dynamic PDF page on a website.


It really is that simple, I hope this prototype shows you the power the RazorViewEngine gives you especially when you start combining its output with other third party libraries. You can download my protoype MVC 3 site with this in and have a poke. It includes a simple PDFActionResult along with the above code.


Update

Whilst writing this blog post up I have decided to roll this prototype into a proper component which should simplfy the above and not require the use of Hugo's class. I will blog about this component when it is finished and publish it on CodePlex or something similar.

Monday, 31 January 2011

Using DeferredLoadListBox in a Pivot Control

Recently I've been using the DeferredLoadListBox It's a fantastic way of improving the performance of your list views.


I started using the DeferredLoadListBox within a Pivot control but occasionally found that the app would randomly crash throwing the following exception:

All containers must have a Height set (ex: via ItemContainerStyle), though the heights need not all need to be the same.
This usually means you haven't set the properly. However I had ensured I had set the style height. I then got the source and started poking around.


What I found was although in the UnmaskItemContent method the container had a height if you inspected the ActualHeight property this was 0. I did a bit of research and found the following on MSDN:

ActualWidth and ActualHeight are calculated based on the Width/Height property values and the layout system.  There is no guarantee as to when these values will be "calculated"
So the problem appeared to be with timing.


After contacting the very helpful David Anson and exchanging a few ideas on solving the issue I decided to implement a retry counter. The concept being the first two times we find the container height is 0 drop out of the unmask method and try again later. If we still don't have a height on the third attempt an error must have occurred.


Implementing the counter was very simple and the most important thing is that this simple retry counter allows time for the height to be calculated and work as expected. The only minor side effect of this counter is that you can notice a slight pause in the UI however as this exception doesn't occur everytime this seems fair enough.


Find the modified source below and I hope it can be of use to you.

// Copyright (C) Microsoft Corporation. All Rights Reserved.
// This code released under the terms of the Microsoft Public License
// (Ms-PL, http://opensource.org/licenses/ms-pl.html).

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;

namespace Delay
{
    /// 
    /// Implements a subclass of ListBox based on a StackPanel that defers the
    /// loading of off-screen items until necessary in order to minimize impact
    /// to the UI thread.
    /// 
    public class DeferredLoadListBox : ListBox
    {
        private enum OverlapKind { Overlap, ChildAbove, ChildBelow };

        private ScrollViewer _scrollViewer;
        private ItemContainerGenerator _generator;
        private bool _queuedUnmaskVisibleContent;
        private bool _inOnApplyTemplate;

        /// 
        /// Handles the application of the Control's Template.
        /// 
        public override void OnApplyTemplate()
        {
            // Unhook from old Template elements
            _inOnApplyTemplate = true;
            ClearValue(VerticalOffsetShadowProperty);
            _scrollViewer = null;
            _generator = null;

            // Apply new Template
            base.OnApplyTemplate();

            // Hook up to new Template elements
            _scrollViewer = FindFirstChildOfType(this);
            if (null == _scrollViewer)
            {
                throw new NotSupportedException("Control Template must include a ScrollViewer (wrapping ItemsHost).");
            }
            _generator = ItemContainerGenerator;
            SetBinding(VerticalOffsetShadowProperty, new Binding { Source = _scrollViewer, Path = new PropertyPath("VerticalOffset") });
            _inOnApplyTemplate = false;
        }

        /// 
        /// Determines if the specified item is (or is eligible to be) its own item container.
        /// 
        /// The specified item.
        /// true if the item is its own item container; otherwise, false.
        protected override bool IsItemItsOwnContainerOverride(object item)
        {
            // Check container type
            return item is DeferredLoadListBoxItem;
        }

        /// 
        /// Creates or identifies the element used to display a specified item.
        /// 
        /// A DeferredLoadListBoxItem corresponding to a specified item.
        protected override DependencyObject GetContainerForItemOverride()
        {
            // Create container (matches ListBox implementation)
            var item = new DeferredLoadListBoxItem();
            if (ItemContainerStyle != null)
            {
                item.Style = ItemContainerStyle;
            }
            return item;
        }

        /// 
        /// Prepares the specified element to display the specified item.
        /// 
        /// The element used to display the specified item.
        /// The item to display.
        protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
        {
            // Perform base class preparation
            base.PrepareContainerForItemOverride(element, item);

            // Mask the container's content
            var container = (DeferredLoadListBoxItem)element;
            if (!DesignerProperties.IsInDesignTool)
            {
                container.MaskContent();
            }

            // Queue a (single) pass to unmask newly visible content on the next tick
            if (!_queuedUnmaskVisibleContent)
            {
                _queuedUnmaskVisibleContent = true;
                Dispatcher.BeginInvoke(() =>
                {
                    _queuedUnmaskVisibleContent = false;
                    UnmaskVisibleContent();
                });
            }
        }

        private static readonly DependencyProperty VerticalOffsetShadowProperty =
            DependencyProperty.Register("VerticalOffsetShadow", typeof(double), typeof(DeferredLoadListBox), new PropertyMetadata(-1.0, OnVerticalOffsetShadowChanged));
        private static void OnVerticalOffsetShadowChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            // Handle ScrollViewer VerticalOffset change by unmasking newly visible content
            ((DeferredLoadListBox)o).UnmaskVisibleContent();
        }

        private void UnmaskVisibleContent()
        {
            // Capture variables
            var count = Items.Count;

            // Find index of any container within view using (1-indexed) binary search
            var index = -1;
            var l = 0;
            var r = count + 1;
            while (-1 == index)
            {
                var p = (r - l) / 2;
                if (0 == p)
                {
                    break;
                }
                p += l;
                var c = (DeferredLoadListBoxItem)_generator.ContainerFromIndex(p - 1);
                if (null == c)
                {
                    if (_inOnApplyTemplate)
                    {
                        // Applying template; don't expect to have containers at this point
                        return;
                    }
                    // Should always be able to get the container
                    var presenter = FindFirstChildOfType(_scrollViewer);
                    var panel = (null == presenter) ? null : FindFirstChildOfType(presenter);
                    if (panel is VirtualizingStackPanel)
                    {
                        throw new NotSupportedException("Must change ItemsPanel to be a StackPanel (via the ItemsPanel property).");
                    }
                    else
                    {
                        throw new NotSupportedException("Couldn't find container for item (ItemsPanel should be a StackPanel).");
                    }
                }
                switch (Overlap(_scrollViewer, c, 0))
                {
                    case OverlapKind.Overlap:
                        index = p - 1;
                        break;
                    case OverlapKind.ChildAbove:
                        l = p;
                        break;
                    case OverlapKind.ChildBelow:
                        r = p;
                        break;
                }
            }

            if (-1 != index)
            {
                // Unmask visible items below the current item
                for (var i = index; i < count; i++)
                {
                    if (!UnmaskItemContent(i))
                    {
                        break;
                    }
                }

                // Unmask visible items above the current item
                for (var i = index - 1; 0 <= i; i--)
                {
                    if (!UnmaskItemContent(i))
                    {
                        break;
                    }
                }
            }
        }

        private bool UnmaskItemContent(int index)
        {
            var container = (DeferredLoadListBoxItem)_generator.ContainerFromIndex(index);
            if (null != container)
            {
                // Return quickly if not masked (but periodically check visibility anyway so we can stop once we're out of range)
                if (!container.Masked && (0 != (index % 16)))
                {
                    return true;
                }
                // Check necessary conditions
                if (0 == container.Height)
                {
                    throw new NotSupportedException("All containers must have a Height set (ex: via ItemContainerStyle), though the heights need not all need to be the same.");
                }
                // If container overlaps the "visible" area (i.e. on or near the screen), unmask it
                if (OverlapKind.Overlap == Overlap(_scrollViewer, container, 2 * _scrollViewer.ActualHeight))
                {
                    container.UnmaskContent();
                    return true;
                }
            }
            return false;
        }

        private static bool Overlap(double startA, double endA, double startB, double endB)
        {
            return (((startA <= startB) && (startB <= endA)) ||
                    ((startB <= startA) && (startA <= endB)));
        }

        private static OverlapKind Overlap(FrameworkElement parent, FrameworkElement child, double padding)
        {
            // Get child bounds relative to parent
            var transform = child.TransformToVisual(parent);
            var bounds = new Rect(transform.Transform(new Point()), transform.Transform(new Point(/*child.ActualWidth*/ 0, child.ActualHeight)));
            // Return kind of overlap
            if (Overlap(0 - padding, parent.ActualHeight + padding, bounds.Top, bounds.Bottom))
            {
                return OverlapKind.Overlap;
            }
            else if (bounds.Top < 0)
            {
                return OverlapKind.ChildAbove;
            }
            else
            {
                return OverlapKind.ChildBelow;
            }
        }

        private static T FindFirstChildOfType(DependencyObject root) where T : class
        {
            // Enqueue root node
            var queue = new Queue();
            queue.Enqueue(root);
            while (0 < queue.Count)
            {
                // Dequeue next node and check its children
                var current = queue.Dequeue();
                for (var i = VisualTreeHelper.GetChildrenCount(current) - 1; 0 <= i; i--)
                {
                    var child = VisualTreeHelper.GetChild(current, i);
                    var typedChild = child as T;
                    if (null != typedChild)
                    {
                        return typedChild;
                    }
                    // Enqueue child
                    queue.Enqueue(child);
                }
            }
            // No children match
            return null;
        }
    }
}

Saturday, 22 January 2011

Windows Phone 7: Security Exception on deactivate

This morning I have been finishing the tombstoning part of one of my Windows Phone applications I have been developing. Whilst testing I found that when the application deactivated or terminated that a SecurityException was thrown. Looking at the stack trace I noticed that it was occurring whilst trying to serialise some data to the Applications State store.


Now I knew I was putting some data into state so this wasn't to hard to find however, when I looked at what I was putting into state it was nothing more complicated than a custom type that exposed a collection of POCO classes. Why would this cause a security exception.


I decided to do a simple Google search for the exception "windows phone 7 security exception" and the second result looked similar: "c# - SecurityException was unhandled when using isolated storage" [http://stackoverflow.com/questions/4209280/securityexception-was-unhandled-when-using-isolated-storage]. So I had a look, and guessed that if you can't put internal classes into IsolatedStorage you also can't do it for ApplicationState, which makes sense when you think about it. So I made my type public and not internal and all was good in the world again.


Another week another tip.... I wonder what I will find out next week

Saturday, 15 January 2011

Windows Phone Development and Designer Exceptions

I've been building a few Windows Phone applications recently and have been learning lot's along the way and am hopefully releasing a couple of these in the near future. However on my current project I have been constantly hounded by the designer view of my xaml pages throwing exceptions.


Tonight I decided to finally look into why these occur, I have managed to ignore them previously due to the weird nature of them. My exception scenario consists of a page that contains a pivot control and inside one of the pivot items there is a user control. The user control contains a simple list view which has some data bound to it

Now I have always got past the exceptions as if you use the designer view for the user control it works fine, however as soon as I put it into a page or a pivot the designer starts kicking off.


The actual exception is as follows:

Could not load type 'System.Net.HttpUtility' from assembly 'System.Windows, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e'.
It then goes on to tell me the line number and location etc of this. At first glance this exception looks a bit weird as in the code view and if you run the application the exception is never thrown.

The first thing I did to try and resolve this issue was to find where System.Net.HttpUtility lives, in the desktop version of Silverlight it lives in System.Windows.Browser But as the Windows Phone version of Silverlight doesn't include System.Windows.Browser the development team moved the httputility type into the System.Net namespace and put it into the System.Windows assembly, http://msdn.microsoft.com/en-us/library/dd470087(v=vs.95).aspx#Assemblies. This explains why the project builds and runs etc, however it doesn't explain why the designer has such a bad time with it.


Sadly I couldn't find a source or information on this exception, my guess is the designer is loading an older version of the assembly, a missing designer tool update from the RTM or something similar {Edit see my update below}. However what I have found is that Silverlight does have another way of encoding and decoding URL strings. System.Uri.EscapeUriString, if you use this the phone and the designer all work happily. A minor change and a headache eased :) I'm hoping the next tools update fixes the issue but until then this seems the safest method of escaping url strings.

Update

As I oddly found this issue intriguing I decided to investigate further. I opened another Visual Studio attached it to the Visual Studio process I had my project in and turned on catch all exceptions. Then when the exception was thrown I looked at the AppDomains assembly list, System.AppDomain.CurrentDomain.GetAssemblies(), 55 in all were loaded. I then looked through this list for the assembly referenced in the exception. This assembly then had the following location value: Location "c:\\Program Files (x86)\\Microsoft Silverlight\\4.0.51204.0\\System.Windows.dll". This is the main Silverlight 4 runtime, NOT the Windows Phone version which is 3.7.x, to be certain I then opened the assembly in Reflector to be sure the System.Net.HttpUtility was missing and it was.

So as suspected the designer is loading the wrong assembly thus the error, the assembly it should be loading is in the referenced assemblies folder, C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\Silverlight\v4.0\Profile\WindowsPhone\System.Windows.dll , hopefully reporting this on Connect will result in a hotfix / update.

Thursday, 16 December 2010

MVC 3 RC2 Install Error (0x80070643)

This evening I came to installing MVC3 RC2 on both my desktop and laptop. The laptop installed first time, however I found that on my desktop it refused to install. Citing the following error in its install log: Installation failed with error code: (0x80070643)


After poking around in the log file I found that the VS10-KB2465236-x86.exe patch was where it was falling over. To try and get more information I found the temp directory with this file in and ran the installer manually. This also as expected errored, however this time when I looked in the log file I found a slightly different error: Install failed on product (Microsoft Visual Studio 2010 Express for Windows Phone - ENU)


The next obvious place for me to go next was to uninstall the Windows Phone Developer Tools, I figured I can always reinstall them. Upon removing this I then tried to install MVC3 RC2 again. This time it still failed, with the same error I had previously but this time for Visual Studio 2010.


As a final shot I wondered if the Visual Studio 2010 SP1 beta may have had the patch in and was causing some sort of a conflict. As such if I skipped the KB2465236 patch and simply installed the rest of MVC3 seeing if it would work OK.


It turns out I did manage to get it all installed, I had to grab the MSI Installers out of the temp directory that the main MVC installer creates and then run the installers in the following order: aspnetwebpages.msi, aspnetwebpagesvs2010tools.msi, aspnetmvc3.msi, aspnetmvc3vs2010tools.msi and finally nuget.msi
VS2010 now has the new templates and so far everything looks in order. This is a bit of a risky way of installing MVC3 RC2 but for me it works, if you have this issue and want to try bypassing the KB patch then I hope this has helped

Tuesday, 13 April 2010

MVC DateTime Suffix HTMLHelper

Recently I have been working on an MVC Project, and tonight I got to the point where I wanted to output a date in a specific format, for example Tuesday 13th April 2010. Sadly DateTime formatting still doesn't allow you to specify output a suffix, you can do Tuesday 13 April 2010 but not what I wanted.


I decided that I could achieve what I wanted by writing a quick HTMLHelper, this would then allow me to specify a datetime format string with a magic / special character which I could then replace for the appropriate suffix.


Writing the helper was very quick and easy, if you want to learn about HTMLHelpers and how to write your own I recommend looking at Stephen Walther's Post on HTMLHelpers .

The code for my datetimehelper is below:


using System;
using System.Web.Mvc;

namespace mjjames.MVCHelpers
{
 public static class DateTimeExtensions
 {
  public static string DateTimeFormat(this HtmlHelper helper, string dateTimeFormat, DateTime dateTime){
            var dateTimeOutput = dateTime.ToString(dateTimeFormat);
            if (dateTimeFormat.Contains("~"))
            {
                dateTimeOutput = dateTimeOutput.Replace("~", GenerateDaySuffix(dateTime.Day));
            }
      return dateTimeOutput;
  }

        /// <summary>
        /// Generates a Day Suffix from the Day Number
        /// </summary>
        /// <param name="day">Day Number</param>
        /// <returns>Suffix String</returns>
     private static string GenerateDaySuffix(int day)
     {
         var suffix = "";
            //find out if the day matches a suffix which isn't th
         switch(day)
         {
             case 1:
                case 21:
                case 31:
                 suffix = "st";
                    break;
                case 2:
                case 22:
                 suffix = "nd";
                 break;
                case 3:
                case 23:
                 suffix = "rd";
                    break;
                default:
                 suffix = "th";
                 break;
         }
         return suffix;
     }
 }
}

Then to use it first include the namespace in your view:


<%@ Import Namespace="mjjames.MVCHelpers" %>

And then to use it call Html.DateTimeFormat passing the format string and the DateTime value. To use the day suffix include the ~ character. Note you can use it with.


<%= Html.DateTimeFormat("dddd d~ h", Model.StartDate) %>
<%= Html.DateTimeFormat("dddd d h", Model.EndDate) %>

There we go, nice and easy, if you want to use this feel free I hope it helps

Tuesday, 6 April 2010

Using DOTRas - An Overview and some things I've learnt

Yesterday I decided to starting knocking together a quick application to help me backup my server to some local storage. The idea being that at any point I have a local copy of my server setup a maximum of a day old. The point of this application and how I've gone about writing it, what libraries I'm using etc will be part of a future blog post.


I decided early on that I however I wanted to transfer files I wanted to do this over a VPN to the server. I had several reasons for this, being able to expose my files over a network share, more secure etc. My Application will be running on an old laptop, so I first thought about just always having it connected to a VPN using windows, and run the application as normal. However I then thought what if the VPN disconnects and I don't notice, how long would it take until I noticed etc. So I decided to make the application create a VPN Connection at start up and then disconnect from it upon completion.


I figured that there would be a good library that would help with this and it turns out there is. DotRAS provides remote access service (RAS) components for .NET languages , it's tag line is "WindowsRAS made easy" and I have to say so far it has lived up to that.


A quick example of how to open the computers RAS PhoneBook :


using(var phoneBook = new RasPhoneBook())
{
     phoneBook.Open();
}
You could then find an existing entry within the phonebook to make a connection too and open a connection:

var entry = phoneBook.Entries.FirstOrDefault(e => e.Name.Equals("mikes test entry");
if(entry != null){
    entry.Open();
}

Now there's obviously alot you can do with it, create and manage connections programatically etc, use phone dialers but so far I'm just tinkering with VPN's.


Tips and Tricks

Now to what I wanted to post about, tips and tricks. Sadly whilst working with DOTRas I found a few gotcha's that I wanted to post about. I will also update this list as I find more. It's worth noting that all of these apply to DotRAS 1.1 and I'm using the Win2k8 build, some of these I know also apply to the XPSP2 build. And my development machine is Windows 7 x64.

Invalid Default PhoneBook Location

The default phonebook location, which is called when you just do phonebook.Open(), is set to use RasPhoneBookType.AllUsers, now this maps to : C:\ProgramData\Microsoft\Network\Connections\Pbk\rasphone.pbk which for me doesn't exist. The folders exist up to connections, I have no Pbk folder.


I could obviously check for this and then create the phonebook entry but really you should always use RasPhoneBookType.User which uses the phonebook located within the current users AppData.

The entry is not associated with a phone book

Actually quite an obvious issue but worth commenting on, If you create a new phonebook entry


var entry = RasEntry.CreateVpnEntry(_connectionName, IPAddress.Loopback.ToString(), RasVpnStrategy.Default,                                 RasDevice.GetDeviceByName("(PPTP)", RasDeviceType.Vpn));

And then try to set the user's credentials without first adding the entry to the phonebook:


entry.UpdateCredentials(new NetworkCredential(authenticationDetails.UserName, authenticationDetails.Password));

Everything goes horribly wrong, instead add the entry to the phonebook and then set the credentials.


phoneBook.Entries.Add(entry);
entry.UpdateCredentials(new NetworkCredential(authenticationDetails.UserName, authenticationDetails.Password));

Keep an eye on this post, I'll update it as I continue to use DotRAS and then on a later date post about my application in full.

Tuesday, 9 February 2010

Unable to merge or create branches with SVN

I've been using SubVersion for a while, several years infact, I use VisualSVN on my server and TortoiseSVN on my machines.


Until recently I had never experienced any problems or glitches with it. However at some point within the last two months one of my main repositories started playing up. It's the only repository I have ever branched. The problem occurred when after working on a branch for several months I decided it was time to merge it back into the head. However when I tried to use the merge function and take the branch into the head I was given the following error message:

[branch] is not a child of repository root URL [trunk]


I tried several attempts at trying to do this, rechecking out the branch and trunk in case they were corrupt but to no avail. I then decided to try creating a new branch to see if the repository was totally broken and I indeed got an error.

Repository moved permanently to [svn server address] please relocate;


So it looked liked the reopsitory was broken. On a bit of a whim I noticed that my file path looked something similar to f:\development\shared\a repository name\trunk and similarly the branch was f:\development\shared\a repository name\1.6 The SVN url however was http://[svn-url]/svn/arepositoryname , I was wondering if somehow the directory path was confusing SVN, was it always expecting the directories to be checked out in the same folder structure as the server.


So I tried creating a new directory called arepositoryname and then checked out the trunk and version 1.6 underneath it. I then tried to merge the 1.6 repository back into the trunk and create a new branch and it all worked fine!


So it indeed seemed that TortoiseSVN was getting confused by my folder structure not replicating the servers, simply fixing this, maybe it was just removing the spaces need to possibly try that further, resolved my issue.


Just to add clarification if you stumble across this trying to solve your issue that I was running VisualSVN 2.1 and Tortoise 1.6 on a Windows 7 x64 machine.


Some crazy behaviour but I least I figured it out after several hours ... hope this helps.

Friday, 22 January 2010

Drivers Causing "This file came from another computer and might be blocked to help protect this computer" On Startup

This weekend I had to reinstall a driver for a laptop touch pad. I went to the Sony site and got the driver, it came in a zip file and looked pretty standard. A load of driver files and an install file.


It install all OK and the touchpad started to have its scroll options, however then I rebooted and found each time the laptop started up Windows Vista kept asking to confirm I wanted to run the programs, "This file came from another computer and might be blocked to help protect this computer". Straight away I thought it had to be the file was blocked from running as it came from a download. I went to the files unblocked them all, clicked apply and rebooted again only to find it happen again.


Just to ensure I hadn't forgotten to click apply I tried this again only to find the same thing. After Googling for the issue I found the issue was that the installer had simply copied the drivers and files. This meant that the NTFS information was also copied, this information contained the flag "downloaded from internet" along with others.


The solution was to strip these files of this information, luckily you can obtain a free tool to do this. "Streams" from Sysinternals, I simply downloaded this,http://www.microsoft.com/technet/sysinternals/utilities/Streams.mspx, ran it on the directory, command prompt only, and rebooted to find all was well.

Certainly one to remember as I have came across this before and removed the program.

Thursday, 1 October 2009

Why we have to be more careful about what we read and more importantly what we write

In this day and age it is very uncommon to not use the internet to research or solve problems. Our reliance on printed reference books and even reference sites has dwindled massively.


As developers, especially budding developers, we often just Google our problems, in fact I think most of our senior dev's often say to us "have you Googled it?" When asked about something.


Now googling things of course has changed our industry, we can often solve problems or get good starting points within seconds.
This on its own is not a bad thing, we google we get the results and we crack on. The problem however is when you pick the first item or a random article and use what someone else has written as FACT.


The problem with our "Google" culture, this applys to more than programming, is that we often don't filter what we read. We suffer from fps, first page symdrom. If its on the first page of our results it has to be correct.


Sadly though all too often the actual blogs or forum posts we end up reading are from FACT. Today I was investigating an issue with a JS tab solution I had wrote and sadly found a ton of some very poor "tutorials". These articles / blogs although well presented and often written with the best intentions often lead people to learn / pick up bad habits. I won't name the article that prompted me to write this but to say the solution was so far wrong is an understatement.


New developers will always trust what they read, I think it stems from how our education systems work. I believe we need to refine our "google" culture tendencies and in particular our FPS.


How do we change this? Firstly we need to encourage people to not just read the first article / blog they reach from a search. Instead to open several tabs of articles on the subject matter and then read each one, and then and only then look at the common concepts / answers they provide. We need to consider multiple sources before something is FACT.


Also I believe blog and article writers also have an obligation to research / check out what others think or do regarding a subject before they post onto the internet. This also applies to big sites like the BBC, in fact the bigger you are the more this applies.


Its great to share solutions to problems and to write about things we like, things we have done, things we think are cool but we must ensure that what we write is technically sound, otherwise we continue to breed a culture and community of half baked products and websites.


This is where I believe sites like stack overflow and all their derivatives will help. As these sites continue to grow and questions with highly voted answers appear in our search engines, hopefully quality will begin to cut through the noise.


Our "google" culture no doubt makes things easier and quicker and I'm a believer in "have you Googled for it?" , but I do think us as writers and us as searchers need to be more analytical of what we read in order to produce things of higher quality and to grow in our profession.

Wednesday, 26 August 2009

TDD Masterclass in the UK

Roy Osherove is giving an hands-on TDD Masterclass in the UK, September 21-25. Roy is author of "The Art of Unit Testing" (http://www.artofunittesting.com/), a leading tdd & unit testing book; he maintains a blog at http://iserializable.com (which amoung other things has critiqued tests written by Microsoft for asp.net MVC - check out the testreviews category) and has recently been on the Scott Hanselman podcast (http://bit.ly/psgYO) where he educated Scott on best practices in Unit Testing techniques. For a further insight into Roy's style, be sure to also check out Roy's talk at the recent Norwegian Developer's Conference (http://bit.ly/NuJVa).


Full Details here: http://bbits.co.uk/tddmasterclass

bbits are holding a raffle for a free ticket for the event. To be eligible to win the ticket (worth £2395!) you MUST paste this text, including all links, into your blog and email Ian@bbits.co.uk with the url to the blog entry. The draw will be made on September 1st and the winner informed by email and on bbits.co.uk/blog

Friday, 26 June 2009

FlickVimTube - An FCKEditor Plugin

First things first, ignore the random title for this post, it does have a meaning and it's the best I could up with ;)


The other evening I was hitting some downtime and rather than carry on playing FarCry 2 I decided I'd write a quick FCKEditor plugin which I've been meaning to write for a while. By default the editor comes with functionality to insert flash files into your content which works well however I wanted to have a way to only insert online videos from Flickr, Vimeo or YouTube. To insert these I was having to manually go into source view, paste in the embed code etc. A chore and a heartache.


So enough was enough and I banged together a quick plugin that would take a YouTube Embed URL and then insert the appropiate embed HTML for it. This was actually quite simple. I worked out there was four main steps, 1. Extract the video ID from the URL 2. Create suitable embed markup and insert into the editor 3. Create a preview video so you can ensure it works before clicking ok. 4. Be able to view and update the video after inserting.


Extracting the video ID I'm sure could be done using some clever regex expression however for simplicity and speed I opted to simply slice the YouTube url at ?v= bit and then use the remainder as the ID. As the official embed url is in the format http://www.youtube.com/watch?v={id} I have decided for my first version of this plugin I can nievly split, however I should really parse it properly.


To insert the embed object I made use of some build in FCKEditor methods to create the object and then assign the attributes to it.


e = FCK.EditorDocument.createElement('EMBED');
SetAttribute(e, 'src', embedUrl);

SetAttribute(e, 'type', 'application/x-shockwave-flash');
SetAttribute(e, 'pluginspage', 'http://www.macromedia.com/go/getflashplayer');

SetAttribute(e, "width", GetE('txtWidth').value == '' ? 360 : GetE('txtWidth').value);
SetAttribute(e, "height", GetE('txtHeight').value == '' ? 150 : GetE('txtHeight').value);
SetAttribute(e, "allowscriptaccess", "always");
SetAttribute(e, "allowfullscreen", "true");

After getting the basics working I decided to add support for Vimeo and Flickr. This was a case of just working out which service it was and then parsing out the id's and then setting the correct embed URL on the embed object.

FCKEditor plugin's are dead easy to configure, in your fckeditor settings file simply register the plugin using FCKEditor.Plugins.Add and ensure your custom toolbar settings include the button, 'OnlineVideo'


FCKConfig.ToolbarSets["mjjames"] = [
    ['Cut','Copy','PasteText','-','SpellCheck',
    '-','Image','OnlineVideo','Table','Rule','Smiley','SpecialChar','-',
 'Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat','-','Link','Unlink','Anchor','-','Source'],
 ['Style','FontFormat','-','JustifyLeft','JustifyCenter','JustifyRight','JustifyFull',
 '-','Bold','Italic','Superscript','OrderedList','UnorderedList','-','Outdent','Indent'] 
];

FCKConfig.Plugins.Add('OnlineVideo', 'en');

The plugin file includes language settings for english, however adding additional languages can be added by simply providing translations for the labels and then changing your plugin registration to include the new file name. If you want to add French for example create a file called fr.js in the plugins languages and then the plugin registration becomes:


FCKConfig.Plugins.Add('OnlineVideo', 'fr');

The following language settings are available:
OnlineVideoTip, DlgOnlineVideoTitle, DlgNoVideo, DlgInvalidVideoUrl, DlgOnlineVideoURL, DlgOnlineVideoWidth, DlgOnlineVideoHeight, DlgOnlineVideoQuality, DlgOnlineVideoLow, DlgOnlineVideoHigh.


The plugin can be downloaded as a zip file and is licensed under Creative Commons Attribution-Share Alike 3.0 License


In the future I intend to extend this further, maybe to allow users to find and search for videos using the various API's provided by the video sources but that will be at a later date.

Tuesday, 19 May 2009

Wasting Bandwidth One Image at a Time....

Content Management Systems are great, they allow the average Joe to have a great level of control over their website. Gone are the days of clients asking for static pages to be amended, we live in the database powered give the power of editing and updating content to the client


Our clients get to use Rich Text Editors like FCKEditor. They look and feel just like Microsoft Word, they can play with text, upload images, resize them simply by dragging them and are very often really happy.


However this often comes at a cost. Most RTE's by default simply resize images by sticking on the HTML img attributes height and width. As many people are aware this doesn't actually resize the image, it just simply tells the browser take this massive image and render it smaller. The end user still has to download the huge image, I have seen on some sites 2000px x 1200px images being downloaded and then only shown at 250px x 120px!, which can take a while to load dependant their internet connection and ultimately we the developers have to pay the bandwidth cost for those images.


Tonight I decided to come up with something I could put on my church websites to combat this. It's more to aid the overall user experience rather than bandwidth cost but it all helps ;)

The Solution

The solution is actually really simple.

  1. Take the CMS content from the database
  2. Before rendering it to the page, parse it into a HTML Parser
  3. Using the parser find all the image tags
  4. Using the height,width and src attributes to generate a new URL to an image resizer app
  5. Replace the images src attribute with the new URL
  6. Render the content to the page

Parsing HTML in ASP.Net really is easy nowadays. Previously it was a pain, regex would never work properly or you could risk parsing your page as XML but luckily there are now tons of 3rd party libraries. I chose HTML AGility Pack as alot of people seemed to recommend it on stackoverflow

For my solution I simply did the following:


HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(page.body);
HtmlNodeCollection imgs = doc.DocumentNode.SelectNodes("//img");
if (imgs != null)
{
    foreach (HtmlNode img in imgs)
    {
        if (img.Attributes["height"] != null && img.Attributes["width"] != null)
        {
            HtmlAttribute src = img.Attributes["src"];
            string imgurl = src.Value;
            src.Value = String.Format("/loadImage.aspx?image={0}&action={1}&width={2}&height={3}", imgurl, "resizecrop",img.Attributes["width"].Value, img.Attributes["height"].Value);
        }
    }
}
mainContent.Text = doc.DocumentNode.InnerHtml;

So what's going on here? Well first I create a new HtmlDocument, provided by the HTMLAgilityPack. I then load my html from my db model, page.body, then I use a nice simple XPATH syntax to pull out any img tags within the content and stick them into a HTMLNode collection. Next after ensuring I have some nodes, I spin through each of them and check to see if they have a height and width attribute. If they do then I use these and the original image url to write a new url, in my case I have a page that does this, ideally though this should be a httpmodule or something to be nice and tidy. Then with the img tags updated I render the HtmlDocument back out to the page.


That's it, a few line's of code. Now I was concerned I may have slowed down my page, as parsing and spinning through tag's could be CPU intensive however I found on my machine and quickly playing with it, I noticed no real difference. In production I'd output cache the page for a period of time anyway. I noticed 100KB difference on one of my site's home pages and they weren't massive images, so I think it's well worth it.


There you have it, at the end of the day CMS's provide users with alot of power / flexibility but we have to ensure as developers that we provide systems that cater for users abusing the system, be this uploading massive images or otherwise. In the case of images simply having something that resizes the images on render keeps the frontend responsive whilst allowing the client to provide high resolution images if needed, in this case it wasn't alot of effort and the improvement alone was worth it.

Wednesday, 29 April 2009

Content Disposition in different browsers

Today I had to resolve an issue where in different browsers the filed dynamically generated download worked very differently / at all


The setup, we had an xml file with a custom extension, say .mj, which was being served up by ASP. The HTTP Header had a content disposition header and a response type set.


Response.AddHeader "Content-Disposition", "attachment; filename=""our file.mj"""
Response.ContentType = "text/xml"

This worked fine in Internet Explorer, the file was downloaded as "our file.mj". However FireFox and Chrome acted very differently, in FireFox the file was downloaded as just "our", and Chrome as "our file.xml".

In FireFox it appears that the issue is caused by having a space in the file name, this forum post by funkdaddu helped me on this, so by removing the space FireFox could now download the file as "ourfile.mj".


Chrome however did not want to play ball. It was still insisting on changing the file extension to ".xml". I guessed it was because we were telling it we serving up text/xml mime/type under a different file extension, I decided to change the response type to "Application/Save" just to see if this would make a difference, and amazingly it did. Amazing!


So there we have it, changing the file name to have no spaces and ensuring that the content type is set to "Application/Save" seems to make all browsers behave at least some what consistently with the Content Disposition header. Its worth noting that Scott Hanselman has a great blog post The Content Disposition Saga which talks alot about how the different of IE handles it. Also GreenBytes has a ton Test Cases for HTTP Content-Disposition header which I certainly found helpful.

Thursday, 23 April 2009

Looking for a URL using Linq and SiteMaps

I've been off sick from work with Man Flu but this afternoon I was getting bored of staying in bed for the second day so I got out my laptop just to have a play in between blowing my nose.


I wanted to write a quick way of looking up the full url path for a page within a sitemap. The only bit of information I know is the key of the page. Now I knew that I could possibly make use of the IndexOf method. This expects a SiteMapNode of the value you are looking for and returns the index of the node, you then need to get the node out of your collection of nodes, example below.


SiteMapNodeCollection nodes = SiteMap.RootNode.GetAllNodes();
int nodeIndex = nodes.IndexOf(new SiteMapNode(SiteMap.Provider, pagekey));
return nodes[nodeIndex].Url ?? String.Empty;

Now that method does work, however it felt dead clunky, I was sure I could write a more .Net 3.5 shiny one line way of doing this using Linq. In fact it was dead easy. First I still needed to get all the nodes, SiteMap.RootNode.GetAllNodes(), but I then cast these to SiteMapNode, I could then use FirstOrDefault with a nice lambda expression that matches the key property. Code below.


SiteMapNode node = SiteMap.RootNode.GetAllNodes().Cast().FirstOrDefault(n => n.Key.Equals(pagekey));
return node != null ? node.Url : String.Empty;
//note: the </sitemapnode> is not part of the code, the code highlighter JS is randomly adding it.

Simple, my only concern is that the bigger the sitemap becomes the more memory / slower this becomes, especially if called multiple times per page. It may make sense to cache the sitemapnodecollection for the duration of the page however that's beyond the scope of this brief article.


What hopefully you can see though is that a little bit of Linq and Lambda expressions can take chunks of code that seem long winded and turn them into nice neat one liners, which I think is usually more readable.

Wednesday, 22 April 2009

Book Review: ASP.Net MVC 1.0 Quickly

So this month I again have the privaledge of writing another book review. This time in an area I have particular interest. ASP.Net MVC has recently been released and there are no end of books coming out of the market, one of these being Maarten Balliauw's ASP.NET MVC 1.0 Quickly.


When the book arrived I first noted how it used the traditional orange Packt colour scheme with an interesting picture of a pair of glasses on the beach. This I preferred over the look of the last book I reviewed, however I am yet to work out the significance of the picture if there even is one?


The book starts by saying that the book will take you through "the essential tasks" and "does not cover every single feature in detail". This is my opinion is not a bad thing. It is not a full reference book like ... but more of a rapid guide to get developers to start using MVC and know the basics preety much everything, you can then get the in depth knowledge as you go along.


The book covers the following topics, not necessarily in order:

  • What MVC Is
  • Brief comparison between ASP.NET web forms and ASP.NET MVC
  • What a Controller, View and Model is and how you go about creating them in VS
  • What the process of a page is and how you handle interactions
  • What is routing and what you can do with it
  • Customizing the framework
  • Using Web Forms features in MVC
  • JQuery and AJAX in MVC
  • Testing and Mocking
  • Deployment


It then has three appendices which I recommend NOT skipping, It has a full application with source code, information on the MockHandlers available and finally tons of links on where to get more information on topics. The links help to fill in the gaps that the book has left due to its "quickly" approach and for me at least have been the most thumbed pages of the book.


The format of the book is very clear, lots of examples in C#, screenshots where appropiate and well worded. In particular I like areas where Maarten Balliauw takes the time to explain all the options for an attribute. For example in Chapter 4 he outlines Action Method Attributes, rather than just give a brief description of what they are, Marten takes the time to outline briefly all the possible attributes with a simple piece of source code if appropiate. Again this highlights how the book is just trying to make you aware of what exists so when you come to write something you think about what you could use and then go away and find out more if needed.


I have to admit I really like the book as an intro into MVC and to get people aware of it, it highlights alot and get's you thinking about design methodologies. It's one I recommend others to read.

Scores

Presentation 8/10 - Overall this book feels well put together and everything is clearly laid out

Code Examples 9/10 - Quite a high score for this, the book it littered with short code snippets and examples but what really does it for me is the example application included in the appendices. Simply working through this highlights everything you have read so far and highlights more. Well worth looking at

Quality of Content 8/10 - Again repeating what I have said earlier but I feel the content has been well put together and arranged in a manner that is clear and conjusive to learning

Overall 8/10 - If you are looking at just finding out about this ASP.NET MVC is all about and just want an outline to get you started this book is for you. It's not claiming to be a reference but a starting block to use to get you started, the links in the back give you some where else to go afterwards. Well worth a read.

Saturday, 28 March 2009

Google SiteMap Generator + Input validation failed Error

Since Google release their Google Site Map Generator I have been using it on my web server for the sites that I manage. Setting it up and getting it running was fine and I haven't had a problem, that was until this week.


This week I noticed that as I was only letting the generator update the sitemap from actual URL hits quite often a few of my sites aren't hit for a day at a time which was resulting in empty sitemaps. This then causes Google WebMaster Tools to whinge at you which isn't a good thing. So I decided to update my settings to include parsing my IIS Log Files in the hope it would use previous days ones and not generate blank files.


This is where I hit a road block. When ever I changed a setting and clicked save the generator would be really useful and tell me that "Input Validation Failed" and to basically sort myself out. I was confused to say the least as everything was fine, no field was highlighted as being erroneous so I ended up giving up and leaving it.


Today I came back to it and tried again but the same error occurred. So I started to poke around and decided to manually update the sitesetttings xml file, usually located: C:\Program Files (x86)\Google\Google Sitemap Generator\sitesettings.xml. And this is when I noticed the issue that was affecting me.


Each site within your IIS setup has a node in the sitesettings xml file, here it has information about it's host name, whether it's setup for sitemaps etc. But it also contains the location of the IIS log files regardless of whether you are parsing them or not.


Now a few weeks ago I decided to move all my sites log files from the default location of C:\WINDOWS\system32\LogFiles\{site} to a more convenient location, for this example lets say E:\LogFiles\{site}, now this was all well and good for IIS etc but upon creation of sites the Google SiteMap Generator is logging these locations. So when I had moved the log files the generator was still looking at the old location, a bit of guessing / how I would do it lead me to believe that upon saying parse log files for sitemaps the generator checks to see if it can read the log files, but as they have moved it cant find them and errors.


Now all I did to fix this was manually do a find and replace on the log file locations within sitesettings.xml saved the file and restarted the generator to find it was finally happy and working OK. Hopefully Google in the next release of this generator will remove this issue / make it clearer what is wrong. Ideally upon startup or even when you choose to use log file parsing it should look at IIS to see if the path to the log files is the same as it has, if not update it before it validates. This would save heartache for a few people at least.


So I'm now happy again with the generator, the problem wasn't that hard to fix and upon spotting it made alot of sense, it just shows what a little bit of investigating can do.

Friday, 20 March 2009

Book Review: C# 2008 and 2005 Threaded Programming

So last month Packt Publishing contacted me regarding sending me a promotional copy of C# 2008 and 2005 Threaded Programming to review. This is the first time I have been asked to do a book review and decided to take them up on the offer.


Now I have been using ASP.Net for around three years now but I've never had to or decided to look into writing multi-threaded apps so the fact that this book was aimed at beginners meant that I was an ideal target audience for this book. Packt shortly sent me the book and upon first looking at it thought it looked a bit ugly! I know you can't tell a book by its cover but this cover did put me off, the green and picture didn't do it for me but alas I carried on anyway.


The book is organised into several chapters and is example driven. What I mean by this is that it doesn't give you bags of theory and then an example, it takes the approach of you following along the code examples and then it has gaps explaining bits and pieces. More on this later.


The chapters within the book are organised in a way that as you progress each chapter delves into multi threading more. First of all it explains what multi threading is, then it looks as basic thread techniques, background workers, debugging multi threaded apps, thread pools all the way up to exploring the new future of multi threaded apps and new framework extensions to help with this. On the whole the chapter organisation made a lot of sense to me and allowed you to use what you had learnt before and build upon it. The one thing that struck me was that I expected ThreadPools to be talked about way before chapter 9 but that’s a minor thing.


One of the things I especially liked about this book is that at the end of each chapter you are given a quick pop quiz on the chapters content, this for me at least provided a quick way of ensuring I had understood the chapter and if I hadn't to go back and re read it, so this was good.


As I mentioned earlier the book is based on learning using examples and less about theory. Personally I'm not a huge fan of this technique; the writer Gastón C. Hillar does try to provide examples that are practical however I find that by simply following these you don't really learn what is going on; you learn how threading roughly works and that it’s there but when you need to use it in a real life application or you need to work out why something isn't working as expected you are left without the knowledge to solve these issues.


I do realise that this book is for beginners and is meant to get developers to look into and start writing multi-threaded apps and not be a complete resource, but personally I would prefer a touch more theory. In particular locking is over looked, what setting a WinForms app to [MTAThread] really means (you can't use dialogues for example). This was probably left out to try and keep things simple for beginners but not discussing locking or exceptions could mean bad practices are picked up and carried into production code.


It is worth mentioning that his book solely focuses on WinForm apps, it doesn't look into WPF or WebForms, and this is both a blessing and a curse in my eyes. With that said WinForms is simple to learn and the examples really do cover everything you need to get them working so if you have never used WinForms don't be put off reading this book, by the end of it you will not only know more about multi-threading but also how to write simple WinForm apps.
Also the book says that you can use Visual Studio 2008 Standard edition to debug multi threaded apps, I found out that sadly this isn't the case. In order to have the threads debug window you need the Pro edition or above version of Visual Studio.


Overall I find the book alright, personally the presentation of the book, colour schemes, internal typography could do with an improvement, the headings look like they are in Impact which is wrong on so many levels, and the examples can seem slightly farfetched but the book does cover a lot. As someone new to multi-threading by the end of it I felt confident enough in what I had learnt to write a simple multi threaded WinForm app for work to perform some tests.


Scores

Presentation - 6 / 10 - Although it’s clear to read the bulk of the content, the cover and headings for me let it down.
Code Examples – 7/10 – The code examples are clearly written and cover all the detail you need I feel that they aren’t as real life as they could be which hinders taking what they are meant to show you and apply it to real life scenarios.
Quality of Content – 7/10 – Overall I felt the quality content was quite good, potentially a bit over the top in places about being a “multi threaded guru” but overall OK. One down fall was to say that Visual Studio Standard edition can be used to debug multi threaded apps when it can’t.
Overall - 6.5 / 10 In light of everything I'm not going to suggest this is a book that everyone should read / own unlike over books like the pragmatic programmer etc. However if you are looking at learning about multi-threading and want something to ease you into it then this is for you, it will cover the basics of everything you need to know and what to expect in the future.