Friday, February 27, 2009

LINQ versus SqlDataReader versus DataSet performance

There is the potential that switching to DataSets rather than business objects will add a significant overhead and adversely affect performance.

  • "accessing data through the DataReader is a magnitude of 27-56 times faster than accessing data using the DataSet"
    http://www.devx.com/vb2themax/Article/19887/1954?pf=true
  • -------------------------------------------------------------------
    Strongly Typed DataSet Breakdown
    -------------------------------------------------------------------
    175.11% slower than SQL Data Reader
    216.26% faster than LINQ to SQL
    112.17% slower than LINQ to SQL Compiled
    -------------------------------------------------------------------
    
    Performance benchmarks for LINQ vs. SqlDataReader, DataSet - LINQ Compiled Queries: Part 2 http://www.devtoolshed.com/node/12

Tuesday, February 24, 2009

BitVector32 constructor examples

static void Main(string[] args)
{
    int[] testData = new int[] { 
        int.MinValue, int.MinValue + 1,
        -2, -1, 0, 1, 2, 
        int.MaxValue - 1, int.MaxValue };
    foreach (int test in testData)
    {
        Console.WriteLine(string.Format("{0} -> {1}", test, new BitVector32(test)));
    }

    Console.ReadLine();
}

Outputs:

-2147483648 -> BitVector32{10000000000000000000000000000000}
-2147483647 -> BitVector32{10000000000000000000000000000001}
-2 -> BitVector32{11111111111111111111111111111110}
-1 -> BitVector32{11111111111111111111111111111111}
0 -> BitVector32{00000000000000000000000000000000}
1 -> BitVector32{00000000000000000000000000000001}
2 -> BitVector32{00000000000000000000000000000010}
2147483646 -> BitVector32{01111111111111111111111111111110}
2147483647 -> BitVector32{01111111111111111111111111111111}

So while new BitVector32(-1) may seem a bit odd for a constructor the intention is to set all 32 bits on.

Removing the "warning CS3008: Identifier '__ASP' is not CLS-compliant" warning

I'd been getting "warning CS3008: Identifier '__ASP' is not CLS-compliant" when building a ASP.NET 2.0 website.

Compiler Warning (level 1) CS3008 occurs when:

A public, protected, or protected internal identifier breaks compliance with the Common Language Specification (CLS) if it begins with an underscore character (_).

In my case I was able to remove this warning by changing the CLSCompliantAttribute in the AssemblyInfo.cs of the website.

[assembly: CLSCompliant(false)] //Used to be set to true.

Note: It is a bit odd having an AssemblyInfo.cs for a website. It was linked in using the CompilerOptions property in the masterpage.

Nelson .NET User Group Presentation - Building fast and scalable websites with ASP.NET and SQL Server - 11th of March

Upcoming presentation

Rick Kiessig will be giving a presentation on Wednesday the 11th of March. Rick worked at Microsoft in Silicon Valley for six years as a Solutions Architect, before moving to Nelson two years ago, where he is now working as a consultant.

Title:
Building fast and scalable websites with ASP.NET and SQL Server

Abstract:
Overview of high-impact performance optimization principles that can be applied to all components that affect your website's performance, from the browser to the database. Hear about how browsers process a page and how you can use that information to decrease page load times. Learn how to achieve high-performance, end-to-end caching, along with several tricks that can dramatically improve the performance and scalability of ASP.NET and SQL Server.

Useful links:

When:
Wednesday 11th March 2009
Gather at 11:50 am, starting at 12:00 pm.
Approximately 1 hour plus pizza afterwards.

Where:
FuseIT Ltd,
Ground Floor,
7 Forests Rd,
Stoke,
Nelson
(Off Nayland Rd and behind Carters)

http://local.live.com/default.aspx?v=2&cp=-41.299774~173.236231&style=r&lvl=16&alt=-1000
or
http://maps.google.com/?ie=UTF8&om=1&z=17&ll=-41.299774,173.236231&spn=0.005239,0.010042&t=h

If you are parking on site, please use the parks marked FuseIT that are at the back of the site.

Giveaways:
A Microsoft Wireless Laser Mouse 5000

Catering: Pizza & Drinks
Door Charge: Free

RSVP to me if you are going to attend so I can guesstimate the food and drink requirements.

However, feel free to turn up on the day though if you can't commit at the moment.

Please feel free to invite anyone who may be interested in attending.

New Zealand Community SharePoint Conference

Planning has started for the New Zealand Community SharePoint Conference on 2/3 July 2009 (tentative dates) in Wellington.

You can register your interest in attending, speaking or sponsoring here: http://www.sharepointusergroup.net.nz/SPCONF/default.aspx

Monday, February 23, 2009

Seperating the ObjectDataSource Service and business objects

In hindsight this seems like a rather trivial example. Still, up until now I've always been configuring ObjectDataSource against a single class. This class provided the CRUD methods and the properties for whatever the data source was connected to.

An alternative approach is to separate the CRUD methods from the business object into a service object. E.g. :

Business object:

using System;

public class Car
{
    private string _vin;

    public string Vin
    {
        get { return this._vin; }
    }

    public Car(string vin)
    {
        this._vin = vin;
    }
}

Service:

using System;
using System.Collections.Generic;

public class CarService
{
 public CarService()
 {
 }

    public List<Car> Select()
    {
        List<Car> cars = new List<Car>();
        cars.Add(new Car("vin1"));
        cars.Add(new Car("vin2"));
        return cars;
    }
}

Default.aspx

    <form id="form1" runat="server">
    <div>
    
    <asp:GridView ID="GridView1" runat="server" DataSourceID="Objectdatasource1">
    </asp:GridView>
    <asp:objectdatasource ID="Objectdatasource1" runat="server" SelectMethod="Select" TypeName="CarService"></asp:objectdatasource>
    
    </div>
    </form>

Help getting IE6 off the web

IE6 has been with us for some time now and for a good part of that time it's been a real pain to develop for. It was made in simpler (more naive?) times before good CSS support was a sensible requirement for a web browser.

The problem most web developers now face is how to encourage everyone to upgrade to a more standards compliant browser.

Vincent HasselgÄrd posted some code to encourage IE6 users to upgrade their browsers. See Give IE6 users the message to upgrade or change

Will putting this code on developer focused sites help? Probably not. I'd be surprised to find a developer using IE6 by choice. Still, any step I can take in removing IE6 from the web is a step in the right direction.

Wednesday, February 18, 2009

Can the first day of a century fall on a Sunday? Proof by brute force

After reading Straight Dope: Can the first day of a century fall on a Sunday? I threw this code together. Not that I didn't believe their maths/logic, but I thought this would be a really easy thing to brute force with a couple of minutes coding.
        static void Main(string[] args)
        {
            Dictionary<DayOfWeek, int> fallsOnDay = new Dictionary<DayOfWeek, int>();
            foreach (DayOfWeek dow in Enum.GetValues(typeof(DayOfWeek)))
            {
                fallsOnDay.Add(dow, 0);
            }

            for (int year = 100; year < 10000; year += 100)
            {
                DateTime toTest = new DateTime(year, 1, 1);
                fallsOnDay[toTest.DayOfWeek]++;
            }

            foreach (KeyValuePair<DayOfWeek, int> dowCount in fallsOnDay)
            {
                Console.WriteLine(dowCount.Key + " " + dowCount.Value);
            }
            Console.ReadLine();
        }
Output:
Sunday 0
Monday 25
Tuesday 0
Wednesday 25
Thursday 0
Friday 25
Saturday 24
You change line 11 to easily test any other day of the year. For example:
DateTime toTest = new DateTime(year-1, 12, 31); //line 11 changed to test DOW before change in century.
Output:
Sunday 25
Monday 0
Tuesday 25
Wednesday 0
Thursday 25
Friday 24
Saturday 0

Monday, February 16, 2009

Caching with HttpRuntime.Cache

  string someObject = "FooBar";

  //Cache the value
  HttpRuntime.Cache.Add("somekey", someObject, null,
    DateTime.UtcNow.AddMinutes(1.0),
    System.Web.Caching.Cache.NoSlidingExpiration,
    System.Web.Caching.CacheItemPriority.Normal, null);

  someObject = null;      

  //Access the cached value
  someObject = (string) HttpRuntime.Cache.Get( "somekey" );

Friday, February 13, 2009

EnumHelper using generics to reduce casting

using System;
using System.ComponentModel;

namespace ConsoleApplication1
{
    public enum ThreadPriority
    {
        [Description("Highest")]
        Highest,
        [Description("Above Normal")]
        AboveNormal,
        [Description("Normal")]
        Normal,
        [Description("Below Normal")]
        BelowNormal,
        [Description("Lowest")]
        Lowest
    }

    class Program
    {
        static void Main(string[] args)
        {
            ThreadPriority threadPriority = EnumHelper<ThreadPriority>.Parse("AboveNormal");
            System.Diagnostics.Debug.Assert(threadPriority == ThreadPriority.AboveNormal);

            string description = EnumHelper<ThreadPriority>.EnumValueDescription(threadPriority);
            System.Diagnostics.Debug.Assert(description.Equals("Above Normal"));

            threadPriority = EnumHelper<ThreadPriority>.ParseOrDescriptionMatch("Below Normal", ThreadPriority.Normal);
            System.Diagnostics.Debug.Assert(threadPriority == ThreadPriority.BelowNormal);

            if (!EnumHelper<ThreadPriority>.TryParse("Highest", out threadPriority))
            {
                System.Diagnostics.Debug.Fail("TryParse expected to succeed");
            }
            System.Diagnostics.Debug.Assert(threadPriority == ThreadPriority.Highest);

            if (EnumHelper<ThreadPriority>.TryParse("Foo Bar", out threadPriority))
            {
                System.Diagnostics.Debug.Fail("TryParse expected to fail");
            }
        }
    }

    public static class EnumHelper<T>
       where T : struct, IComparable, IFormattable, IConvertible
    {

        /// 
        /// Static constructor to ensure T is an enum
        /// 
        static EnumHelper()
        {
            if (!typeof(T).IsEnum)
            {
                throw new ArgumentException("Type parameter must be an enum");
            }
        }     
  
        /// <summary>
        ///  Converts the string representation of the name or numeric value of one or
        ///  more enumerated constants to an equivalent enumerated object.
        /// </summary>
        /// <param name="value">A string containing the name or value to convert.</param>
        /// <returns>An object of type T whose value is represented by value.</returns>
        /// <exception cref="System.ArgumentNullException">value is null</exception>
        /// <exception cref="System.ArgumentException">value is either an empty string or
        /// only contains white space.  -or- value is a name, but not one of the named
        /// constants defined for the enumeration.
        ///</exception>
        public static T Parse(string value)
        {
            return (T)Enum.Parse(typeof(T), value);
        }

        /// 
        /// Converts the string representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.
        /// 
        /// A string containing the name or value to convert.
        /// When this method returns, contains the object of type T whose 
        /// value is represented by the value, if the conversion succeeded, or the first 
        /// value in the enum if conversion failed. This parameter is passed uninitialized.
        /// true if value was converted successfully; otherwise, false.
        public static bool TryParse(object value, out T returnValue)
        {
            Type underlyingType = Enum.GetUnderlyingType(typeof(T));
            bool supportedType = (value is string || value.GetType().Equals(underlyingType));
            if (supportedType && Enum.IsDefined(typeof(T), value))
            {
                //direct string or underlying type match
                returnValue = Parse(value.ToString());
                return true;
            }
            else if (Enum.IsDefined(typeof(T), Convert.ChangeType(value, underlyingType))) // May throw overflow exception. E.g. long.MaxValue to Int32
            {
                //underlying numeric type match after type conversion
                returnValue = (T)Enum.Parse(typeof(T), value.ToString());
                return true;
            }
            else
            {
                //Default to the first item from the enum
                string[] values = Enum.GetNames(typeof(T));
                returnValue = (T)Enum.Parse(typeof(T), values[0], true);

                //default(T) won't work for all enums.
                //E.g. if the underlying type for the enum is int default(T) will always return 0, for which there might not be a value.
            }

            return false;
        }

        /// <summary>
        /// Attempt to read the Description Attribute
        /// </summary>
        /// <param name="e">The enum value to read the description from</param>
        /// <returns>The description value for the enum, otherwise the enum value converted to a string.</returns>
        public static string EnumValueDescription(T e)
        {
            System.Reflection.FieldInfo EnumInfo = e.GetType().GetField(e.ToString());

            System.ComponentModel.DescriptionAttribute[] enumAttributes =
                (System.ComponentModel.DescriptionAttribute[])
                EnumInfo.GetCustomAttributes(typeof(System.ComponentModel.DescriptionAttribute), false);

            if (enumAttributes.Length > 0)
            {
                return enumAttributes[0].Description;
            }
            return e.ToString();
        }

        /// <summary>
        /// Converts the string representation of the name or numeric value of one or
        ///  more enumerated constants to an equivalent enumerated object.
        ///  If a direct match isn't found a match will be attempted on the description attributes.
        /// </summary>
        /// <param name="value"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static T ParseOrDescriptionMatch(string value, T defaultValue)
        {
            Type type = typeof(T);
            try
            {
                return Parse(value);
            }
            catch (Exception)
            {
                //Try Description Matching
                string[] names = Enum.GetNames(type);
                foreach (string name in names)
                {
                    T nameEnum = Parse(name);

                    string nameEnumValue = EnumValueDescription(nameEnum);

                    if (nameEnumValue == value)
                    {
                        return nameEnum;
                    }
                }

                return defaultValue;
            }
        }
    }
}

Thursday, February 12, 2009

Common Visual Studio settings

Tools > Options > Projects and Solutions > General > ...

  • "Track Active Item in Solution Explorer"
  • "Show Output window when build starts"

Tools > Options > Environment > Find and Replace ...

  • Automatically populate Find What with text from the editor

If you have a suitable level of OCD you can also show white spaces.
Edit > Advanced > View White Space. See Also Coding Horror - Whitespace: The Silent Killer

DataReader.ToInt32(0) versus Convert.ToInt32(dataReader[0])

DataReader.GetInt32() will not perform any type conversions; therefore, the data retrieved must already be a 32-bit signed integer. Convert.ToInt32() will be happy to convert from a double, for example.

I'd tend to err towards the stricter approach to avoid unforeseen casting issues. I.e. something changes in the database.

Checks for IsDBNull() may also be required.

You will also need to consider how to reference the columns. By name or by ordinal position. See Roughly 3% penalty for indexing SqlDataReader columns by string rather than int.

Monday, February 9, 2009

Fine grained authorization in ASP.NET 2.0+

I have the requirement for fine grained authorization control. I considered using the standard ASP.NET roles, but really need an extra "level" added.

For example, tasks or logical operations like "CanCreateNewDocument" map to logical roles in the organization like "Manager". Users are then assigned to logical roles rather than logical operations. Authorization checks are performed against logical operations.

There doesn't appear to be the ability to nest roles within roles. I.e. Role Inheritance.

Something like the Microsoft Authorization Manager may be able to fill the gap.

Using Membership, Role, and Profile outside of ASP.NET (3.5)

From Accessing the ASP.NET Authentication, Profile and Role Service in Silverlight. Use the following WCF services:

LINQ to SQL and LINQ to Entities

The blog entry Microsoft’s Data Access Strategy has some good points on when you would consider using LINQ to Entities versus LINQ to SQL.

Generating compiled help files - CHM with sandcastle

Setting up Sandcastle to build CHM files can be a bit fiddly using MSBuild files etc... The Sandcastle Help File Builder adds a GUI front end among other things.
Sandcastle, created by Microsoft, is a tool used for creating MSDN-style documentation from .NET assemblies and their associated XML comments files. The current version is the May 2008 release. It is command line based and has no GUI front-end, project management features, or an automated build process like those that you can find in NDoc. The Sandcastle Help File Builder was created to fill in the gaps, provide the missing NDoc-like features that are used most often, and provide graphical and command line based tools to build a help file in an automated fashion.

See also - MSDN: Recommended Tags for Documentation Comments (C# Programming Guide)