# Wednesday, July 07, 2010

LINQ Group by MAX Date Query

I've found some weird, wonderful and ridiculously complicated LINQ queries for getting the row with MAX(DATE) based on a key.  Most unnecessarily use lambda expressions, and some just had several interim steps.  I knew there had to be a better way, and found an unassuming post at the bottom of a StackOverflow page.

Here's my non-lambda'd, contrived example...  assuming you've got an EntityFramework model (i.e. context)

            //Get Client Order with (max) order date 
            var maxclientOrder = (from clientOrder in context.ClientOrders
                               where clientOrder.OrderDate ==
                               (from clientOrder2 in context.ClientOrders
                                where clientOrder2.ClientID == clientOrder.ClientID
                                    select clientOrder2.OrderDate).Max()
                                select clientOrder).ToList();

posted on Wednesday, July 07, 2010 3:02:18 PM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [1]
# Monday, July 05, 2010

Simple WPF Page Navigation From an MVVM ViewModel

Navigation in WPF is easy - unless of course you're trying to apply an MVVM pattern.  Most examples tell you about all the great things you can do with MVVM/WPF whilst brushing such things as navigation under the carpet.  I've not found one person who's adequately explained an MVVM example with 'all' of the facets you'll need when writing a real application.

I'm not going to tell you how to implement full-scale configurable multi-context navigation using MVVM, but I'll briefly discuss one approach to a nagging issue - that of triggering and controlling navigation from the ViewModel.  I'm also talking specifically about 'pages' here too, as I'm targeting a browser with this application.

WPF 'Page' objects expose a NavigationService property, which hooks into the WPF navigation framework.  This is very convenient and powerful.  MVVM effectively steers you away from doing anything behind your 'views', and tries to substitute the traditional coupling between view/controller/viewmodel (depending on the flavour) with reliance on data binding to give the viewmodel everything it needs to perform all the UI logic.

Your ViewModel isn't supposed to have any reference to (or knowledge of) your view.  This means you won't have a reference to the Page to be able to access its NavigationService.  There's other ways to navigate, like using more of a frame appropach (most of the examples so this), but if you want to navigate 'web-style' from Page1 to Page2 to Page3 etc - controlling this from your ViewModel, what do you do?

After messing around with quite a number of approaches I've currently settled for a very simple technique that doesn't feel 'too' dirty.  It became clear (in my case) that loading the ViewModel from the View is actually more appropriate and practical than loading the view from the ViewModel (through a DataTemplate mapping as others would suggest).  I found starting everything from the ViewModel paints you into something of a technical corner, as some core WPF functionality only exists at the view level.  You can of course write your own implementations, but I've always thought patterns are meant to 'help', and when they cease to help, you stop.

In our example, the Application object effectively sets things up by being the all-seeing eye on the navigation framework.

The code below simply sets the startup uri (from another library), and subscribes to the 'navigated' event, which will fire after every page movement.

    public partial class App : Application
    {

        private static NavigationService navigator;

        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            this.StartupUri = 
new
Uri("pack://application:,,,/MyPageLibrary;component/MyStartupPage.xaml"); } void App_Navigated(object sender, NavigationEventArgs e) { Page page = e.Content as Page; if (page != null) ApplicationHelper.NavigationService = page.NavigationService; } }
The ApplicationHelper class is a simple static implementation to provide the whole application with what is in effect a 'bus' service - the means to navigate, using the NavigationService injected from the Application.  I said this was simple.

    public static class ApplicationHelper
    {
        private static NavigationService navigator;

        public static NavigationService NavigationService
        {
            set
            {
                navigator = value;
            }
            get
            {
                return navigator;
            }

        }

    }
The ViewModel is then free to navigate whereever it likes (I'm constructing the pages as objects here with parameters to use in constructing the viewmodel, rather than using a uri).

    //Now navigate to the detail view
//Datacontext used to construct the ViewModel
MyNextPage nextPage = new MyNextPage(SomeDataContext);
ApplicationHelper.NavigationService.Navigate(nextPage);

I'm sure this will evolve again (like everything I'm finding with WPF), but for now this seems to perform my basic requirements


posted on Monday, July 05, 2010 3:00:20 PM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]
# Friday, April 09, 2010

LINQ to SQL Connection Strings with Class Library and Web.Config

Most Microsoft technologies that you can operate with a GUI come with some tradeoffs.  Things have certainly improved over the years and now something like the LINQ to SQL designer is pretty trouble free - unless of course you have something like this fairly common scenario:

I had a class library (Data Access), and decided to add LINQ to SQL classes for a new database that was being introduced.
This class library is also ultimately being consumed by WCF web services.  I have dev, test, prod environments, so I use ASP.NET Web Deployment projects to change configuration per environment for things like appSettings and connectionStrings.

It therefore followed that I wanted to configure the LINQ DataContext connection properties in web.config.  Out of the box you'll find your connection properties go into your Settings properties class, which gets a little bit in the way.

If you start playing around with the generated classes to change where you're getting the connection info from then any changes in the designer will wipe them out, so a (relatively) pain free approach to setting your connection safely is the following:


Go to your LINQ to SQL designer and remove the Connection String, and set Application Settings to False

Create a new partial class to mirror your DataContext, and set the constructor to retrieve from your alternative source...

using System;
using System.Linq;
using System.Configuration;
using System.Data.Linq;

namespace CodeBureau.Services.DataAccess
{
    public partial class MyDataContext : DataContext
    {
        public MyDataContext()
            : base(ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString, 
mappingSource) { OnCreated(); } } }

This will leave all your generated code intact, but will sort out your configuration woes.
posted on Friday, April 09, 2010 8:27:54 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]
# Monday, March 16, 2009

ASP.NET Data Binding - Accessing a parent data item from within a nested repeater

I'm maintaining an app at the moment that uses quite a few nested repeaters, and found that headers were being output when there was no data present.  It was found that the header was being written in the ItemTemplate of an 'outer' repeater, rather than as the HeaderTemplate of the 'inner' repeater.  The next problem was how to reference the outer repeater from the 'inner' HeaderTemplate...

The following will bind to a field called HeaderDescription.

<%# DataBinder.Eval(Container.Parent.Parent, "DataItem.HeaderDescription") %>

The parent of the inner item is it's repeater, so you have to go to it's parent to get the right RepeaterItem.  Why don't you just do the following you ask?

<%# DataBinder.Eval(Container.Parent.Parent.DataItem, "HeaderDescription") %>

..'cos it doesn't work - The Eval method expects a 'Control' as its first parameter.  There's other ways to do this server-side, but the first option is probably the easiest.

To complete the picture and only show when there's data you can add the following to the 'inner' repeater declaration

OnItemDataBound="ItemDataBound" Visible="false"

then..

        protected void ItemDataBound(object sender, RepeaterItemEventArgs e)
        {
            if (e.Item.ItemType == ListItemType.Item)
            {
                if (!e.Item.Parent.Visible)
                    e.Item.Parent.Visible = true;
            }

        }


This will ensure that you'll only show if you've bound a 'data' item (remember you're doing binding in the HeaderTemplate too).  You could also hook similar things into other events, but it's generally more convenient to put these things into events that relate to the actual control (pre_render's probably another good candidate as it will only get called once and you can check the count in the DataSource).


Technorati Profile
posted on Monday, March 16, 2009 10:28:27 AM (AUS Eastern Daylight Time, UTC+11:00)  #    Comments [0]
# Friday, February 13, 2009

Converting from scientific number format to decimal or string in C#

I'm a programmer, but there are a few things I'll admit I don't do much of (as I have little interest in learning).  Playing with numbers in scientific notation is one of them.

I was importing from a spreadsheet and needed to get "6.00234419836431E+15" to something readable.

I discovered that you need to specify a NumberStyle when you parse as a decimal, because by default you'll get an error as it expects an 'easy' conversion.

The number above had decimals, exponent, the lot - so the following line of code does the job nicely...


Console.WriteLine(decimal.Parse("6.00234419836431E+15", System.Globalization.NumberStyles.Any).ToString());
//Globalisation namespace just shown for clarity - you'll want to put this in a using statement :)


This gives you "6002344198364310". Sweet.

posted on Friday, February 13, 2009 4:39:34 PM (AUS Eastern Daylight Time, UTC+11:00)  #    Comments [0]
# Wednesday, January 28, 2009

CommaDelimitedStringCollection - for when you want to write a comma delimited string from a collection !

Amazing that I still find BCL classes every day in .NET to do simple tasks.  I had a feeling that something may exist but didn't expect to find it in the System.Configuration namespace.

using System.Configuration;

CommaDelimitedStringCollection strings = new CommaDelimitedStringCollection();

foreach(string item in myOtherCollection)
{
    strings.Add(item);
}

//Spit out your comma separated string
string output = strings.ToString();

Simple!

posted on Wednesday, January 28, 2009 3:32:30 PM (AUS Eastern Daylight Time, UTC+11:00)  #    Comments [1]
# Wednesday, December 31, 2008

Using local SQL 2005 Express databases, |DataDirectory| setting and SQLBulkCopy with SemiColon delimited text files

Had some fun with this today as I was utterly confused why I wasn't able to save data to my SQL2005 express DB (linked within a Windows Forms VS 2005 project), even though I wasn't getting any errors from a SQLBulkCopy operation.  The connection string was set (from the wizard) to use '|DataDirectory|\dbname.mdf.

It turned out of course that the reason there wasn't an error was because the data was being written fine, but just not to the database I was looking at.  By default any database linked in your project file will be copied to the output folder (bin\debug) when you build.  It does this 'every time'.  I thought I was going mad as I wasn't getting primary key violations trying to insert the same data over and over again!

The useful info is in this MSDN blog entry.  It talks about different options for working around the local databases, but I plumped for a subfolder (called data) in my project, then setting 'Copy if newer' on the MDF file rather than 'do not copy'.  This just means that if you make a schema change then it will blow everything away (probably what I normally want in debug-land).

The reason I was using the SQLBulkCopy class was so I could take in a dataset picked from a semicolon-delimited text file. 
The first thing to do was read the text file into a dataset - and after some mucking about with the Jet OleDb provider and avoiding changing registry entries (to get it to recognise ';' as the delimiter) I found that a schema.ini file is what's required.

Here's some quick code I then used to get a DataSet from a semicolon-delimited Text File:

       
       /// <summary>
        /// Import from a given file to database
        /// </summary>
        /// <param name="fileName">File to import from (CSV)</param>
        
public void Import(string fileName)
        {
            
if (File.Exists(fileName))
            {
                SqlConnection connection 
= new SqlConnection(Properties.Settings.Default.RBStatsConnectionString);
                
//Get Dataset from file
                
DataSet importData GetDataSetFromFile(fileName);

                
//Import using SQLBulkCopy
                
SqlBulkCopy copyManager = new SqlBulkCopy(connection.ConnectionString, SqlBulkCopyOptions.UseInternalTransaction);
                
copyManager.DestinationTableName "TShirtStatistic";
                
copyManager.BatchSize importData.Tables[0].Rows.Count;
                try
                
{
                    copyManager.WriteToServer(importData.Tables[
0]);
                
}
                
catch (Exception exception)
                {
                    Console.WriteLine(exception)
;
                
}
                copyManager.Close()
;
                
connection.Close();
            
}


        }

        
public DataSet GetDataSetFromFile(string fileName)
        {
            
//Ensure we've got a schema file to fudge the semicolon delimited text
            
string schemaFile Path.Combine(Path.GetDirectoryName(fileName), "schema.ini");
            
File.Delete(schemaFile);
            string 
schemaContents "[" + Path.GetFileName(fileName) + "]\nColNameHeader=True\nCharacterSet=ANSI\nFormat=Delimited(;)";
            
StreamWriter writer File.CreateText(schemaFile);
            
writer.Write(schemaContents);
            
writer.Close();

            string 
connectionString "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + Path.GetDirectoryName(fileName) + "\\;Extended Properties='text;HDR=Yes;FMT=Delimited'";
            string 
query "SELECT * FROM " + Path.GetFileName(fileName);
            
OleDbConnection connection = new OleDbConnection(connectionString);
            
OleDbCommand command = new OleDbCommand(query, connection);

            
connection.Open();

            
OleDbDataAdapter adapter = new OleDbDataAdapter(query, connection);
            
DataSet ds = new DataSet();
            
adapter.Fill(ds);

            return 
ds;

        
}
posted on Wednesday, December 31, 2008 11:40:27 AM (AUS Eastern Daylight Time, UTC+11:00)  #    Comments [0]
# Thursday, November 20, 2008

Minify your Javascript using JSMin to minimise client downloads

This isn't a new thing, but I just came across a C# console app class, ported from JSMin to help with 'minifying' (or uglifying) your Javascript.  You can find it here.

Just create a new C# console app, pass in the 'input file' and 'output file' and away you go...

posted on Thursday, November 20, 2008 11:16:10 AM (AUS Eastern Daylight Time, UTC+11:00)  #    Comments [0]
# Saturday, October 18, 2008

Updated DeletedOld CodeProject Article

After an extremely long time I've made an update to the DeleteOld console app and updated the CodeProject article.  The original was .NET 1.1 - this one's now 2.0 (I'm not on 3 yet!)

It still does the same things it did - deletes files of a specified age, but now also:
  • Allows files to be deleted 'newer' than a certain age as well as 'older'
  • Allows files to be deleted based on an absolute date (overriding the timeframe arguments)
  • Allows the 'root' path to be preserved if 'remove empty folders' is selected and the path specified is empty.
  • Fixed a bug in Arguments parsing regex as noted by dudik
  • Changed output datetime format to 'full' rather than specific dd/mm/yyyy as noted by a.plus.01
I had a need for the absolute date, so thought I may as well cover off a few other things. 

Hope it's of some use...

You can also see my other CodeProject articles.

posted on Saturday, October 18, 2008 8:32:58 AM (AUS Eastern Daylight Time, UTC+11:00)  #    Comments [0]
# Friday, August 15, 2008

Issues with data binding, Typed Datasets, DataRows and Null Values

Had some fun this morning as we're trying to get a simple framework set up for a web app, that others can understand, using out-of-the-box functionality if possible.  I tried to avoid the normal tendency to put the 'architecture hat' on and overcook it.  If you can't be bothered to read all of this then just scroll to the bottom for the 'take home' on binding against typed datasets with null values.

We started off the prototype with a typed Dataset (including a couple of DB tables, and key relationships between each), a table adapter for each table, and a DetailsView at the front end, bound to an objectdatasource, which in turn talks to a business object that calls the TableAdapter.  That's a lot for one sentence so from top to bottom...

DetailsView --> ObjectDataSource --> BusinessService --> TableAdapter --> Database

This all sounds pretty straightforward, and it is - unless (as in this case) you decide to have your business object pass back a single (strongly typed) DataRow for your front end to bind against.  This seemed reasonable at the time because I only ever want to show one row here.

You'll basically get a StrongTypingException for every null field you try and bind, because by default a null field value will raise an exception when accessed through its generated property. 

We got distracted for a while exploring this as the natural tendency is to think 'ah - I'll make sure it doesn't raise the exception' - treating the symptom rather than the cause.

It turns out that any non-string field in a typed dataset table can't have a default of null (or anything other than 'raise exception').  If you stuff in a _null value directly in the XSD your fields will disappear from the public properties (rather curiously) when you compile - so that's not an 'enterprise' option.

I tried changing the return type in my business method to a generic DataRow to try and force the data binding to NOT use the strongly typed properties (getting warmer but still no dice.  I knew I was missing something really simple...

I then stumbled across a post (bottom of the page) that illustrated databinding will use different methods to bind based on what interfaces it finds.  The binding mechanism basically has no choice but to use the strongly typed public properties on a strongly typed or generic DataRow, as there's no way for it to successfully enumerate the values.  If you bind against almost anything else you'll most likely have no issues (it appears the databinding doesn't rely solely on ITypedList).  We're now binding against a typed DataTable (rather than generic DataView) and all's fine.  If you want (rather) more info on ITypedList then look here.

So...

Don't databind against a typed (or untyped) DataRow!

posted on Friday, August 15, 2008 1:45:43 PM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [1]
# Tuesday, July 22, 2008

Refactoring the inefficient data loop

OK. Last one for today.  I'm not going to go into too much detail except to say that the offending piece of code loads all items in a 'matrix' database table and builds a business object collection hierarchy.  This table has 4 fields, one or more of which may be NULL - for different meanings.  I really don't particularly like this type of approach but changing it right now would involve quite a bit of work.

The table looks something like

Level1ID
Level2aID
Level2bID
Level2cID

This ultimately translates to a business object hierarchy of:

 

  • Rows with Level1ID (only) - all other fields null - are Level1 objects
  • Rows with Level1ID and Level2a ID (only) are Level2a objects

etc...

 

The matrix table just contains the primary keys for each object so the code loads each one in the style below...

 

Load matrix
for each row in matrix
{
 if row type == Level1
     Add to Level1Collection
 else if row type == Level2a
 {
  Load Level2a object
  Add Level2a object to Level1[Level1ID].Level2aCollection
 }
 else if row type == Level2b
 {
  Load Level2b object
  Add Level2b object to Level1[Level1ID].Level2bCollection
 }
 else if row type == Level2c
 {
  Load Level2c object
  Add Level2c object to Level1[Level1ID].Level2cCollection
 }
}

 

This seems reasonable enough (logical anyway) given the way the data's being retrieved.

This does however load another several hundred rows from more stored proc calls that load each new object into the child collections.  This whole thing consistently takes around 8 seconds.

 

The Refactoring

 

A wise man once told me that if you can be sure the bulk of your operation is data access then lump it all together if you can, and do the loop or other grunt processing on the client. 

  1. I created a new Stored Procedure to return 4 result sets.  These come back as 4 tables in the target DataSet.  Each resultset is qualified by the same master criteria, and just uses joins to get a set of data that can be loaded directly into the collections.  The original stored proc is no longer required and this is now the only data access call.
  2. I changed my collection classes slightly to allow a LoadData method which takes in a dataset, a tablename and a parent key.  This means I can add Level2a objects to the appropriate Level1 collection.  The pseudo code now looks like...

Load multiple results
for each row in matrix
{
 if Level1 Results present 
   LoadData on Level1Collection
    
 if Level2a Results present
  for each Level1 row
   LoadData on Level1[Level1ID].Level2aCollection
   
 if Level2b Results present
  for each Level1 row
   LoadData on Level1[Level1ID].Level2bCollection

 if Level2c Results present
  for each Level1 row
   LoadData on Level1[Level1ID].LevelcbCollection
 
}

As I said at the beginning, there are some definite improvements to be made from changing the data structure, and a lot of this code could look a lot nicer by using Typed Datasets with relationships defined.

 

The new approach actually completes in less than 100ms.  I couldn't actually believe it myself, and made sure I killed connections, cache etc to make sure the database was coming in cold.  Still the same.

 

This basically proves that for data-heavy operations, things really start to hurt when you're making repeated client round-trips, however small the call.  This is basically a 99% saving in load time for this business object. 

 

The resulting page is also really snappy now and I'm sure the customer won't even notice :-)

 

posted on Tuesday, July 22, 2008 5:15:02 PM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]

Refactoring the blindingly obvious - Enums and Value Names

I know many people have written about this one, but it's cropped up yet again in the app I'm maintaining.  The old chestnut of setting a string based on an enum value - when the enum names are identical to the string values (most often with one word names). 


public enum ItemStatus
{
    Draft 
1,
    Pending 
2,
    Released 
3,
    Recalled 
4,
    Rejected 
5,
}


switch (item.ItemStatus)
{
    
case ItemStatus.Draft:
        {
            statusLabel.Text 
"Draft";
            break;
        
}
    
case ItemStatus.Pending:
        {
            statusLabel.Text 
"Pending";
            break;
        
}
    
case ItemStatus.Released:
        {
            statusLabel.Text 
"Released";
            break;
        
}
    
case ItemStatus.Recalled:
        {
            statusLabel.Text 
"Recalled";
            break;
        
}
    
case ItemStatus.Rejected:
        {
            statusLabel.Text 
"Rejected";
            break;
        
}
}


//or alternatively just take the 'name' as a string and get rid of the switch altogether
statusLabel.Text communicationItem.CommunicationItemStatus.ToString("g");


Colorized by: CarlosAg.CodeColorizer

There's other ways to do this if you want more of a 'description' (e.g. multiple words).  The StringEnum class could do it for you, or you could provide your own implementation with a Description attribute on the values - e.g.  [Description("My extended value name")]

posted on Tuesday, July 22, 2008 10:52:28 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]

Refactoring below the surface. Things aren't always as they seem

Continuing the refactoring from last week, I spotted another great example this morning that I 'almost' didn't improve at all.

My 'entry point' into this refactoring was trying to eradicate dynamic SQL and also the dreaded SELECT *, so this snippet popped up in my search.
My immediate reaction was to refactor the dynamic SQL to a Stored Procedure, but then I took a step back and had a further look at the code.

The following has been changed slightly (naming) to protect the innocent. 

string listOfIDs "";
foreach 
(Organisation organisation in dataObject.DistributionList.Organisations.Values)
{
    listOfIDs +
"'" + organisation.OrganisationID + "',";
}
string sql "";
if 
(listOfIDs.Length > 0)
{
    listOfIDs 
listOfIDs.Substring(0, listOfIDs.Length - 1);
    
sql "SELECT * FROM tblOrganisations WHERE OrganisationID IN (" + listOfIDs + ") ORDER BY [Name]";
}
else
{
    sql 
"SELECT * FROM tblOrganisations WHERE OrganisationID = '' ORDER BY [Name]";
}
Database database 
Database.GetDefaultConnection();
DataSet dsOrganisations database.Execute(sql, Database.CommandExecutionType.TSQLDS);
dgrConfirmDistributionList.DataSource dsOrganisations;
dgrConfirmDistributionList.DataBind();

int 
rowCount 0;
foreach 
(Organisation organisation in dataObject.DistributionList.Organisations.Values)
{
    
// Get the datagrid row we are working on

    
DataGridItem dataGridItem dgrConfirmDistributionList.Items[rowCount];

    
// Bind Target Divisions & Select Targeted Divisions
    
Divisions divisions = new Divisions();
    
divisions.Load(dataGridItem.Cells[0].Text);
    
DataGrid dgrConfirmTargetDivisions =
        
(DataGrid) dataGridItem.Cells[2].FindControl("dgrDivisions");
//more.....

 


What this code is doing is actually using an 'already populated' collection of 'Organisation's and performing a nasty dynamic SQL call (using an constructed IN clause) back to the database to retrieve the same data again so it can bind against a datagrid. 

It then goes back to using the original collection further on and does some more binding through the object hierarchy by linking to child controls in the grid.

It's clear here that we've got more going on than the need to just refactor the SQL code (dodgy table names etc).  It turns out that I've touched this code quite recently by refactoring what was a 'pseudo-not-quite' collection into a generic dictionary.  The original poor collection implementation was probably confusing enough to put the developer off trying to use that for data binding.  The collection now can of course be bound against the original collection with no need to go back to the database.

We end up with the following:

dgrConfirmDistributionList.DataSource dataObject.DistributionList.Organisations.Values;
dgrConfirmDistributionList.DataBind();

int 
rowCount 0;
foreach 
(Organisation organisation in dataObject.DistributionList.Organisations.Values)
{
    
// Get the datagrid row we are working on

    
DataGridItem dataGridItem dgrConfirmDistributionList.Items[rowCount];

    
// Bind Target Divisions & Select Targeted Divisions
    
Divisions divisions = new Divisions();
    
divisions.Load(dataGridItem.Cells[0].Text);
    
DataGrid dgrConfirmTargetDivisions =
        
(DataGrid) dataGridItem.Cells[2].FindControl("dgrDivisions");
        
//more.....    

This not only removed bad and unnecessary code that could have hurt performance, it also made me focus on 'real' refactoring opportunities.  It turned out that 90% of the remaining dynamic sql calls we actually duplicates of this code in other ASP.NET pages, and so that could be removed too. 

Refactoring can be quite like peeling an onion.  I'd been in this code a few days ago, and due to a small improvement made then (with the collection), I was able to come back and do some more.  The moral here is 'patience'.  If you're serious about improving all code you touch then you learn to make small, manageable changes that you can test before moving on to the next.  There's still work to do on this application but the code smells are starting to fade.

posted on Tuesday, July 22, 2008 10:14:16 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]
# Friday, July 11, 2008

Feelgood Friday - Refactoring with .NET Generics

As it's Friday I thought - let's do some cleaning up.  The following exercise is refactoring a class from an app I'm maintaining at the moment.  The app's fine (functional etc), and was originally a .NET 1.1 build.  A combination of this, a previous, straight conversion to .NET 2.0 (with the minimum of mods) and a variety of historical developers (mostly junior) means that the code has a certain amount of 'odour' in numerous places. 

Lack of consistency isn't the main problem, it's more about doing things smartly rather than taking the path of least resistance with your current knowledge.  The latter generally means you don't learn much, get much satisfaction or pride in your work, and you may end up a tad confused as to why you always end up with the 'less glamourous' projects.

Here's the original code.  A simple collection class containing custom MenuItem objects. 

    /// <summary>
    /// Represents a collection of MenuItems.
    /// </summary>
    
public class MenuItems
    {
        
private readonly ArrayList palMenuItems = new ArrayList();


        
// Add a MenuItem to the Collection
        
public void Add(MenuItem objMenuItem)
        {
            palMenuItems.Add(objMenuItem)
;
        
}

        
// Retrieve a MenuItem from the Collection
        
public MenuItem Item(string strTitle)
        {
            
if (palMenuItems.Count > 0)
            {
                
foreach (MenuItem objMenuItem in palMenuItems)
                {
                    
if (objMenuItem.Title.ToLower() == strTitle.ToLower())
                    {
                        
return objMenuItem;
                    
}
                }
            }
            
return new MenuItem();
        
}

        
// Returns True if the specified Key exists in the collection
        
public bool Exists(string strTitle)
        {
            
if (palMenuItems.Count > 0)
            {
                
foreach (MenuItem objMenuItem in palMenuItems)
                {
                    
if (objMenuItem.Title.ToLower() == strTitle.ToLower())
                    {
                        
return true;
                    
}
                }
            }
            
return false;
        
}

        
// Remove a MenuItem from the Collection
        
public void Remove(string strTitle)
        {
            
if (palMenuItems.Count > 0)
            {
                
foreach (MenuItem objMenuItem in palMenuItems)
                {
                    
if (objMenuItem.Title.ToLower() == strTitle.ToLower())
                    {
                        palMenuItems.Remove(objMenuItem)
;
                        break;
                    
}
                }
            }
        }

        
// Clear the collections contents
        
public void Clear()
        {
            palMenuItems.Clear()
;
        
}

        
// Return the Number of MenuItems in the Collection
        
public int Count()
        {
            
return palMenuItems.Count;
        
}

        
// Access to the enumerator
        
public MenuItemEnumerator GetEnumerator()
        {
            
return new MenuItemEnumerator(this);
        
}

        
#region MenuItem Enumaration Class

        
//=================================================================================================
        // Inner Enumeration Class:
        //=================================================================================================
        
public class MenuItemEnumerator
        {
            
private readonly MenuItems pMenuItems;
            private int 
position -1;

            public 
MenuItemEnumerator(MenuItems objMenuItems)
            {
                pMenuItems 
objMenuItems;
            
}

            
public MenuItem Current
            {
                
get return (MenuItem) pMenuItems.palMenuItems[position]}
            }

            
// Declare the MoveNext method required by IEnumerator:
            
public bool MoveNext()
            {
                
if (position < pMenuItems.palMenuItems.Count - 1)
                {
                    position++
;
                    return true;
                
}
                
else
                
{
                    
return false;
                
}
            }

            
// Declare the Reset method required by IEnumerator:
            
public void Reset()
            {
                position 
-1;
            
}

            
// Declare the Current property required by IEnumerator:
        
}

        
#endregion
    
}


Colorized by: CarlosAg.CodeColorizer

OK - Here's the issues I see with the code.

  1. You can spot some room for improvement immediately with variable naming (a weird variation of hungarian notation) - palMenuItems, objMenuItems etc.
  2. This is a simple wrapper implementation.  In conjunction with the naming you may conclude this is an ex-VB programmer who hasn't quite grasped object-oriented concepts yet (an observation - not a criticism).  If the developer knew about inheritance then they would have hopefully just inherited from one of the many applicable classes in System.Collections.  This would have also removed the need for implementing a custom Enumerator class.  Note that neither class actually inherits from any useful base class.  This is copy/paste pseudo-inheritance, and is rarely a wise idea.
  3. The Exists and Item methods use a simple loop mechanism to find the item they're looking for.  This is potentially wasteful with many items, and goes back to the 'path of least resistance' thought.  It's also using the inefficient 'ToLower' implementation to do comparison of what should be a key.  In practice the match doesn't need to be case-insensitive, and if it did then a String.Compare should be used with the appropriate 'ignorecase' argument).  The developer clearly isn't familiar (or maybe just not comfortable) with dictionaries or hashtables where access to items via a key is supported and far more efficient.
  4. The Count implementation is a method rather than a property (aargh!)
  5. It could be argued that the menuitem class itself (not shown here) is redundant as the ASP.NET framework does define a similar MenuItem class.  In reality the whole implementation could be replaced with a sitemap, a menu control, and some custom code, but I'll leave that out of scope for now.
That gives some pointers as to what 'could' have been done in .NET 1.1.  .NET 2.0 of course introduced generics and so let's follow that path.

The MenuItems class is used in a few places:

  • A menu control (that renders menu items)
  • Many pages that load items into the control (I already refactored some of this into inherited menu controls, where all pages shared the same 'dynamic' menu), but there's plenty more improvements to be made.

The first thing is to say...

I don't need a collections class...

The following is now in the Menu Control:

        //private MenuItems menuItems = new MenuItems(); - Out with the Old
        
private Dictionary<string, MenuItem> menuItems = new Dictionary<string, MenuItem>()//In with the new

        
public Dictionary<string, MenuItem> MenuItems
        {
            
get return menuItems}
            
set { menuItems = value; }
        }


Colorized by: CarlosAg.CodeColorizer

This simply uses a generic Dictionary object with a string key (as we find items by title).  Anywhere that references this dictionary will now need to add a 'key' in certain places (e.g. Add method), and the Item method will no longer work as that's gone.  That needs to change to a class indexer.

Fortunately Visual Studio's find and replace can cope with all of this as the code is largely consistent...

A quick compile should show all the patterns (from the errors) if there's any inconsistencies.  There were probably about 200 references to patch here with about 4 different variations of naming and scope - but it took about 5 minutes to patch (including writing this text :-))

We then have to patch all the 'Item' references.  These are almost always setting a 'selected' property (Stopwatch on...)

As there's only a few references here (about 20), I could patch them manually, but this could be done entirely by using regular expressions to do the find and replace.  http://msdn.microsoft.com/en-us/library/2k3te2cs.aspx shows all the options available.  I tend to only use regexp for large volume stuff where I get a return on my time investment to work out the expression! 

I'm just doing it in two steps without regexp as follows:

The code

            Menu1.MenuItems.Item(UserMenu.MyCommunicationItems).Selected = true;

becomes

            Menu1.MenuItems[UserMenu.MyCommunicationItems].Selected = true;

For extra points you then use Resharper to rename all of the obj and pal references to something reasonable, e,g, objMenuItem becomes menuItem.

Oh - and use Resharper's 'Find Usages' on the MenuItems class to confirm it can be deleted.

And relax.....


In hindsight I would have used Resharper to 'change signature' on the methods in MenuItems (to patch all the references first), then converted to the generic Dictionary.  Could have been a bit quicker.

Hope this gives someone some courage to get in there and improve their code.  I firmly believe you've just got to 'want' to do it and then you're biggest problem will be stopping yourself!


posted on Friday, July 11, 2008 10:06:52 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [2]
# Wednesday, July 09, 2008

Test out String.Format options online

Love this little tool.  It's making me think about writing some stuff for Silverlight!

I just plain can't remember the options for String.Format and standard/custom strings so being able to try them out is great.  Nice work!

I'd normally use John Sheehan's excellent Cheat Sheet, but I'll probably stick to the tool in future.

posted on Wednesday, July 09, 2008 5:13:49 PM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [1]
# Monday, June 16, 2008

If, For, Switch, but...

I love that Scott Hanselman challenges his own as well as others' thoughts on how to solve technical problems.

I'm not making any judgements about junior devs vs. senior devs. I'm talking about idioms and "vocab." I think that using only if, for and switch is the Computer Programmer equivalent of using "like" in every sentence. Like, you know, like, he was all, like, whatever, and I was like, dude, and he was, like, ewh, and I was like meh, you know?

There's some good examples on forcing yourself to learn new objects, features and constructs through refactoring.  If you don't refactor then you'll never know...

posted on Monday, June 16, 2008 9:32:26 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]
# Monday, June 02, 2008

Using RSS.NET to re-write an existing feed XML file

Don't know what happened to RSS.NET.  Looks like it's trying to go commercial, but still very quiet.  Code examples are few and far between (with some simple ones on the site).  Also on InformIT.

I'm writing a quick RSS console app (as I lost the last one I wrote!) that I can use to write an RSS feed from a SubVersion hook.  Most people use a pre-existing tool for Python (can't remember the name), but I thought I'd give RSS.NET another go just for kicks.

The example above works fine assuming you're maintaining state somewhere other than the feed xml file itself.  The example also assumes you're serving this up to the web.  You can override the RssFeed.Write() method to take a filename.

If run the same code again (assuming you've written to a file) it will simply overwrite it with one item (not add to it).  This isn't what I wanted so...

You need to

  1. Read the file back in if it's there - otherwise create
  2. Add your item to the existing channel if it's there - otherwise create
  3. Fix the date behaviour as RSS.NET always assumes UTC dates and appends 'GMT'.  The problem here is that if you're in Australia (like me) reading and rewriting the same items will effectively add several hours on to existing items every time, because you write the date you read back in for existing items (read and parse into region-specific date, then write back as is).  There's two ways to fix this:
    1. Before you add your new item - loop through all items and change the item.PubDate to item.PubDate.ToUniversalTime().  This effectively sets it back to the 'correct' date.
    2. Change the RSSWriter class in RSS.NET to convert ToUniversalTime for the Item.PubDate, Channel.PubDate etc.  This seems like a better option, but it has potentially more knock on effects in RSS.NET.  I'm here to achieve a result, not change the behaviour (possibly adversely) of RSS.NET so I chose option 1

So here's the code.  Not finished yet and rough around the edges, but works as I need.  The intention is to avoid the need for config files and configuring up of feeds specifically.  I just want a library function that's called by a console app.  The web serving will simply be based on the location of the file and pointing to some folder in IIS.

        private static void WriteFeed(string feedFileName, string feedName, string feedDescription, 
        string feedURL, string itemTitle, string itemDescription, 
        DateTime itemPublishDate, 
string itemURL)
        {
            
bool newFeed = false;
            
//Try and first open the feed (to see if it's existing)
            
RssFeed feed = null;
            try
            
{
                feed 
RssFeed.Read(feedFileName);
            
}
            
catch (FileNotFoundException ex)
            {
                feed 
= new RssFeed();
                
newFeed = true;
            
}
            
catch (Exception ex)
            {
                WriteError(ex)
;
                return;
            
}

            RssChannel channel 
= null;

            
//Loop through all channels and if we've got the same title reuse
            
for (int 0i < feed.Channels.Counti++)
            {
                
if (feed.Channels[i].Title == feedName)
                {
                    channel 
feed.Channels[i];
                    break;
                
}
            }

            
if (channel == null)
            {
                channel 
= new RssChannel();
                
feed.Channels.Add(channel)//might blow up if already there?
            
}

            RssItem item 
= new RssItem();

            
item.Title itemTitle;
            
item.Description itemDescription;
            
item.PubDate itemPublishDate.ToUniversalTime();
            
item.Link = new Uri(itemURL);

            
//To ensure we don't screw up existing dates - convert to UTC
            
foreach (RssItem existingItem in channel.Items)
            {
                existingItem.PubDate 
existingItem.PubDate.ToUniversalTime();
            
}

            
//Now add our new item
            
channel.Items.Add(item);

            
channel.Title feedName;
            
channel.Description feedDescription;
            
//channel.LastBuildDate = channel.Items.LatestPubDate();
            
channel.PubDate DateTime.UtcNow;
            
channel.Link = new Uri(feedURL);

            
feed.Write(feedFileName);
        
}

Colorized by: CarlosAg.CodeColorizer

 

 

posted on Monday, June 02, 2008 9:28:25 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]
# Tuesday, April 01, 2008

VB.NET - I know I shouldn't care but I just don't like it!

Aaargh! I thought to myself for the umpteenth time as I looked through my inherited VB.NET web site (it's bad enough that it was a web 'site' with dodgy auto-generated (i.e. NO) namespaces), but the following things are now officially going on my list of reasons I choose C# over VB.NET.  I know I shouldn't care as it all compiles to the same thing, but (just from this project) -

  • VB.NET still allows you to dodge the Option Strict and Option Explicit (definitely a 'Web Site', as there's no option in the property pages like for any other standard .NET project).  This means that you happily declare untyped variables, and do all sorts of dodgy late binding.  Some people obviously like this facility, but it's one of the things that holds VB programmers back in the world of object-oriented design.
  • Modules still exist!  OK you could argue this is just a public class with everything marked as static, but it's still 'global', and you don't need to reference the module name when you access a method or variable.  This often leads to the 'where the hell is that defined?' question
  • Variants still exist.  This is obviously an extension of the first one, but it's a big enough annoyance that people use to get themselves out of a (I don't know how to implement this properly using object-oriented techniques) hole
  • Syntax 'feel' - e.g. Me vs this, and MyBase vs base - it feels a bit 'Mickey Mouse'.
  • Verbose syntax causing RSI:
    • #Region "String Constant" vs #region whatever you like without having to put quotes around (C# regions can also be indented with the code unlike VB)
    • Global functions instead of operators like TryCast instead of 'as', CType instead of (Mytype)variable.  CStr, Cint etc still exist.  Not only is this more typing in many cases as you need to enter two or more parameters, it also feels like yet more 'baggage' from the bad old days, as you can still pass in your objects to generic 'library' functions rather than use methods on the object themselves (like ToString()).  Many VB programmers will lap this up because the language still allows them and they don't have to learn something new.
  • Case insensitivity.  OK I'll give you this one as it was one of the 'speed' things in VB6.  It does lead you back to the horrible pseudo-hungarian thing (for some people) though as you get naming conflicts with properties and variables if you follow the general pascal/camel C# standard (can't remember whether there's an option to switch that off though).  The alternative to the pseudo-hungarian notation is the _ prefix for class variables, but even that seems like too much of a concession.
  • Methods can still look like properties.  Call a method in VB without parameters and it will happily let you write MyMethod.  This just feels like inconsistency.
  • Tool support and productivity. 
    • VB.NET simply doesn't have the same support in Visual Studio or refactoring addins (ReSharper's only catching on to VB.NET now) This isn't the language's fault, but it's easier to be more productive through tools with C#.  (I know this is improved with every release of VS).
    • The default VS 2005 refactoring capability (limited but present in some form) for C# is basically non-existent for VB.NET.  Maybe Microsoft think VB programmers don't need refactoring support?
    • Intellisense is also generally rather lacking for VB.NET
    • XML Documentation comments (just now catching up, but was really lacking)
  • Angle brackets <> look uglier than square brackets [] for attributes (OK that's a bit picky!)

The bottom line is that VB.NET lets you be sloppy, like VB always did.  The problem is that a fair percentage of those who migrate from VB to VB.NET (not all I agree - don't shoot me!) take the same shortcuts that they always did - because they still can.  

My opinion is that on average you'll find more elegant design, better formatted and more object-oriented code from C# programmers as they're more likely to have come from a C++ or Java background.

Did I mention that I was a VB programmer for years, and have only dabbled a bit in Java and not really ever C++.  I loved VB6 (at the time) as I could get systems written quickly and well.  What I didn't love was how much backwards-compatible support it kept leaving in with each new version, just so people could upgrade their crap legacy code.  I've seen some very clear, consistent, well formatted and commented VB.NET code, but I've seen an awful lot more that's not.  The flip side is that C# coders often think they're great just because they're writing in C#.  I've seen plenty of horror stories there too, but the 'bad' percentage is much lower (sorry - it just is).

VB unfortunately doesn't encourage discipline in programmers, whereas C# benefits from a clean slate without the historical baggage.  I believe the slightly more formal language specification of C#, and the fact it attracts more 'OO' coders, tends to lead people to think a little more about design rather than just skipping straight to implementation (which is where the 'real' problem is :-) ).  You can write the same crap code in any language you like.  VB unfortunately just makes it easier.

posted on Tuesday, April 01, 2008 5:16:51 PM (AUS Eastern Daylight Time, UTC+11:00)  #    Comments [0]
# Thursday, November 08, 2007

Change the Default C# 'Add Class' template in Visual Studio 2005

There's a fairly major annoyance about VS2005 and the fact that when you create 'New Class' it effectively creates the class as private.  This article explains how to change it...

http://mark.michaelis.net/Blog/ChangingTheDefaultItemTemplatesInVisualStudio2005.aspx

posted on Thursday, November 08, 2007 8:37:22 AM (AUS Eastern Daylight Time, UTC+11:00)  #    Comments [0]
# Monday, September 03, 2007

Parse and TryParse

Like many people I'd mistakenly thought that xxx.Parse was the only way to validate types like ints and DateTimes.  This kindof 'was' the case in .NET 1.1, but numeric data could be validated with Double.TryParse then converted to the correct type.

In .NET 2.0 you can happily use the TryParse method on pretty much all the types you'd want to validate - without the nasty overhead of catching an exception.  This post goes into a bit more detail, and links to a tool to benchmark the results for yourself.

I'll be on the lookout to convert whereever I find int.Parse, as it's pretty rare you 'actually' want an exception to be thrown...

posted on Monday, September 03, 2007 9:58:53 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]
# Tuesday, August 07, 2007

Breaking Changes between .NET 1.1 and 2.0

I spotted an old post from Brad Abrams on breaking changes for .NET 1.1 apps recompiled with 2.0.  The links had moved but are still relevant...

http://msdn2.microsoft.com/en-au/netframework/Aa570326.aspx

My own observations from converting projects are a little higher level in that I'm also interested in what effort you're liable for when converting, and also things to watch out for if (like me) you're having to persist a 'shared' code base of 1.1 and 2.0 core libraries (temporarily) while you migrate all your client apps.

ASP.NET Projects.  This includes Web Service projects.  You'll find that you'll probably have to reconstruct these projects based on the content files and you'll also need to make a decision on the model you're going to use for debugging - i.e. whether to use the new debugging host (removing IIS from the debugging equation) and whether to use a dynamic or static port.  Steer clear of the 'Web Site' project too as this could leave you confused for hours wondering why you can't debug anything - look for the ASP.NET projects.  More info on the ASP.NET side can be found from Peter Laudati's post.  Microsoft also have an article covering conversion of ASP.NET projects

DataSets.  These have changed in that a 'designer' file is now the main source file (apart from the .xsd).  If you're sharing DataSets across 1.1 and 2.0 projects just share the .xsd file otherwise you'll be asking for trouble as the other generated files are incompatible.  Any structural changes to a DataSet in one project (say .NET 1.1) will not be reflected in the corresponding 2.0 project.  You'll need to check out the XSD in both places (assuming you're using VSS) and build both in order to avoid breaking one of the projects.

Windows Forms.  I've had a recent experience where the InitializeComponent() method in a form (edited with VS 2005) places calls that aren't compatible with .NET 1.1.  The code compiles (as below) but 'exception thrown by target of an invocation' gets thrown on the following line...

((System.ComponentModel.ISupportInitialize)(this.validatingProgress)).BeginInit();

This is because the control (a PictureBox in this case) doesn't implement ISupportInitialize in .NET 1.1 but does in 2.0.  The equivalent in 1.1 is SuspendLayout()

This would just be one example of something that's syntactically correct, but could fail at runtime.

posted on Tuesday, August 07, 2007 4:38:59 PM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]
# Friday, July 06, 2007

Cryptography - Using Base64 to encrypt/decrypt Strings rather than Unicode or ASCII - Error - 'Length of the data to decrypt is invalid'

I recently did a conversion from .NET 1.1 to .NET 2.0 for a particular project.  The framework classes were still in 1.1 (where our TripleDES encryption library lives).  Unit Tests still run in 1.1 and all pass.

A production problem then started to show 'Length of the data to decrypt is invalid' and I was horribly confused as I'd inherited some of the code and all seemed good...

I thankfully found the following ... http://blogs.msdn.com/shawnfa/archive/2005/11/10/491431.aspx that explained why it's really not a good idea to assume that even though you're only encrypting ASCII characters, you don't use 7 or 8 bit encoding to encrypt/decrypt.  The key is in the fact that the overall sequence of bytes isn't guaranteed to be valid Unicode or ASCII.

(Why do the unit tests pass?) - because the Cryptography classes in .NET framework were revamped for V2.0 and validation tightened up.  As Shawn says - it's better that it doesn't successfully decrypt into an invalid string.

This leaves a bit of a tidyup of course as I've now got to re-stuff all encrypted data into the database and patch the apps to ensure that the correct encryption is used.  I've also got to find a way to support existing files and string encrypted with the class as some code is still happily using this in 1.1-land and some is clearly 'not' working in 2.0-land - fun!

I guess it's a lesson that today's code won't necessarily work tomorrow - and you shouldn't discount breakages from framework changes when you're investigating issues.

posted on Friday, July 06, 2007 9:12:30 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]
# Friday, July 15, 2005

CodeProject Article - String Enumerations in C#

This new article talks about the options available for string enums in C# - just one of those little quandaries you find yourself up against from time to time....

http://www.codeproject.com/csharp/stringenum.asp

posted on Friday, July 15, 2005 7:32:41 PM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]
# Monday, February 21, 2005

CodeProject Article - Enhanced DataSet Quick Watch

This article runs through some amendments I've made to an already excellent VS Addin to 'visualise' datasets in the IDE.  There's definitely some improvements that can be made, but it shows how you can use the debugger to return useful information at runtime...

http://www.codeproject.com/csharp/dswatchex.asp

posted on Monday, February 21, 2005 7:37:26 PM (AUS Eastern Daylight Time, UTC+11:00)  #    Comments [0]
# Tuesday, February 01, 2005

CodeProject Article - DeleteOld Console Application

This new article goes through more of a journey of unit testing a console app than anything else - but might prove useful to people...

http://www.codeproject.com/cs/files/deleteold.asp

posted on Tuesday, February 01, 2005 7:30:59 PM (AUS Eastern Daylight Time, UTC+11:00)  #    Comments [0]