S h o r t S t o r i e s

// Tales from software development

Posts Tagged ‘C#

C# StorageValue type part 2: Struct

leave a comment »

Almost as soon as I started on a struct implementation I decided to change the type used to hold the storage value from the long (System.Int64) type used in the class implementation to a ulong (System.UInt64). I’d considered this previously and decided that a signed value might be useful for storage/memory calculations but, the more I thought about it the more I realised this didn’t make much sense and it would be better to use an unsigned value.

The class implementation of the StorageClass type in the previous post was useful to developing the code required to parse and format storage values. However, I knew that I really wanted the semantics and behaviour associated with the built in value types such as int.

Another change I considered was to use the implicit operator to allow conversion from a string value without an explicit cast.

With the current implementation, assignment looks like this:

StorageValue s1 = new StorageValue(100 * 1024);
StorageValue s2 = new StorageValue("1.5MB");

but I really wanted to be able to do this:

StorageValue s1 = 100 * 1024;
StorageValue s2 = "1.5MB";

In fact, it’s very easy to do this, whether the type is a class or struct, by implementing the implicit operator:

 public static implicit operator StorageValue(int value)
 {
    return new StorageValue(value);
 }
 
 public static implicit operator StorageValue(string value)
 {
    return StorageValue.Parse(value);
 }

Unfortunately, this is represents an abuse of what the implicit operator is designed for. To explain why, it’s worth considering the intent of the explicit operator first. Typically, when a conversion might fail or will potentially result in data loss, the compiler requires an explicit cast, e.g.

long gb = 1024 * 1024 * 1024;
int i = (int)gb // cast required

This conversion using a cast is implemented using the explicit operator. The point of this is to allow the conversion but only when the programmer uses a cast, i.e. it ensures that the programmer is aware of the potential for failure or data loss.

The implicit operator implements the conversion without requiring the cast, i.e. the programmer is not made aware of the possible failure or data loss when the conversion is performed.

So, while it would be nice to allow syntax such as

StorageValue s2 = "1.5GB";

it is not correct use of the implicit operator as this conversion could easily fail if the string is not correctly formatted, e.g.

StorageValue s2 = "Hello world!";

There is a much bigger issue with a struct implementation rather than a class implementation which is simply that it will be passed by value rather than by reference. For this implementation this is probably the correct behaviour but, again, it really comes down to what feels right.

Advertisements

Written by Sea Monkey

January 30, 2017 at 6:18 pm

Posted in Development

Tagged with ,

C# StorageValue type part 1: Class

leave a comment »

I’ve been meaning to write a class for the past few years for dealing with memory storage values.

The clinical data interfaces that I write often need to read and write disk and memory storage values. For example, the Vitality HL7 MLLP software that receives and sends HL7 messages via TCP/IP, writes incoming messages to disk and so it monitors disk free space to ensure that there is always sufficient disk space available. The required disk free space is held as a configuration file value. I could use a long value for bytes, or an int value for MB, but it would be more convenient to have a datatype that allowed me to express the value using the most appropriate units, e.g. as “500MB” or “0.5GB” rather than 524288000.

Conversely, these interfaces typically log the process working set size before and after processing to aid diagnosing memory problems. Again, it’s possible just to log the number of bytes but it’s easier to read and understand a working set size of, for example, 18.56MB rather than 19464932.

So, the class must implement parsing methods to take values such as “1234”, “123.4KB”, “12.34MB”, “1.234GB”, “1.234GB” and to provide formatting methods to display storage values according to a format string or default to an appropriate formatted value.

Most of the code already existed as discrete utility methods in my code. I just needed to bring them all together into a single class and implement the IFormattable interface so that an instance of the class can be formatted in composite formatting methods such as string.Format().

The formatting supported for parsing is a numeric value optionally suffixed with B, KB, MB, GB, or TB. For example:

StorageValue s1 = StorageValue.Parse("150MB");

The IFormattable formatting supported for conversion to string values is any of the suffixes above, optionally with a leading +/- to enable or disable the display of the suffix, and an optional space in front of the suffix to indicate that a space should be placed between the numeric value and the suffix. If not format is specified then an appropriate unit will be used. Examples:

StorageValue workingSetSize = Process.GetCurrentProcess().WorkingSet64;

// Let the StorageValue class choose the most appropriate units:
applicationLog.WriteInfo(string.Format("Working set size: {0}", workingSetSize));

// Use KB:
applicationLog.WriteInfo(string.Format("Working set size: {0:KB}", workingSetSize));

// Use MB with a space between the value and the suffix:
applicationLog.WriteInfo(string.Format("Working set size: {0: MB}", workingSetSize));

// Use MB but don't display the units:
applicationLog.WriteInfo(string.Format("Working set size: {0:-MB} megabytes", workingSetSize));

One of the big design decisions that needs to be made is whether this type should be a class or a struct. A class is the easy option as its behaviour is likely to be more intuitive as it avoids some of the issues like immutability that structs raise. However, because this type is a thin wrapper around the System.Int64 type, a struct might offer more intuitive syntax and semantics. I decided to write the implementation as a class and then re-work it as a struct to see how the two implementations compared.

The initial implementation as a class is below and the struct implementation will follow in a second post. (Hint: Wait for the struct version, I think it’s better…)

(StorageClass.cs):

namespace Vitality.Common.Types
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Text;

    /// <summary>
    /// Represents a memory or disk storage value.
    /// </summary>
    [Serializable]
    public class StorageValue : IFormattable
    {
        #region Private Constants

        /// <summary>
        /// Constant for one terabyte.
        /// </summary>
        private const long TeraByte = 1024 * 1024 * 1024 * 1024L;

        /// <summary>
        /// Constant for one gigabyte.
        /// </summary>
        private const long GigaByte = 1024 * 1024 * 1024L;

        /// <summary>
        /// Constant for one megabyte.
        /// </summary>
        private const long MegaByte = 1024 * 1024L;

        /// <summary>
        /// Constant for one kilobyte.
        /// </summary>
        private const long KiloByte = 1024L;

        /// <summary>
        /// Constant for one byte.
        /// </summary>
        private const long Byte = 1L;

        #endregion Private Constants

        #region Private Members

        /// <summary>
        /// The storage value in bytes.
        /// </summary>
        private long bytes;

        #endregion Private Members

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the StorageValue class.
        /// </summary>
        /// <param name="bytes">The number of bytes for the storage value.</param>
        public StorageValue(long bytes)
        {
            this.bytes = bytes;
        }

        #endregion Constructors

        #region Public Properties

        /// <summary>
        /// Gets or sets the number of bytes in the StorageValue.
        /// </summary>
        public long Bytes
        {
            get { return this.bytes; }
            set { this.bytes = value; }
        }

        #endregion Public Properties

        #region Public Static Methods

        /// <summary>
        /// Converts the string representation of a storage value to an instance of the StorageValue class.
        /// </summary>
        /// <param name="s">The string representing a storage value and optionally specifying B, KB, MB, GB, or TB as a suffix.</param>
        /// <returns>An instance of the StorageValue class.</returns>
        public static StorageValue Parse(string s)
        {
            long conversionFactor = 1;

            if (s.EndsWith("TB", StringComparison.OrdinalIgnoreCase))
            {
                conversionFactor = StorageValue.TeraByte;
                s = s.Substring(0, s.Length - 2);
            }
            else if (s.EndsWith("GB", StringComparison.OrdinalIgnoreCase))
            {
                conversionFactor = StorageValue.GigaByte;
                s = s.Substring(0, s.Length - 2);
            }
            else if (s.EndsWith("MB", StringComparison.OrdinalIgnoreCase))
            {
                conversionFactor = StorageValue.MegaByte;
                s = s.Substring(0, s.Length - 2);
            }
            else if (s.EndsWith("KB", StringComparison.OrdinalIgnoreCase))
            {
                conversionFactor = StorageValue.KiloByte;
                s = s.Substring(0, s.Length - 2);
            }
            else if (s.EndsWith("B", StringComparison.OrdinalIgnoreCase))
            {
                conversionFactor = StorageValue.Byte;
                s = s.Substring(0, s.Length - 1);
            }
            else
            {
                conversionFactor = StorageValue.Byte;
            }

            double value = double.Parse(s.Trim());

            return new StorageValue((long)(value * conversionFactor));
        }

        /// <summary>
        /// Returns a string representation of the storage value.
        /// </summary>
        /// <param name="bytes">The storage value in bytes.</param>
        /// <returns>A formatted string representing the storage value.</returns>
        public static string ToString(long bytes)
        {
            return StorageValue.ToString(bytes, string.Empty, null);
        }

        /// <summary>
        /// Returns a string representation of the storage value.
        /// </summary>
        /// <param name="bytes">The storage value in bytes.</param>
        /// <param name="format">The format string to use.</param>
        /// <returns>A formatted string representing the storage value.</returns>
        public static string ToString(long bytes, string format)
        {
            return StorageValue.ToString(bytes, format, null);
        }

        /// <summary>
        /// Returns a string representation of the storage value.
        /// </summary>
        /// <param name="bytes">The storage value in bytes.</param>
        /// <param name="format">The format string to use.</param>
        /// <param name="formatProvider">The IFormatProvider to use.</param>
        /// <returns>A formatted string representing the storage value.</returns>
        public static string ToString(long bytes, string format, IFormatProvider formatProvider)
        {
            long conversionFactor = 1L;
            string suffix = string.Empty;
            bool includeSuffix = true;
            bool includeSpace = false;
            bool isUnitsSpecified = false;

            // Format: [+/-][ ]units
            // + : include factor (this is the default if not specified), e.g. "10MB"
            // - : omit factor, e.g. "10"
            // : a space before the units indicates that the value and units should be separated by a space, e.g. "10 MB" rather than "10MB"
            // units : TB, GB, MB, KB, B for, respectively, terabytes, gigabytes, megabytes, kilobytes, and bytes.
            //
            // Examples:
            //
            // "GB" --> "10GB"
            // "+GB" --> "10GB"
            // "+ GB" --> "10 GB"
            // "-GB" --> "10"
            //
            // Commas and decimal places will be used when necessary, e.g. "0.1TB", "102,400KB", etc/
            //
            // The factor argument is not case sensitive, e.g. "10mb", "10MB", "10Mb", and "10mB" are all valid.
            if (!string.IsNullOrEmpty(format))
            {
                if (format.StartsWith("+", StringComparison.OrdinalIgnoreCase))
                {
                    includeSuffix = true;
                    format = format.Substring(1);
                }
                else if (format.StartsWith("-", StringComparison.OrdinalIgnoreCase))
                {
                    includeSuffix = false;
                    format = format.Substring(1);
                }

                if (format.StartsWith(" ", StringComparison.OrdinalIgnoreCase))
                {
                    includeSpace = true;
                    format = format.Substring(1);
                }

                if (string.Compare(format, "TB", true) == 0)
                {
                    conversionFactor = StorageValue.TeraByte;
                    suffix = "TB";
                    isUnitsSpecified = true;
                }
                else if (string.Compare(format, "GB", true) == 0)
                {
                    conversionFactor = StorageValue.GigaByte;
                    suffix = "GB";
                    isUnitsSpecified = true;
                }
                else if (string.Compare(format, "MB", true) == 0)
                {
                    conversionFactor = StorageValue.MegaByte;
                    suffix = "MB";
                    isUnitsSpecified = true;
                }
                else if (string.Compare(format, "KB", true) == 0)
                {
                    conversionFactor = StorageValue.KiloByte;
                    suffix = "KB";
                    isUnitsSpecified = true;
                }
                else if (string.Compare(format, "B", true) == 0)
                {
                    conversionFactor = StorageValue.Byte;
                    suffix = "B";
                    isUnitsSpecified = true;
                }
            }

            if (!isUnitsSpecified)
            {
                // No valid explicit units given so attempt to determine the most suitable to use:
                if (bytes > StorageValue.TeraByte)
                {
                    conversionFactor = StorageValue.TeraByte;
                    suffix = "TB";
                }
                else if (bytes > StorageValue.GigaByte)
                {
                    conversionFactor = StorageValue.GigaByte;
                    suffix = "GB";
                }
                else if (bytes > StorageValue.MegaByte)
                {
                    conversionFactor = StorageValue.MegaByte;
                    suffix = "MB";
                }
                else if (bytes > StorageValue.KiloByte)
                {
                    conversionFactor = StorageValue.KiloByte;
                    suffix = "KB";
                }
                else
                {
                    conversionFactor = StorageValue.Byte;
                    suffix = "B";
                }
            }

            // Format the string according to the IFormatProvider the caller specified, otherwise; use the InvariantCulture:
            NumberFormatInfo numberFormatInfo = null;

            if (formatProvider != null)
            {
                NumberFormatInfo formatProviderNumberFormatInfo = formatProvider.GetFormat(typeof(NumberFormatInfo)) as NumberFormatInfo;

                if (formatProviderNumberFormatInfo != null)
                {
                    numberFormatInfo = formatProviderNumberFormatInfo;
                }
            }

            if (numberFormatInfo == null)
            {
                numberFormatInfo = CultureInfo.InvariantCulture.NumberFormat;
            }

            // Use the culture specific thousands and decimal characters:
            string numberFormatString = string.Format("{{0:#{0}##0{1}##}}{{1}}{{2}}", numberFormatInfo.NumberGroupSeparator, numberFormatInfo.NumberDecimalSeparator);

            return string.Format(numberFormatString, (double)(bytes / (conversionFactor * 1.0d)), includeSpace ? " " : string.Empty, includeSuffix ? suffix : string.Empty);
        }

        #endregion Public Static Methods

        #region Public Methods

        /// <summary>
        /// Returns a string representation of the storage value.
        /// </summary>
        /// <returns>A formatted string representing the storage value.</returns>
        public override string ToString()
        {
            return StorageValue.ToString(this.bytes);
        }

        /// <summary>
        /// Returns a string representation of the storage value.
        /// </summary>
        /// <param name="format">The format string to use.</param>
        /// <returns>A formatted string representing the storage value.</returns>
        public string ToString(string format)
        {
            return StorageValue.ToString(this.bytes, format);
        }

        /// <summary>
        /// Returns a string representation of the storage value.
        /// </summary>
        /// <param name="format">The format string to use.</param>
        /// <param name="formatProvider">The IFormatProvider to use.</param>
        /// <returns>A formatted string representing the storage value.</returns>
        public string ToString(string format, IFormatProvider formatProvider)
        {
            return StorageValue.ToString(this.bytes, format, formatProvider);
        }

        #endregion Public Methods
    }
}



Written by Sea Monkey

January 25, 2017 at 5:08 pm

Posted in Development

Tagged with ,

Creating a tray icon application using NotifyIcon

leave a comment »

There are numerous code examples of how to create a tray icon application using the .NET NotifyIcon class. Many claim to show the ‘proper’ way to do this by using a hidden Windows Form as the main component of the application.

It’s easy to see why some might believe this to be true as the Form class implements IDispose methods and an IContainer for controls such as the System.Windows.Forms Timer control (as opposed to other Timer controls such as System.Timers namespace) that might be useful in a tray application.

However, there really isn’t any need to use a Form and, arguably, it adds problems of its own.

All that’s required is that you implement IDisposable and, if you want, an IContainer:

public class MonitorTrayApp : IDisposable
{
  /// <summary>
  /// Container for the form's components.
  /// </summary>
  private IContainer components = null;

  /// <summary>
  /// The NotifyIcon instance.
  /// </summary>
  private NotifyIcon notifyIcon = null;

  /// <summary>
  /// A timer control to drive the polling of the Vitalpulse Monitor service.
  /// </summary>
  private Timer intervalTimer = null;
 
  /// <summary>
  /// Initializes a new instance of the MonitorTrayApp class.
  /// </summary>
  public MonitorTrayApp()
  {
    this.InitializeComponent();
  }
 
  /// <summary>
  /// Initialises the system tray icon.
  /// </summary>
  private void InitializeComponent()
  {
    this.components = new Container();

    this.notifyIcon = new NotifyIcon(this.components);

    this.intervalTimer = new Timer(this.components);
    this.intervalTimer.Tick += new EventHandler(this.IntervalTimerTick);
    this.intervalTimer.Interval = Settings.Default.PollInterval * 1000;
    this.intervalTimer.Enabled = true;

    this.notifyIcon.Icon = Resources.AppIcon;
    this.notifyIcon.Text = Application.ProductName;
    this.notifyIcon.Visible = true;
  }
 
  /// <summary>
  /// Tick event handler for the interval timer control.
  /// </summary>
  /// <param name="sender">The object raising the event.</param>
  /// <param name="e">The event args.</param>
  private void IntervalTimerTick(object sender, EventArgs e)
  {
    // Do some processing here...
  } 
 
  /// <summary>
  /// Performs application IDisposable processing.
  /// </summary>
  public void Dispose()
  {
    this.Dispose(true);
  }

  /// <summary>
  /// Performs application IDisposable processing.
  /// </summary>
  /// <param name="disposing"></param>
  protected virtual void Dispose(bool disposing)
  {
    if (disposing)
    {
      if (this.components != null)
      {
        this.components.Dispose();
      }

      this.notifyIcon.Dispose();
    }
  }
}

The application’s entry point is a Main() method:

public static class Program
{
  [STAThread]
  public static void Main()
  {
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);

    using (MonitorTrayApp notifierTrayIcon = new MonitorTrayApp())
    {
      Application.Run();
    }
  }
}

Written by Sea Monkey

January 18, 2017 at 8:00 pm

Posted in Development

Tagged with ,

Determining the size required to display text: TextRenderer.MeasureText()

leave a comment »

One of the limitations of the .NET NotifyIcon class’s ShowBalloonTip() method is that there is a limit to the length of text that can be displayed. It’s great for a short warning message but not suitable for a long list of errors.

I’ve just written a tray application that connects to a Windows Service and polls for error messages. Once there are more than four or five error messages, using the ShowBalloonTip() method isn’t appropriate to display these messages so I wrote created a form that mimics the BalloonTip window to some extent but allows more text to be displayed.

As I didn’t want to use scrollbars, I needed to determine how much space is required to display the text and then resize the form and its controls appropriately.

After a bit of searching I found that the System.Windows.Forms namespace contains the TextRenderer class that implements the MeasureText() method. This returns a Size instance indicating the size of the rendered text for the specified Graphics context, text, and Font.

It took a bit of testing to realise that you also need to indicate that text lines can be broken if this is required. Otherwise, regardless of whether the specified Graphics context is not wide enough, the MeasureText() will not wrap or break lines. It will however correctly return the required width even though this is wider than the specific Graphics context.

As an example, my form uses a Label control called MessagesLabel to display the text and this is the call to MeasureText() used to determine the height that the control needs to be to display the text without changing the width of the control:

// Determine how much space the message text requires.
Size textSize = TextRenderer.MeasureText(this.MessagesLabel.CreateGraphics(), this.MessagesLabel.Text, this.MessagesLabel.Font, this.MessagesLabel.Size, TextFormatFlags.WordBreak);

// Adjust the height of the form to accommodate the text to be displayed.
int topMargin = this.MessagesLabel.Top;
int bottomMargin = this.ClientRectangle.Size.Height - this.MessagesLabel.Top - this.MessagesLabel.Height;
int requiredHeight = topMargin + bottomMargin + textSize.Height;

 

 

Written by Sea Monkey

January 16, 2017 at 8:00 pm

Posted in Development

Tagged with ,

Detecting when a program is executing from the Visual Studio IDE

leave a comment »

When developing small ad hoc command line utilities in the Visual Studio IDE I often add a Console.ReadKey() method call so that I can see the console output before the program terminates and returns control the IDE. This call is removed when development is complete but I’ve often thought that it would be useful to detect if the program was executing from the IDE and, if so, pause before exiting.

I finally got round to looking at how to do this yesterday. I’d assumed that simply looking at Assembly.GetEntryAssembly() would be enough to determine that the execution was initiated by the IDE but of course Visual Studio is a lot more sophisticated than this. It appears to create an application Domain and launches the program within this domain. However, as the IDE sets itself as the Domain’s DomainManager this can be used to detect whether the program is executing from the IDE:

    // If we're running from the Visual Studio IDE then pause before exiting:
    if (AppDomain.CurrentDomain.DomainManager != null && AppDomain.CurrentDomain.DomainManager.GetType().FullName == "Microsoft.VisualStudio.HostingProcess.VSHostAppDomainManager")
    {
        Console.WriteLine();
        Console.WriteLine("Press any key to exit and return to Visual Studio...");
        Console.ReadKey();
    }

This works for Visual Studio 2010 but I haven’t tested it against other versions yet.

Written by Sea Monkey

October 20, 2016 at 7:00 pm

Posted in Development

Tagged with ,

Dynamic load of 32 bit class library from 32 bit Windows Service fails with BadImageFormatException

leave a comment »

We have an HL7 interface that runs as a Windows Service that is deployed at half a dozen customer sites. Each site has slightly differing interfacing requirements and this is handled using a site specific assembly that is dynamically loaded by the service.

Until recently we haven’t had to worry about whether the service was running as a 32 bit or 64 bit process but some of the more recent deployments have used a site specific plugin assembly that has dependencies on other 32 bit assemblies. So, the target of the automated build was changed from ‘Any CPU’ to ‘x86’. Note that no change was made to the solution or project files.

A few weeks later I deployed a new version of a plugin assembly to a test environment at one of our customer sites and found that the service would not start due to a BadImageFormat Exception being thrown when the service tried to dynamically load the plugin assembly. The intention was that all class library assemblies would be compiled as ‘AnyCPU’ but in fact this plugin assembly had been targeted to ‘x86’ and so was 32 bit only. This should not have been a problem however as the Windows Service executable was also compiled as 32 bit only.

I used DumpBin.exe to confirm that the headers in the two assemblies indicated that these were 32 bit only. [Edit: See this post that explains why this doesn’t work.]

Then I enabled fusion logging to see why the load was failing and at first glance the log didn’t appear to shed any light on why the load failure was happening:

*** Assembly Binder Log Entry  (10/07/2013 @ 11:52:12) ***

The operation failed.
Bind result: hr = 0x8007000b. An attempt was made to load a program with an incorrect format.

Assembly manager loaded from:  C:\Windows\Microsoft.NET\Framework64\v2.0.50727\mscorwks.dll
Running under executable  C:\Program Files (x86)\Vitality\HL7Interface\InterfaceService\Vitality.HL7.Processing.Service.exe
--- A detailed error log follows.

=== Pre-bind state information ===
LOG: User = NT AUTHORITY\SYSTEM
LOG: Where-ref bind. Location = C:\Program Files (x86)\Vitality\HL7Interface\InterfaceService\Vitality.HL7.Processing.RFH.dll
LOG: Appbase = file:///C:/Program Files (x86)/Vitality/HL7Interface/InterfaceService/
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = NULL Calling assembly : (Unknown).
===
LOG: This bind starts in LoadFrom load context.
WRN: Native image will not be probed in LoadFrom context. Native image will only be probed in default load context, like with Assembly.Load().
LOG: Using application configuration file: C:\Program Files (x86)\Vitality\HL7Interface\InterfaceService\Vitality.HL7.Processing.Service.exe.Config
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\v2.0.50727\config\machine.config.
LOG: Attempting download of new URL file:///C:/Program Files (x86)/Vitality/HL7Interface/InterfaceService/Vitality.HL7.Processing.RFH.dll.
LOG: Assembly download was successful. Attempting setup of file: C:\Program Files (x86)\Vitality\HL7Interface\InterfaceService\Vitality.HL7.Processing.RFH.dll
LOG: Entering run-from-source setup phase.
LOG: Assembly Name is: Vitality.HL7.Processing.RFH, Version=2.0.0.0, Culture=neutral, PublicKeyToken=f529d7a12981f1ee
ERR: Failed to complete setup of assembly (hr = 0x8007000b). Probing terminated.

It took me a few minutes to spot that line 6 indicates that the 64 bit loader is being used:

Assembly manager loaded from: C:\Windows\Microsoft.NET\Framework64\v2.0.50727\mscorwks.dll

Similarly, line 23 indicates that the machine configuration file being used is from the Framework64 folder.

This doesn’t make any sense as the service executable is running as a 32 bit process.

It seems that setting the PlatformTarget property to “x86” when invoking MSBuild in the automated build to build the project files is not having the effect I thought it did.

The project files in Visual Studio are set to PlatformTarget = “AnyCPU” and have references to 64 bit Framework assemblies. When the project files are compiled in the automated build with PlatformTarget=”x86″ the 32 bit flag is set on the assemblies created but the 64 bit framework assembly references remain unchanged.

The solution was to configure the platform targets for each project in Visual Studio as required (“x86” for executables and “AnyCPU” for class libraries) and then update the automated build to build the solution files rather than the project files.

Written by Sea Monkey

July 12, 2013 at 8:00 pm

Posted in Debugging, Development

Tagged with , ,

MySQL Connector/ODBC 3.5.1.30 and CommandBehavior.KeyInfo

leave a comment »

Some time ago I wrote a query tool that connects to databases using ODBC. The tool is intended to be database agnostic and is used on a daily basis against both MySQL and Microsoft SQL Server databases.

However, it’s recently exhibited some odd behaviour when running queries against MySQL – a single row is returned even when the resultset is larger than one row.

Adding a LIMIT clause to the query caused all the rows or at least the number of rows specified to be returned. Further investigation showed that the problem only occurred when version 3.51.1.30 of MySQL Connector/ODBC was installed. Earlier versions don’t show this behaviour and instead all rows in the resultset are returned.

It looked like a bug in 3.51.1.30 so I logged it with the MySQL development team. However, it soon became apparent that the problem is a change in 3.51.1.30 that is intended to correctly handle the CommandBehaviour.KeyInfo option that I was using in the call to ExecuteReader():

OdbcCommand command = new OdbcCommand(sql, this.connection);
command.CommandType = CommandType.Text;
command.CommandTimeout = Settings.Default.CommandTimeout;
OdbcDataReader reader = command.ExecuteReader(CommandBehavior.KeyInfo);

The intent was to retrieve extended information about the resultset. During development of the application, specifying this option appeared to have provided the additional information when executing queries against Microsoft SQL Server. According to the MySQL technician who dealt with the bug report, this option was implemented to retrieve metadata from Microsoft SQL Server but, as of version 3.51.1.30, MySQL Connector/ODBC is now taking account of it too. Unfortunately, the MySQL developers have decided to handle it in a different way to the Microsoft SQL Server development team. In short, the option adds metadata to the resultset without changing the number or rows returned when executing against a Microsoft SQL Server database but Connector/ODBC will always return only one row when this option is set regardless of how many rows the query would otherwise return.

It’s difficult not to consider this a bug although the MySQL development team don’t agree. If the MySQL development team acknowledge that the option was intended for Microsoft SQL Server but they have now decided to handle it as well, what is the logic behind handling it in a different manner to Microsoft SQL Server ?

Written by Sea Monkey

May 10, 2013 at 8:00 pm

Posted in Debugging, Development

Tagged with , , ,