Technology Trends – October 2010 update

Around this time last year I wrote a blog post showing technology trends for browsers and operating systems as reported by Google Analytics for my blog and some mainstream websites.

Since then the world has moved on and the trends continue to evolve. New technology arrives, old technology slowly withers.

For the moment, it is interesting (to me, at least) to see what the trends are for my own blog.

Operating System

Windows XP is fading, not as fast as I’d hope, but still it is now below 50% of visits. On the other hand Windows 7 is rising fast and sitting at 38% in the space of just over a year. The biggest hit is for people moving away from Windows Vista, that’s where most of the market share is being lost from.

Other operating systems, such as MacOS and Linux don’t feature very heavily, bobbing around the 2% to 5% without much real movement. I’m not completely surprised by that, my blog is mostly aimed at .NET Development which is a Microsoft developer technology.

MyBlog-3yr-OS

Browser

While Operating System technology is being dominated by Microsoft they are losing share on the Browser front. While IE is still the more prevalent browser its share is being eroded, not by FireFox as I would have thought, but by Google’s offering, Chrome.

FireFox has stayed fairly steady over the last three years bobbing around the 30% to 40% mark. IE has gone from just shy of 70% to just over 40%. Meanwhile Chrome has risen from nothing to just under 20%.

Other browsers, such as Opera and Safari, don’t really make that much of an impact.

MyBlog-3yr-Browser

Tip of the Day #19: Create a list of objects instead of many lists of values

I?ve been reviewing some code and I came across something that jars. What is wrong with this is many-fold. Essentially, instead of encapsulating an related data into an entity that describes the whole the developer had created silos of data values, and you’d better hope that nothing went awry with any of it.

It looked like this:

List<string> addOnNames = new List<string>();
List<string> addOnDescriptions = new List<string>();
List<string> addOnCodes = new List<string>();
List<decimal> addOnBasePrice = new List<decimal>();

Okay, so if you don’t yet find it jarring, here are some of the things that are wrong with this structure.

To iterate through and operate on a single “add on” you have to do something like this:

for(int i = 0; i < addOnNames.Count; i++)
{
    string name = addOnNames[i];
    string description = addOnDescriptions[i];
    string code = addOnCodes[i];
    decimal basePrice = addOnCode[i];
    // Do stuff that operates on an "Add On" here
}

That?s quite a bit of work just to get at the values you need in a particular loop iteration.

If you need to pass the lists on to methods, you have to pass each in its own parameter. Method signatures start to become needlessly large. In the simplified example above (yes, the real code had a lot more in it) there are three extra parameters that need to be passed around to each method call that needs to act on a collection of add-ons.

private void DoSomethingToACollectionOfAddOns(
                     List<string> addOnNames,
                      List<string> addOnDescriptions,
                      List<string> addOnCodes,
                     List<decimal> addOnBasePrice)
{
    // Do something.
}

It is also quite difficult to use LINQ to query what is effectively an AddOn entity.

Unless you are very careful, your lists can become out of sync, and when that happens all sorts of strange and difficult to trace bugs enter into the system. In the code I was reviewing there was an edge case where by one of the lists didn?t get updated when it was initially populated. Because all the lists are assumed to be the same length the first time that they were iterated over, what should have been the final entity couldn?t be retrieved on one of the lists because it didn?t exist. As this happened well after the creation of the lists and in a method called several levels deep it was quite a job working out where the original bug was coming from.

Solution

What needs to happen here is that an entity class is created for an add on. Each of these lists indicates a field or property in an entity class. The class might simply look something like this:

public class AddOn{
    public string Name { get; set; }
    public string Description { get; set; }
    public string Code { get; set; }
    public decimal BasePrice { get; set; }
}

This encapsulates everything about a single add on entity in one place. If you want a collection of these objects you can do something like this:

    List<AddOn> addOns = new List<AddOn>();

If you want to loop over them you don?t have to write lots of code to get all the elements out of a variety of lists a simple foreach will suffice (unless you need to also know the index)

    foreach(AddOn addOn in addOns){}

If you need to pass the entity around, or a collection of the entities around then you only need to pass one parameter into a method.

    private void DoSomethingToACollectionOfAddOns(List<AddOn> addOns) {}

Using LINQ becomes much easier because now you have everything encapsulated in the one place. I can?t even imagine the convoluted joins that would be needed to process the individual values in a LINQ expression otherwise.

When creating the initial collection, if any particular property is not needed then it can be simply ignored, if need be a default value can be set in the constructor. Then you never have an issue with one collection being out of sync with another. You no longer have to worry about synchronising collections, everything to do with a single instance of the entity is in one place.

Tip of the Day #18: Dealing with data rounding issues

If you have data coming in from a database, web service or other source external to your application and it contains, say for example, price information then do not round it. Don?t attempt to apply any form of formatting to it regardless of how much the client insists that the data will be in this form (e.g. all prices at go live will be in round pounds).

Yes, having whole rounded pounds, without having to display the pennies, on the front end looks nice and pretty. However, the display of whole rounded pounds on the front end is a rendering issue and should always be left to the code that is rendering the user interface. The rounding should never take place close to the point that the information is extracted from its source.

Why? Because eventually the client will want things displayed differently, to put prices with pennies in it, and you have to display those pennies. Now you don?t have to just update the rendering code to display the pounds and pence, but you also have to track down where that rounding occurs and stop it from happening in the first place.

But that?s not all. If you round data, effectively truncating it, early then subsequent calculations will be wrong. In fact, rounding isn?t just like truncating the data. Truncating is just removing precision. Rounding can change the value. That can cause even more havoc later on.

So, why round that early anyway, especially if the data is arriving in a certain way in the first place? Well, I can only guess that perhaps because back before anything went live, in the test system they weren’t. So, just to make the test system look like the eventual live system the prices were rounded the moment they arrived in the application just to be sure they were in round pounds.

The lesson, clients change their minds and business logic should always operate on the cleanest data, unsullied by rendering constraints wherever possible. This means that calculations based on that data will remain correct, even if other factors change.

Dime/Daim Bar Cake

This is not so much a cake as a shortbread base with a sweet dairy topping. It serves many people and ideal as a party food. This recipe produces 20 servings.

Ingredients

  • 250g of softened butter or margarine
  • 150g granulated sugar
  • 450g plain flour
  • 500ml double (whipping) cream
  • 250g Dime/Daim bars

Equipment

  • Baking tray (25cm x 35cm approx)
  • Grease proof paper (optional) cut to fit the baking tray
  • Large mixing bowl
  • Sieve
  • Rolling pin
  • Fork
  • Fan assisted oven (if not fan assisted, adjust cooking times accordingly)
  • Disposable food bag (e.g. a freezer bag)
  • Food mallet
  • Refrigerator
  • Whisk
  • Spatula

Instructions

1. Lightly grease or lay grease proof paper in the baking tray.

2. In a large mixing bowl, cream together the butter/margarine with the sugar until light and fluffy.

3. Sieve the flour into the mixing bowl. Mix everything together until a soft dough is formed.

4. Put the dough on centre of the the grease proof paper (or on a floured surface). Roll out the dough to roughly fit the baking tray. Place in the baking tray and push out to the sides to fill the tray.

5. Prick the surface of the dough evenly several times with a fork. Place the tray in a pre-heated oven at 160°C for approx 30 minutes until the shortbread is crisp and golden in colour.

Daim Bar Cake

6. Allow the shortbread time to cool naturally for about an hour or so. Do not refrigerate.

7. Unwrap the Dime/Daim bars and place in a food bag. Use the mallet to smash the Dime/Daim bars into small pieces of varying sizes (no more than about 1cm?;)

8. Pour the double (whipping) cream into a (new/cleaned) mixing bowl and whisk until the mix starts to firm up.

9. Add the smashed up Dime/Daim bars to the cream and mix together.

10. Using a spatula spread the cream mix over the shortbread base.

Daim Bar Cake

11. Place the baking tray in a refrigerator and allow to cool.

When ready to serve cut into rectangles of the desired size. You should expect to get about 20 pieces from the tray (4 x 5 grid)

Parser Error Message: Ambiguous match found

Symptom

On deploying a newly precompiled web site, the error message as displayed by ASP.NET is as follows:

Server Error in ‘/’ Application.


Parser Error

Description: An error occurred during the parsing of a resource required to service this request. Please review the following specific parse error details and modify your source file appropriately.

Parser Error Message: Ambiguous match found.

Source Error:

Line 1:  <%@ control language="C#" autoeventwireup="true" enableviewstate="false" inherits="WebSiteControls_Offers_TypeFilter, App_Web_typefilter.ascx.bd9a439f" %>

Line 2:
Line 3: <%--

Source File: /WebSiteControls/Offers/TypeFilter.ascx    Line: 1


Version Information: Microsoft .NET Framework Version:2.0.50727.3053; ASP.NET Version:2.0.50727.3053

System Information

This may or may not be relevant, but it reflects my set up at the time I discovered this issue.

  • Web Site project
  • Visual Studio 2008
  • .NET 3.5
  • IIS 6.0

 

Reason

The reason for this is that a control, in this case a Repeater, in the User Control had a similar name as a field on the code behind. The only difference between the two the case. One was called “offerTypes” (in the code behind, an IEnumerable containing entities from the business layer), the other “OfferTypes” (a ASP.NET Repeater control).

Solution

Rename one or other so that the names differ by more that case alone. In my case, I renamed the field referring to the entities to offerTypeInformation.

32bit ASP.NET App on 64bit Windows 7

Recently we all moved to Windows 7 (64bit) on our development machines. With that, we moved also to IIS7.
So that meant that development copies of websites that we are working on have to
be moved to IIS7. This should be simple. Right?

When I attempted to get the first site up and running I got hit by the error “Could not load file or assembly ‘System.Web’ or one of its dependencies. An attempt was made to load a program with an incorrect format.“.
(The full error message is below.)
After searching around I eventually managed to piece together that the reason
was that IIS was trying to run in 64 bit mode and this application was compiled
as a 32 bit application. Okay…. So how do you fix this?

It turns out the fix is rather easy. In the IIS Manager, open up the list of
Application Pools and select the pool for your application. Right-click and
select “Advanced Settings”. From that point you get this dialog:

32-bit application

Just set “Enable 32-bit applications” to “True”, then Okay the dialog. You then
have to recycle the Application Pool for the change to take effect.

 

 

Server Error in ‘/’ Application.


Could not load file or assembly ‘System.Web’ or one of its dependencies. An attempt was made to load a program with an incorrect format.

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.BadImageFormatException: Could not load file or assembly ‘System.Web’ or one of its dependencies. An attempt was made to load a program with an incorrect format.

Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Assembly Load Trace: The following information can be helpful to determine why the assembly ‘System.Web’ could not be loaded.


WRN: Assembly binding logging is turned OFF.
To enable assembly bind failure logging, set the registry value [HKLMSoftwareMicrosoftFusion!EnableLog] (DWORD) to 1.
Note: There is some performance penalty associated with assembly bind failure logging.
To turn this feature off, remove the registry value [HKLMSoftwareMicrosoftFusion!EnableLog].

Stack Trace:


[BadImageFormatException: Could not load file or assembly 'System.Web' or one of its dependencies. An attempt was made to load a program with an incorrect format.]
   System.Reflection.Assembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection) +0
   System.Reflection.Assembly.InternalLoad(AssemblyName assemblyRef, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection) +416
   System.Reflection.Assembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection) +166
   System.Reflection.Assembly.Load(String assemblyString) +35
   System.Web.Configuration.CompilationSection.LoadAssemblyHelper(String assemblyName, Boolean starDirective) +190

[ConfigurationErrorsException: Could not load file or assembly 'System.Web' or one of its dependencies. An attempt was made to load a program with an incorrect format.]
   System.Web.Configuration.CompilationSection.LoadAssemblyHelper(String assemblyName, Boolean starDirective) +11207304
   System.Web.Configuration.CompilationSection.LoadAllAssembliesFromAppDomainBinDirectory() +388
   System.Web.Configuration.CompilationSection.LoadAssembly(AssemblyInfo ai) +232
   System.Web.Configuration.AssemblyInfo.get_AssemblyInternal() +48
   System.Web.Compilation.BuildManager.GetReferencedAssemblies(CompilationSection compConfig) +210
   System.Web.Compilation.BuildProvidersCompiler..ctor(VirtualPath configPath, Boolean supportLocalization, String outputAssemblyName) +76
   System.Web.Compilation.CodeDirectoryCompiler.GetCodeDirectoryAssembly(VirtualPath virtualDir, CodeDirectoryType dirType, String assemblyName, StringSet excludedSubdirectories, Boolean isDirectoryAllowed) +11196482
   System.Web.Compilation.BuildManager.CompileCodeDirectory(VirtualPath virtualDir, CodeDirectoryType dirType, String assemblyName, StringSet excludedSubdirectories) +185
   System.Web.Compilation.BuildManager.EnsureTopLevelFilesCompiled() +551

[HttpException (0x80004005): Could not load file or assembly 'System.Web' or one of its dependencies. An attempt was made to load a program with an incorrect format.]
   System.Web.Compilation.BuildManager.ReportTopLevelCompilationException() +76
   System.Web.Compilation.BuildManager.EnsureTopLevelFilesCompiled() +1012
   System.Web.Hosting.HostingEnvironment.Initialize(ApplicationManager appManager, IApplicationHost appHost, IConfigMapPathFactory configMapPathFactory, HostingEnvironmentParameters hostingParameters) +1025

[HttpException (0x80004005): Could not load file or assembly 'System.Web' or one of its dependencies. An attempt was made to load a program with an incorrect format.]
   System.Web.HttpRuntime.FirstRequestInit(HttpContext context) +11301302
   System.Web.HttpRuntime.EnsureFirstRequestInit(HttpContext context) +88
   System.Web.HttpRuntime.ProcessRequestInternal(HttpWorkerRequest wr) +11174792


Version Information: Microsoft .NET Framework Version:2.0.50727.4927; ASP.NET Version:2.0.50727.4927

Tip of the Day #18: Storing User Input in XML

If you are going to dump user generate input into XML please remember to escape appropriately. For example, the ampersand symbol has special meaning in XML and you must escape it. e.g. & becomes &amp;

Why can't customer services actually be helpful?

I’ve noticed that when ever I have to get in touch with customer services for after sales advice then I get a really bad service. I tend to get stock answers that suggest the person who responded didn’t really read my inquiry. Often they will tell me how important I am, but that really is not the impression I get. I don’t want stock answers. I don’t want to be told how important I am when it is so obvious that it really isn’t true.

However, sometime I see examples that really make me think: Didn’t someone in management at least care enough to stop stuff like this happening.

The example I’m talking about is from Halfords. I was looking for a download of the instructions for my bike rack as I’d lost them a while ago. When I searched for them I found a forum on Halfords website attached to the product that where someone had asked that very question: “Would be it possible for you to send me a complete set of instructions for this bike rack. I have lost mine.”. Now rather than actually answer the question Halfords responded with “The Halfords Bike / Cycle carrier range are designed to be universal. The range of products will fit most cars (This carrier is not recommended for cars with spoilers) The Halfords Rear Mount 3 Cycle Carrier should fit your model of car. Halfords colleagues in any one of our stores will be able to assist you in selecting the best bike carrier for your needs and provide fitting if required

Halford's forum

Ummm… okay. That’s just paraphrasing the product description already.

Luckily, someone, another customer I presume, was at least more helpful and showed how to find the instructions on the website. More helpfully, they also included a direct link to the instructions to make it easy.

Why couldn’t Halfords have done this? Why did they think they could get away with fobbing off the customer with stock drivel?

DDD Scotland: The feedback

I’ll be standing down as chairman of Scottish Developers shortly. I’m not leaving, I’m just looking for a different role within the user group. But as I’m hanging up that particular hat I felt I would add my own personal feedback to the feedback we have received over the last three DDD Scotland events.

On the whole the DDD events in Scotland have been exceptionally well received, especially considering the amount of polish in the main DDD events that happen in Microsoft’s UK HQ in Reading. I suppose from my perspective, sitting on the inside, seeing exactly how it is hanging together I’m always amazed that the feedback is so positive. It is a testament to all the other organisers, speakers and helpers on the day that help keep the event running smoothly.

That said, I feel there are some people that are not fully aware of what goes into organising this event, and on what kind of a budget that we manage it. The speakers are not paid, they volunteer their time, travel, and accommodation expenses.

What makes a DDD event?

The goals for DDD events, as a whole are:

  • The event is free to delegates
  • There are no marketing talks.
  • No speakers from Microsoft.
  • The sessions are voted on to the agenda prior to the event.

You don’t have to aim for everything on this list, and at DDD Scotland we didn’t for various reasons.

We do our best to ensure that it is free to delegates. And we’ve managed that three years running and we will continue to do that for as long as we can.

What follows is some information about how we run the events based on the feedback we have received. I hope this gives you a little insight in to the event. We have received other feedback too that we will be acting upon to improve the event, and if possible we will act on the feedback below to improve things too. I just really wanted to get over the constraints we were running under so you didn’t think that after three years we weren’t listening. We do value all feedback.

The event is “not very well advertised”

We have no advertising budget so we use social media such as blogs, twitter and the like to get the word out. However, even with no advertising we were still oversubscribed. The event “sold out” in 15 days, 3 hours and 35 minutes. After that it went to a waiting list. At its peak there were 88 people on the waiting list.

“Tons of people who signed up didn’t come, maybe the people on the waiting list should the get the spaces of the people who didn’t turn up.”

We do our best on this count. If people realise they cannot come they can unregister which means the website will invite someone on the waiting list along. However, if someone doesn’t want to go and they also don’t unregister then there is little we can do about it.

Over the three years we have run the event we have had a 30%-33% drop out rate and we take that into account when allowing people to sign up. For example, this year we allowed 280 people to sign up before the waiting list was activated, in the end we had 188 delegates turn up on the day. We were aiming for 200 delegates, the same as last year. Last year we had 176 turn up.

In the 24 hours prior to the event the waiting list dropped by 60% allowing nearly 50 people that otherwise would not have had the opportunity to go a chance to get to the event.

“As you go feedback [is] better than a few days later”

This is interesting. There is a debate between some of the various DDD organisers as to which is better: online feedback after the event or paper based during the event.

I can’t say which provides better quality feedback. However, my particular take is that I want to get feedback to the speakers as quickly as possible. The sooner a speaker gets feedback the quicker they can see issues they may have and resolve them, or the quicker they can see what is working or what people appreciate about their presentation.

I also feel that when I get asked for feedback immediately I have a better recollection of what I’m feeding back on. If I wait a couple of days the memories are fading and I won’t recall something that would have helped the speaker improve.

We did a little experiment, with one of the speaker’s permission. He happened to run the same session at DDD8 in Reading as well as at DDD Scotland this year. We compared the feedback he got on line for DDD8 versus the paper feedback he got from DDD Scotland. Roughly the same percentage of the audience filled in feedback, of those that did, roughly the same percentage filled out comments (which is more valuable to the speaker than the raw ratings do). However, where there was a significant difference was that the amount that was written in the freeform feedback was greater online… Perhaps, as developers, we are so used to typing that we can’t write so much these days. But then, was all that extra written material an increase in quality as well as quantity?

I guess the debate will continue on that front.

“Make it a 2 day event, do it twice a year (or more)”

I’d love to do that. It is just very hard work for doing it once a year. But the two day idea is certainly something I’d entertain.

Scottish Developers have noticed that if we run day events during the week we do get a slightly different audience so perhaps it would be an excellent way of reaching out to those that can’t make a Saturday.

Incidentally, the Saturday concept was so that the event didn’t impact on the working week so that contractors didn’t lose a days pay and so that the genuinely enthusiastic could continue geeking out at the weekend.

“The lunch break was too short”

The lunch break was 90 minutes. However, we do have activities on during the lunch break to keep people interested. Unlike previous years, this year we only had grok talks – these are short 10 minute talks that represent topics that would not suit a full presentation or a taster for a full length session.

“more in depth” – “not for beginners”

One of the key aspects of DDD events are their democratic nature. Some people may want more elementary sessions while others may prefer more in-depth sessions. But on the day, what is on the schedule was put there by votes. Every single session was voted in. Back up sessions are chosen by votes too. No session appears without having been voted in.

However, that said, when the ash cloud threatened to disrupt the travel plans of most of our speakers we would have been prepared to bend the rules on that one slightly just to be able to put on an event.

“put feedback forms in goodie bag”

Interestingly we did that last year and we didn’t get that many of them back. People either just didn’t realise they were there or forgot about them. Either way, it meant that the feedback was not as comprehensive as we’d have liked.

Putting the feedback forms on the seats works better for us we’ve found.

“The food was a bit disappointing” – “More coffee in breaks” – “Why not run a café?”

Yes, we know. Unfortunately our budget didn’t allow us to stretch any further than we did. The catering bill was by far the largest expense. Since GCU kindly donated the venue to us for the day the catering budget accounted for about 90% of the running costs.

The first year we ran we got feedback decrying the fact that we didn’t put on breakfast. Possibly this was due to some people who were used to the DDD events in Reading which have the backing of a big corporation that can afford, to us, such luxuries.

There is a café on the ground floor at the venue, however it is closed on a Saturday. We did, the first year, look in to opening it up but were told that was not possible.

Ultimately, what we can afford depends on how much sponsorship we can pull in. This year was particularly disappointing. We were lucky in that we still had a sizeable chunk of money left over from last year which, in fact paid for most of the conference this year. Next year we will be starting from a relatively clean slate financially. Simply put, the cupboard is bare.

“Too much Microsoft oriented presentations”

The conference is run as democratically as we can. If you want to see more non-Microsoft oriented technologies then we need more submissions from people offering non-Microsoft oriented sessions and then we need enough people to vote on them.

We also had people asking for more consistent tracks and we do our best to work in a route through the agenda so that a person who has a high level interest in a particular area, say web technologies, has a route from start to end. But ultimately we can only work with what we have, and what we have are sessions that were submitted and a set of votes telling us what people want to see.

For the future

There are a number of points above that we will try to fix, just as soon as we can. For example, we’d love to keep you all properly fed and watered during the day. We’d love to do this more often, have an event every 6 months or a two day event. We’d love to vary the city.

For these things we need more sponsorship. Not just the variety where a vendor gives us free licenses. I’m not saying we don’t want that. We love to get items from sponsors to use as prizes or swag, it keeps things interesting during the day, but ultimately we need things we can use and the bottom line is mostly money.

If you have any ideas that will help up put on a better event, especially if it helps us solve the issues we currently have then please get in touch at support@scottishdevelopers.com

Type Inference

Type Inference is a neat feature where the C# compiler can work out itself what type a reference to an object is. While this can be used for the developer to be lazy it is most useful when you are dealing with the exceptionally long type names that LINQ expressions can generate. It is also a requirement when dealing with anonymous types, because there simply is no type name that you can use.

For example:

var aString = “ABC”;
Type st = aString.GetType(); // System.String;

var aNumber = 123.45M;
Type nt = aNumber.GetType(); // System.Decimal

var aDate = DateTime.Now;
Type dt = aDate.GetType(); // System.DateTime

In each of the above examples the compiler works out from what is on the right side of the equals sign the type of the reference (or value) on the left hand side.

What is important is that the compiler has something it can work with. In other words, you must assign something.

var aString; // Illegal – Can’t infer type

// Computer says "no":
// Implicitly-typed local variables must be initialized

Also, because the variable is strongly typed from the get-go each time you assign something to it, it must be of the same type.

var aDate = DateTime.Now;
aDate = 123; // Illegal – Can’t change type

// Computer says "no":
// Cannot implicitly convert type 'int' to 'System.DateTime'