Carl’s Geek Notes

December 8, 2008

Run a SQL UPDATE command and return the number of affected records

Filed under: C#/.NET, Computers, Programming, SQL Server — Tags: , , — Carl @ 9:29 am

I wanted a quick way to run a SQL UPDATE command and return the number of affected rows in .NET.  Here’s the code to do it:

int RecordCount = 0;
string SQL = "UPDATE MyTable SET MyField = @MyNewValue "
    + "WHERE MyKey = @MyKey "
    + "SET @RecordCount = @@ROWCOUNT";
SqlCommand MyCmd = new SqlCommand(SQL, MyConn);

// Use an out parameter to get the number of rows affected
SqlParameter RecordCountParam = new SqlParameter("@RecordCount", SqlDbType.Int);
RecordCountParam.Direction = ParameterDirection.Output;
MyCmd.Parameters.Add(RecordCountParam);

// Add the query parameters
MyCmd.Parameters.AddWithValue("@MyNewValue", MyNewValue);
MyCmd.Parameters.AddWithValue("@MyKey", MyKey);

// Run the query
MyCmd.ExecuteNonQuery();

// Check the number of records affected
if (Int32.TryParse(RecordCountParam.Value.ToString(), out RecordCount)
{
	// Do Stuff Here
}

August 27, 2008

XmlNamespaceManager and overridden default XML namespace

Filed under: C#/.NET, Computers, Programming, Web Services, XML — Carl @ 12:22 pm

Issue: My XmlDocument.SelectNodes() or SelectSingleNode() wasn’t working correctly with the incoming XML. It was always returning null even when the element was specified.

Problem: If the XML has an overridden default XML namespace (i.e., the root node has an attribute xmlns=”urn:MyNamespace” in it), SelectNode is unable to properly resolve it.

Solution: Use an XmlNamespaceManager to give a prefix to the default namespace, like so:

string MyXML = "<?xml version=\"1.0\"?><MyElement xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns=\"urn:MyNamespace\"><MySubElement>3</MySubElement></MyElement>";

// Create and load the XML document
XmlDocument MyDoc = new XmlDocument();
MyDoc.LoadXml(Response);

// Create a namespace manager with the XML document's name table
XmlNamespaceManager MyNamespaces = new XmlNamespaceManager(MyDoc.NameTable);

// Add namespaces for all prefixed xmlns declarations
MyNamespaces.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
MyNamespaces.AddNamespace("xsd", "http://www.w3.org/2001/XMLSchema");

// Add the default namespace with a custom prefix
MyNamespaces.AddNamespace("MyNS", "urn:MyNamespace");

// Use the namespace manager to select the node
// Make sure that each element in the default namespace is prefixed with your custom prefix
// It is not sufficient to do MyNS:MyElement/MySubElement, both must be qualified!
XmlNode MyNode = MyDoc.SelectSingleNode("MyNS:MyElement/MyNS:MySubElement", MyNamespaces);

June 5, 2008

Using an object as an array accessor through Reflection

Filed under: C#/.NET, Computers, Programming — Carl @ 1:40 pm

Fun bit of code here using System.Reflection!

I have a class that contains an array of objects.  I wanted a convenient way to be able to access elements by the value of a property within the object, for example, if I have an array of type Employee and I want to find the one in the array where the LastName Property is Smith, it would be nice to be able to call MyArray["EmployeeID", "12345"] to get the item.  Basically, adding some Dictionary-type lookup options to an indexed array is what I wanted.

My solution is:

public class ccnGenericCollection<T>
{
    #region Fields
    private T[] _Items;
    #endregion

    #region Properties
    public T[] Items
    {
        get { return _Items; }
        set { _Items = value; }
    }

    public T this[int index]
    {
        get { return _Items[index]; }
        set { _Items[index] = value; }
    }

    public T this[string fieldName, object searchValue]
    {
        get
        {
            // Make sure the items and key field name are initialized
            if (null != _Items)
            {
                // Get the member 
                MemberInfo[] MyMembers = typeof(T).GetMember(fieldName);
                if ((null != MyMembers) && (MyMembers.GetLength(0) > 0))
                {
                    MemberInfo MyMember = MyMembers[0];
                    // Loop through the items
                    for (int i = 0; i < _Items.GetLength(0); i++)
                    {
                        object MyKeyValue;
                        // Get the field or property value
                        switch (MyMember.MemberType)
                        {
                            case MemberTypes.Field:
                                MyKeyValue = ((FieldInfo)MyMember).GetValue(this[i]);
                                break;
                            case MemberTypes.Property:
                                MyKeyValue = ((PropertyInfo)MyMember).GetValue(this[i], null);
                                break;
                            default:
                                throw new Exception(fieldName + " must be a field or a property");
                        }
                        if (MyKeyValue.Equals(searchValue))
                        {
                             // Found our item
                             return _Items[i];
                        }
                    }
                }
            }
            // Not found, return a default item
            return default(T);
        }
        set
        {
            if (null != _Items)
            {
                // Get the member
                MemberInfo[] MyMembers = typeof(T).GetMember(fieldName);
                if ((null != MyMembers) && (MyMembers.GetLength(0) > 0))
                {
                    MemberInfo MyMember = MyMembers[0];
                    // Loop through the items
                    for (int i = 0; i < _Items.GetLength(0); i++)
                    {
                        switch (MyMember.MemberType)
                        {
                            case MemberTypes.Field:
                                if (((FieldInfo)MyMember).GetValue(this[i]).ToString() == searchValue)
                                {
                                    // Set the item
                                    _Items[i] = value;
                                }
                                break;
                            case MemberTypes.Property:
                                if (((PropertyInfo)MyMember).GetValue(this[i], null).ToString() == searchValue)
                                {
                                    // Set the item
                                    _Items[i] = value;
                                }
                                break;
                        }
                    }
                }
            }
            // No item found, throw a null reference exception
            throw new NullReferenceException();
        }
    }
    #endregion

    #region Operators
    public static implicit operator T[](ccnGenericCollection<T> input)
    {
        // Automatically convert from an array of the underlying class to the generic collection
        T[] Output = (T[])input.Items.Clone();
        return Output;
    }

    public static implicit operator ccnGenericCollection<T>(T[] input)
    {
        // Automatically convert from the generic collection to an array of the underlying class
        ccnGenericCollection<T> Output = new ccnGenericCollection<T>();
        Output.Items = (T[])input.Clone();
        return Output;
    }

    public static implicit operator List<T>(ccnGenericCollection<T> input)
    {
        // Automatically convert from an array of the underlying class to the generic collection
        List<T> Output = new List<T>();
        if (null != input.Items)
        {
            Output.AddRange(input.Items);
        }
        return Output;
    }

    public static implicit operator ccnGenericCollection<T>(List<T> input)
    {
        // Automatically convert from the generic collection to an array of the underlying class
        ccnGenericCollection<T> Output = new ccnGenericCollection<T>();
        Output.Items = (T[])input.ToArray().Clone();
        return Output;
    }
    #endregion

    #region Methods
    public int Add(T newItem)
    {
        // Get the list of the current items
        List<T> MyList = (List<T>)this;
        // Add the new item
        MyList.Add(newItem);
        // Set the items to the array of the list
        _Items = MyList.ToArray();
        // Return the highest index of the items
        return _Items.GetUpperBound(0);
    }
    #endregion
}

This solution allows the field name for the search to be either a field or a property.

Note that it’s prudent to make sure that the field used to access the class is unique; it’ll return the first match for get and update all matches for set.

I got a surprise the first time I ran it when I never got any matches back by using MyKeyField == searchValue as the condition; I had to use MyKeyField.Equals(searchValue) to use the object equality. This also means that it’s easy to define what equality is for a given class by implementing the == operator for the class.

The implicit operators come in handy as well; basically any class that implements this class can be converted to/from arrays and lists.

January 10, 2008

Saving a (possibly binary) file from a URL in C#

Filed under: C#/.NET, Computers, Programming — Carl @ 1:40 pm

Need a quick way to save a file (.PDF, .JPG, .ZIP, etc.) to disk from a URL?  Try this little bit of code:

public static bool SaveFileFromURL(string url, string destinationFileName, int timeoutInSeconds)
{
    // Create a web request to the URL
    HttpWebRequest MyRequest = (HttpWebRequest)WebRequest.Create(url);
    MyRequest.Timeout = timeoutInSeconds * 1000;
    try
    {
        // Get the web response
        HttpWebResponse MyResponse = (HttpWebResponse)MyRequest.GetResponse();

        // Make sure the response is valid
        if (HttpStatusCode.OK == MyResponse.StatusCode)
        {
            // Open the response stream
            using (Stream MyResponseStream = MyResponse.GetResponseStream())
            {
                // Open the destination file
                using (FileStream MyFileStream = new FileStream(destinationFileName, FileMode.OpenOrCreate, FileAccess.Write))
                {
                    // Create a 4K buffer to chunk the file
                    byte[] MyBuffer = new byte[4096];
                    int BytesRead;
                    // Read the chunk of the web response into the buffer
                    while (0 < (BytesRead = MyResponseStream.Read(MyBuffer, 0, MyBuffer.Length)))
                    {
                        // Write the chunk from the buffer to the file
                        MyFileStream.Write(MyBuffer, 0, BytesRead);
                    }
                }
            }
        }
    }
    catch (Exception err)
    {
        throw new Exception("Error saving file from URL:" + err.Message, err);
    }
    return true;
}

December 14, 2007

Caution when using operators for overloading and conversion

Filed under: C#/.NET, Computers, Programming — Carl @ 11:14 am

I have a class that defines several math operations and conversion between other types that was giving unexpected results. After quite a while of debugging, I found that the problem was with the way the compiler casts operands when using operators. My original class was similar to this:

public class MyNumericClassDefinedOperator
{
    protected decimal _MyDecimalValue;

    public decimal MyDecimalValue
    {
        get { return _MyDecimalValue; }
        set { _MyDecimalValue = value; }
    }

    public MyNumericClassDefinedOperator(object x)
    {
        try
        {
            _MyDecimalValue = Decimal.Parse(x.ToString());
        }
        catch { }
    }

    #region Operators with MyNumericClassDefinedOperator
    public static MyNumericClassDefinedOperator operator +(MyNumericClassDefinedOperator x, MyNumericClassDefinedOperator y)
    {
        return new MyNumericClassDefinedOperator(x._MyDecimalValue + y._MyDecimalValue);
    }
    #endregion

    #region Conversion Operators
    public static implicit operator decimal(MyNumericClassDefinedOperator x)
    {
        return x.MyDecimalValue;
    }
    public static implicit operator int(MyNumericClassDefinedOperator x)
    {
        return (int)x.MyDecimalValue;
    }
    public static implicit operator MyNumericClassDefinedOperator(decimal x)
    {
        return new MyNumericClassDefinedOperator(x);
    }
    public static implicit operator MyNumericClassDefinedOperator(int x)
    {
        return new MyNumericClassDefinedOperator(x);
    }
    #endregion

    #region Operators with int
    public static decimal operator +(MyNumericClassDefinedOperator x, int y)
    {
        return (x.MyDecimalValue + y);
    }
    public static decimal operator *(MyNumericClassDefinedOperator x, int y)
    {
        return (x.MyDecimalValue * y);
    }
    #endregion

    #region Operators with decimal
    public static decimal operator +(MyNumericClassDefinedOperator x, decimal y)
    {
        return (x.MyDecimalValue + y);
    }
    public static decimal operator *(MyNumericClassDefinedOperator x, decimal y)
    {
        return (x.MyDecimalValue * y);
    }
    #endregion
}

This simple test gives the incorrect result 54.0:

MyNumericClassDefinedOperator Value1 = 17.656m;
MyNumericClassDefinedOperator Value2 = 1.67845m;
MyNumericClassDefinedOperator Value3 = 4.23905m;
MyNumericClassDefinedOperator Value4 = 9.0564m;
MyNumericClassDefinedOperator Value5 = 3.1345m;
MyNumericClassDefinedOperator Value6 = 0.671m;

MyNumericClassDefinedOperator Result1 = 1.5m * (Value1 + Value2 + Value3 + Value4 + Value5 + Value6);
Console.WriteLine(Result1.MyDecimalValue.ToString());

When I removed the + and * operators with decimal and int (requiring me to add the operator MyNumericClass *(MyNumericClass, MyNumericClass)), the class looks like this:

public class MyNumericClass
{
    protected decimal _MyDecimalValue;

    public decimal MyDecimalValue
    {
        get { return _MyDecimalValue; }
        set { _MyDecimalValue = value; }
    }

    public MyNumericClass(object x)
    {
        try
        {
            _MyDecimalValue = Decimal.Parse(x.ToString());
        }
        catch { }
    }

    #region Operators with MyNumericClass
    public static MyNumericClass operator +(MyNumericClass x, MyNumericClass y)
    {
        return new MyNumericClass(x._MyDecimalValue + y._MyDecimalValue);
    }
    public static MyNumericClass operator *(MyNumericClass x, MyNumericClass y)
    {
        return new MyNumericClass(x._MyDecimalValue * y._MyDecimalValue);
    }
    #endregion

    #region Conversion Operators
    public static implicit operator decimal(MyNumericClass x)
    {
        return x.MyDecimalValue;
    }
    public static implicit operator int(MyNumericClass x)
    {
        return (int)x.MyDecimalValue;
    }
    public static implicit operator MyNumericClass(decimal x)
    {
        return new MyNumericClass(x);
    }
    public static implicit operator MyNumericClass(int x)
    {
        return new MyNumericClass(x);
    }
    #endregion
}

and it gives the correct result, 54.653100:

MyNumericClass Value11 = 17.656m;
MyNumericClass Value12 = 1.67845m;
MyNumericClass Value13 = 4.23905m;
MyNumericClass Value14 = 9.0564m;
MyNumericClass Value15 = 3.1345m;
MyNumericClass Value16 = 0.671m;

MyNumericClass Result2 = 1.5m * (Value11 + Value12 + Value13 + Value14 + Value15 + Value16);
Console.WriteLine(Result2.MyDecimalValue.ToString());

The rule that is implied by this is: Use your own operators to take C# classes into your type, where you control the outcome, rather than letting the compiler choose.

(Side note: I was also able to get correct results simply by adding this operator to the class:

public static MyNumericClassDefinedOperator operator *(MyNumericClassDefinedOperator x, MyNumericClassDefinedOperator y)
{
	return new MyNumericClassDefinedOperator(x._MyDecimalValue * y._MyDecimalValue);
}

without removing the + and * operators with decimal and int, and it indeed gave the right result. I don’t plan on using this method, as removing the decimal and int operators removes possible other options from the compiler. Plus, it is probably clearer to anyone else reviewing the code and it’s easier to code because there’s only one place where the operations are done.)

September 21, 2007

C# string rears its ugly C++ head…

Filed under: C#/.NET, Computers, Microsoft Access, Programming — Carl @ 8:25 am

Here’s one that I won’t forget soon.  I was working on an application that reads data from a Microsoft Access database and builds a string:

string MyString = MyTable.Rows[i]["MyColumn"].ToString();
string NewString = "stuff " + MyString + " some other stuff";

Pretty basic stuff.  I was seeing a problem where NewString’s value was (assuming MyString = “MyStringData”) ”stuff MyStringData”–it cut off everything in the string after MyString.

The issue?  When MyString is assigned, it includes \0 at the end (the string terminator character from C).  .NET apparently stops marshalling at that point.  I corrected it by:

string MyString = MyTable.Rows[i]["MyColumn"].ToString().Replace("", "");

I don’t know whether Access returned it terminated, or the application that originally wrote the data added the string terminator (which I suspect, as I’ve written tomes of code that pull data from Access in my life).  I finally found it by breaking into the application, and when I hovered the cursor over MyString its data was displayed as “MyStringData\0″.  (FYI, it appeared as just “MyStringData” in the string visualizer and Watch window, it was only the hover-over that gave it away.)

September 19, 2007

Using BackgroundWorker to update status

Filed under: C#/.NET, Computers, Programming — Carl @ 8:27 am

I’ve been working on an application the past few days that iterates through directories and processes files within those directories.  I was looking for a good way to keep the user informed of progress, and I decided that using System.ComponentModel.BackgroundWorker was my best solution.

Here’s some relevant code for how I used UserState to keep the user updated of the progress:

namespace BackgroundWorkerSample
{
    public partial class Main : Form
    {
        #region Form Callbacks
        private void btnCancel_Click(object sender, EventArgs e)
        {
            // Let the user know the cancel is processing
            btnCancel.Text = "Cancelling...";
            btnCancel.Enabled = false;

            // Ask the background worker to cancel its process
            if ((null != _FileProcessingWorker) &amp;&amp; (_FileProcessingWorker.IsBusy))
            {
                _FileProcessingWorker.CancelAsync();
            }
        }

        private void btnProcess_Click(object sender, EventArgs e)
        {
            // Set up the background worker
            _FileProcessingWorker = new BackgroundWorker();
            _FileProcessingWorker.WorkerReportsProgress = true;
            _FileProcessingWorker.WorkerSupportsCancellation = true;

            // Add the events needed for the background worker
            _FileProcessingWorker.DoWork += new DoWorkEventHandler(_FileProcessingWorker_DoWork);
            _FileProcessingWorker.ProgressChanged += new ProgressChangedEventHandler(_FileProcessingWorker_ProgressChanged);
            _FileProcessingWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_FileProcessingWorker_RunWorkerCompleted);
            
            // Get the directory to process
            string BasePath = lblDirectoryName.Text;
            DirectoryInfo MyDirectory = new DirectoryInfo(BasePath);
            DirectoryInfo[] MySubDirectories = MyDirectory.GetDirectories();

            // Collect statistics for the progress bar
            int SubDirectoryCount = MySubDirectories.GetLength(0);
            MyProgressBar.Minimum = 0;
            MyProgressBar.Step = 1;
            MyProgressBar.Maximum = SubDirectoryCount;
            MyProgressBar.Value = MyProgressBar.Minimum;
            _LastCounter = 0;

            // Instantiate the background worker and pass the file list
            _FileProcessingWorker.RunWorkerAsync(MySubDirectories);
        }
        #endregion

        #region File processing methods and events
        private void _FileProcessingWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            // Cast the EventArgs to the expected type
            DirectoryInfo[] MySubDirectories = (DirectoryInfo[])e.Argument;

            // Loop through the subdirectories
            for (int i = 0; i < MySubDirectories.GetLength(0); i++)
            {
                DirectoryInfo MySubDirectory = MySubDirectories[i];
                // Keep track of the current directory
                string CurrentDirectory = "Current Directory: " + MySubDirectory.Name;

                // Loop through the matching files
                foreach (FileInfo MyFile in MySubDirectory.GetFiles("*.txt"))
                {
                    // See if the user has requested cancellation
                    if (_FileProcessingWorker.CancellationPending)
                    {
                        e.Cancel = true;
                        return;
                    }
                    // Keep track of the current file
                    string CurrentFile = "Current File: " + MyFile.Name;
                    // Create an object to report the status via UserState
                    string[] CurrentStatus = new string[2];
                    CurrentStatus[0] = CurrentDirectory;
                    CurrentStatus[1] = CurrentFile;
                    // Raise the ProgressChanged event to allow reporting of status
                    _FileProcessingWorker.ReportProgress(i, CurrentStatus);

                    // Do the needed processing on the file
                    Thread.Sleep(500);
                }
            }
        }

        private void _FileProcessingWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            // Update the progress bar and status labels when the background worker reports progress
            // Cast the EventArgs to the expected type
            if (typeof(string[]) == e.UserState.GetType())
            {
                string[] StatusMsg = (string[])e.UserState;
                if (2 == StatusMsg.GetLength(0))
                {
                    // Update the status labels
                    lblCurrentDirectory.Text = StatusMsg[0];
                    lblStatus.Text = StatusMsg[1];
                }
            }

            // See if the percentage completion needs to be updated
            if (e.ProgressPercentage > _LastCounter)
            {
                _LastCounter = e.ProgressPercentage;
                // Increment the progress bar
                MyProgressBar.PerformStep();
            }
        }

        private void _FileProcessingWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            // Keep the display of the last directory and file processed
            lblCurrentDirectory.Text = lblCurrentDirectory.Text.Replace("Current", "Last");
            lblStatus.Text = lblStatus.Text.Replace("Current", "Last");
        }
        #endregion
    }
}

You can download a complete project here.

September 11, 2007

Error CS0012: Unable to generate a temporary class

Filed under: C#/.NET, Computers, Programming, Web Services, XML — Carl @ 8:56 am

I received an error when serializing a class to XML:

Unable to generate a temporary class (result=1).
error CS0012: The type 'MyType' is defined in an assembly that is not referenced. You must add a reference to assembly 'MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=123412341234'.

Searching the web seemed to indicate that marking fields or properties of the class as non-serialized or adding a reference to the assembly containing the type would correct the problem. The odd thing is, MyType wasn’t part of the class being serialized; there were no fields or properties of MyType within the class!

Fortunately, I knew that it was working before I made some changes this morning. I had added the following:

 
public static implicit operator MyType(MySerializedClass parameterValue) {...}

to the class. When I changed it to an explicit operator, the error went away.  I'm not sure why this behavior is so, but it solved my problem.

September 4, 2007

Update file location through iTunes COM SDK

Filed under: C#/.NET, Computers, Gadgets, iPod, Programming — Carl @ 2:12 pm

I’m working on a little app to automate some management tasks in iTunes (primarily the ability to locate a “dead” file in iTunes and look for its new location).  I downloaded the Apple iTunes COM SDK documentation, and have built a little C# application to iterate through the library to find dead files.

What I’d like to be able to do is search the music directory for possible matches (artist, album, file size, etc.) and update the iTunes library with the new location.  The code to loop through the library is pretty straightforward:

public static Hashtable GetInvalidFileList(iTunesAppClass iTunesAppObject)
{
bool CloseWhenDone = (null == iTunesAppObject);
if (CloseWhenDone)
{
iTunesAppObject = new iTunesAppClass();
}

Hashtable InvalidFileList = new Hashtable();

// Get the full library
IITLibraryPlaylist MyLibrary = iTunesAppObject.LibraryPlaylist;

// Get the track collection
IITTrackCollection MyTrackCollection = MyLibrary.Tracks;

for (int i = 0; i < MyTrackCollection.Count; i++)
{
// Get the track
IITTrack MyTrack = MyTrackCollection[i + 1];

// See if it's a file track
if (ITTrackKind.ITTrackKindFile == MyTrack.Kind)
{
// Cast it as a file track
IITFileOrCDTrack MyFileTrack = (IITFileOrCDTrack)MyTrack;

// Inspect the location
if (String.IsNullOrEmpty(MyFileTrack.Location))
{
int TrackDatabaseID = MyFileTrack.TrackDatabaseID;
string TrackName = "Artist: " + MyFileTrack.Artist + " Album: " + MyFileTrack.Album + " Song: " + MyFileTrack.Name;
InvalidFileList.Add(TrackDatabaseID, TrackName);
}
}
}

if (CloseWhenDone)
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(iTunesAppObject);
iTunesAppObject = null;
GC.Collect();
}

return InvalidFileList;
}

However, updating the location appears to be problematic, as IITFileOrCDTrack.Location is read-only.  It seems that the only way to do this through the COM interface is to add a new track, copy the meta from the old track, add the new track to all playlists that the old track was a member of, and delete the old track.  That will set DateAdded to the time the application was run, and it’s a little kludgey as well.  Is there a better way to do this?

August 30, 2007

Updating computer name in TFS workspaces

Filed under: C#/.NET, Computers, Programming — Carl @ 6:46 am

When I changed my computer name recently, I found that all my Team Foundation Server mappings were still looking towards my old computer name.  Fortunately, searching the TFS documentation revealed that there is a quick way to update your computer name without having to reset all your workspaces.  Run the tf.exe tool, found in \Program Files\Microsoft Visual Studio 8\Common7\IDE directory:

tf workspaces /updateComputerName:MyOldComputerName /s:http://MyTFServer:8080

Older Posts »

Theme: Silver is the New Black. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.