Archive

Archive for October, 2009

Working with AS400-iSeries-System i Stored Procedures in .NET

October 26, 2009 6 comments

At my company our primary database is the IBM System i, formerly known as iSeries, formerly known as AS/400, and still affectionately referred to as simply “the 400” by most people I know.  The 400 is a fantastic database server: fast, reliable, secure, and best of all the darn thing just never breaks.

I spend a great deal of my time writing .NET apps that use the 400 as the backend database.  In the past this has meant a lot of dynamic SQL, which I am extremely comfortable developing.  Unfortunately, this can be pretty brittle, difficult to debug, and performance frequently lags.  I’ve done lots of things to improve performance, like using Parameterized Queries. 

In an effort to wring out the last bit of performance and improve the quality of our code, we have been making an effort to expose our data via Stored Procedures. How to create those procedures on the 400 is a topic I’m not going to delve into, mostly because I haven’t been doing it myself, but also because there are lots of options.  In our case we are writing them in SQL, but they could just as easily execute an RPG program.

Consuming a Stored Procedure

I use the IBM DB2 iSeries .NET Data Provider, V5R4.  I also have SP1 installed (SI35287) which corrects a nasty ObjectDisposedException problem that’s been floating around since .NET 2.0 came out.  The classes are all in the IBM.Data.DB2.iSeries namespace.  Be sure you add a reference to <installed_location>\IBM\Client Access\IBM.Data.DB2.iSeries.dll.

The process is actually pretty simple.  Create a Connection, create a Command, add some Parameters, Execute the Command, Process the results.

var connectionstring = String.Format(ConnectionStringFormat,
    IpAddress, User, Password);
_conn = new iDB2Connection(connectionstring);
_conn.Open();

if (_conn != null && _conn.State == ConnectionState.Open;)
{
    _cmd = _conn.CreateCommand();
    _cmd.CommandTimeout = 0;
    _cmd.CommandType = CommandType.StoredProcedure;
    _cmd.CommandText = "LIBNAME.PROCNAME";

    // Add Parameters
    _cmd.Parameters.Add(name, type, size);
    _cmd.Parameters[name].Value = value;
    _cmd.Parameters[name].Direction = direction;

    _cmd.Prepare();

    // For individual value returns (direction intput/output or output
    _cmd.ExecuteNonQuery();
    var dict = new Dictionary();
    foreach (iDB2Parameter parm in _cmd.Parameters)
    {
        if (parm.Direction == ParameterDirection.Input)
            continue;

        dict.Add(parm.ParameterName, parm.Value);
    }

    // For collection value returns
    var adapter = new iDB2DataAdapter(_cmd);
    var ds = new DataSet();
    adapter.Fill(ds);

    _cmd.Dispose();
    _conn.Close();
    _conn.Dispose();
}

Testing Stored Procedures

I quickly found when we started down this path that I was missing a way to easily test Stored Procedures, and since we were planning on writing lots of them this just would not do.

Executing ad-hoc SQL against the 400 can be a draining experience.  There is a command line tool called Interactive SQL (STRSQL in 400 speak) and a Java tool embedded deep in Operations Navigator which I wrote an article about this back in my ITJungle days. Disappointed in the available tools, my first serious .NET application, written in .NET 1.1 back in 2003, was an ad-hoc SQL tool for the iSeries called iHoc Query.  We still use it in house today but I quit distributing it a while back for various reasons.  Mostly it needs an overhaul and I just don’t have the time to spare.

Unfortunately, I never could get ad-hoc execution of a Stored Procedure to work against the 400, which meant I needed to find a tool. There is a utility out there called SQLThing, which is where we started.  It seemed to work fine, with a few glitches here and there, but I needed to get a good handle on how to code for this data access approach.  It also did not have some of the features I wanted, so naturally I hacked together something myself.

It doesn’t look pretty (I wrote it in WinForms), but it works very well and has these goodies:

  • Retains Data Connection information
  • Supports Input, Output, and Input/Output parameter directions
  • Supports returning multiple result sets, displaying each in a tab
  • Allows saving and loading of Stored Procedures, so you can reuse them without reentering the parameters each time

Here is a screen shot:

TestStoredProcScreenShot

You can download this tool here.

Some helpful code

I wrote a wrapper class called SprocEngine to isolate the ugly Managed Provider code and make it more palatable to use them in my programs. Here is the code for the SprocEngine class:

using System;
using System.Collections.Generic;
using System.Data;
using IBM.Data.DB2.iSeries;

namespace SprocEngine
{
    public class As400SprocEngine : PropertyChangedBase, IDisposable
    {
        public enum As400DbType
        {
            Numeric,
            Character
        }

        public As400SprocEngine()
        {
        }

        public As400SprocEngine(string ip, string user, string password)
            : this()
        {
            IpAddress = ip;
            User = user;
            Password = password;

            ConnectToDatabase();
        }

        private Dictionary _typeConverter =
            new Dictionary()
                {
                    {As400DbType.Numeric, iDB2DbType.iDB2Numeric},
                    {As400DbType.Character, iDB2DbType.iDB2Char}
                };

        private iDB2Connection _conn = null;
        private iDB2Command _cmd = null;

        private string _ip;
        private string _user;
        private string _pass;

        private const string ConnectionStringFormat = "DataSource={0};UserID={1};Password={2};";

        public string QualifiedProcedureName
        {
            get
            {
                return _cmd.CommandText;
            }
            set
            {
                _cmd.CommandText = value;
                FirePropertyChangedEvent("QualifiedProcedureName");
            }
        }

        public bool IsConnected
        {
            get { return _conn != null && _conn.State == ConnectionState.Open; }
        }

        public string IpAddress
        {
            get { return _ip; }
            set { _ip = value; FirePropertyChangedEvent("IpAddress"); }
        }

        public string User
        {
            get { return _user; }
            set { _user = value; FirePropertyChangedEvent("User"); }
        }

        public string Password
        {
            get { return _pass; }
            set { _pass = value; FirePropertyChangedEvent("Password"); }
        }

        public List ParmTypes
        {
            get
            {
                var list = new List();
                list.AddRange(Enum.GetNames(typeof(iDB2DbType)));
                return list;
            }
        }
        public void ConnectToDatabase()
        {
            if (IpAddress.Equals("") || User.Equals("") || Password.Equals(""))
                throw new ArgumentException("Required field is blank.");

            var connectionstring = String.Format(ConnectionStringFormat,
                IpAddress, User, Password);
            _conn = new iDB2Connection(connectionstring);
            _conn.Open();

            FirePropertyChangedEvent("IsConnected");

            if (IsConnected)
            {
                _cmd = _conn.CreateCommand();
                _cmd.CommandTimeout = 0;
                _cmd.CommandType = CommandType.StoredProcedure;
            }
        }

        public void ClearParameters()
        {
            _cmd.Parameters.Clear();
        }


        public void AddParameter(string name, As400DbType type, object value)
        {
            AddParameterIDB2(name, _typeConverter[type], null, value, ParameterDirection.Input);
        }

        public void AddParameterIDB2(string name, iDB2DbType type, object value)
        {
            AddParameterIDB2(name, type, null, value, ParameterDirection.Input);
        }

        public void AddParameter(string name, As400DbType type, int? size, object value, ParameterDirection direction)
        {
            AddParameterIDB2(name, _typeConverter[type], size, value, direction);
        }

        public void AddParameterIDB2(string name, iDB2DbType type, int? size, object value, ParameterDirection direction)
        {
            if (size.HasValue)
            {
                _cmd.Parameters.Add(name, type, size.Value);
            }
            else
            {
                _cmd.Parameters.Add(name, type);
            }
            _cmd.Parameters[name].Value = value;
            _cmd.Parameters[name].Direction = direction;
        }

        public Dictionary ExecuteSproc()
        {
            try
            {
                _cmd.Prepare();
                var q = _cmd.ExecuteNonQuery();

                var dict = new Dictionary();
                foreach (iDB2Parameter parm in _cmd.Parameters)
                {
                    if (parm.Direction == ParameterDirection.Input)
                        continue;

                    dict.Add(parm.ParameterName, parm.Value);
                }

                if (dict.Count <= 0)
                    dict.Add("AffectedRowCount", q);

                return dict;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public DataSet ExecuteSprocCollection()
        {
            try
            {
                _cmd.Prepare();

                var adapter = new iDB2DataAdapter(_cmd);
                var ds = new DataSet();
                adapter.Fill(ds);

                return ds;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        #region IDisposable Members
        public void Dispose()
        {
            _cmd.Dispose();
            _conn.Close();
            _conn.Dispose();
        }
        #endregion
    }
}

It inherits a simple class that implements INotifyPropertyChanged called PropertyChangedBase.  If you aren’t planning on binding this to WPF you can remove it, but if you want it here is the code for it:

using System.ComponentModel;
namespace SprocEngine
{
    public class PropertyChangedBase : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        internal void FirePropertyChangedEvent(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Using SprocEngine

Here are a couple of samples showing how to use SprocEngine.

Processing a single returned value:

using (var sproc = new As400SprocEngine(ipaddress, user, password))
{
    sproc.QualifiedProcedureName = ProcName;
    sproc.AddParameter("libname", As400SprocEngine.As400DbType.Character, 10,
        null, ParameterDirection.InputOutput);

    // Execute SPROC
    var retVals = sproc.ExecuteSproc();
    if (retVals == null || retVals.Count <= 0 )
        return null;

    string DataLibrary = retVals["libname"].ToString().Trim();
}

Processing multiple result sets:

using (var sproc = new As400SprocEngine(ipaddress, user, password))
{
    sproc.QualifiedProcedureName = ProcNames[searchType];
    sproc.AddParameter("@prefix", As400SprocEngine.As400DbType.Character, localityPrefix);
    sproc.AddParameter("@record", As400SprocEngine.As400DbType.Numeric, record);
    var header = new ParcelHeaderCollection();
    try
    {
        var ds = sproc.ExecuteSprocCollection();

        foreach (DataRow dr in ds.Tables[0].Rows)
        {
            var parcel = new ParcelHeader();
            parcel.Record = Convert.ToInt32(dr["mrecno"].ToString());
            parcel.Card = Convert.ToInt32(dr["mdwell"].ToString());
            parcel.OwnerName = dr["mlnam"].ToString().Trim();
            parcel.Map = dr["mmap"].ToString().Trim();
            parcel.StreetNumber = Convert.ToInt32(dr["house"].ToString());
            parcel.StreetName = dr["mstrt"].ToString().Trim();
            parcel.OccupancyCode = Convert.ToInt32(dr["moccup"].ToString());
            parcel.SalesPrice = Convert.ToInt32(dr["msellp"].ToString());
            parcel.YearSold = Convert.ToInt32(dr["myrsld"].ToString());
            parcel.MonthSold = Convert.ToInt32(dr["mmosld"].ToString());

            header.Add(parcel);
        }
    }
    // - you should use a more descriptive Exception type here
    catch (Exception ex)
    {
        throw ex;
    }

Final Notes

As always, Your Mileage May Vary.  This is written specifically for the iSeries, but you should be able adapt it to any other provider.  I have not worked much with Stored Procedures on any other system, so I’m not sure how it may need to be varied.

I hope you find this helpful. If you work in .NET with the iSeries, please share any tips or tricks you may have in the comments below.

Categories: AS400, iSeries, System i

IBM iSeries .NET Managed Provider ObjectDisposedException problem SOLVED

October 23, 2009 1 comment

I can’t believe how remiss I’ve been: this has been a big problem for me since early 2007, and we finally got it solved a couple of months ago, and I can’t believe I never blogged about it!

Two things to fix this:

1) Install iSeries Client Access V5R4.  Be sure to install the .NET Managed Provider if it is not already installed.  A full install will NOT do this, you must check the box to include it.

2) Install SI35287 on any PC that is connecting to the 400.  This is essentially V5R4 SP1 and will finally solve this little issue.

I do not know what the status of this bug on V6R1 is, but I will say that once I installed the SP this problem finally stopped.

Categories: .NET, AS400, iSeries, System i

Using Custom Validation Rules in WPF

October 13, 2009 7 comments

Anyone who has ever developed data driven applications of any sort has had to deal with validation.  Let’s face it, users make mistakes, even if they are developers!  Validation can be a real pain to implement, and contrary to all our HelloWorld style applications, MessageBox.Show is not the best way to inform our users that we have an issue with their keyboarding skills.

I remember being ecstatic when I learned about the WinForms ErrorProvider. This handy little approach finally gave us a nice way to alert the user of the problem without being too heavy handed in the UI.  Where it failed, though, was in customization.  First of all, it took a lot of code behind to manage the ErrorProvider: checking field values against business rules, updating the provider’s list of errors, or clearing it out when the status was OK ended up being a lot of code, especially when there were a lot of data fields. And then, beyond deciding whether or not the Error symbol would blink (I always hated the blinking exclamation point), there wasn’t a lot of pizzazz we could add.  Of course, that’s all changed with WPF.

Getting Started

Before I go any further, you can find most of the basics in these articles:

I recommend reading these articles. The CodeProject article even links to a Project with a WPF version of ErrorProvider.  I will be using a couple code samples from these articles.

I’m not going to harp too much on the details presented in these other articles.  Instead, I’m going to layout what I did to get this working and hopefully give you enough information to get started.

Using IDataErrorInfo

When you first start looking into validating data in WPF you will no doubt run into IDataErrorInfo.  This seems to be the jumping off point.  Implementing the interface is pretty straightforward.  Here is a typical example:

using System.ComponentModel;
namespace ValidationRulesPlay.ViewModels
{
    public class DataErrorInfoSample : ViewModelBase, IDataErrorInfo
    {
        private int _int1;
        private int _int2;

        public int Int1
        {
            get { return _int1; }
            set { _int1 = value; FirePropertyChangedEvent("Int1"); }
        }

        public int Int2
        {
            get { return _int2; }
            set { _int2 = value; FirePropertyChangedEvent("Int2"); }
        }

        #region IDataErrorInfo Members

        public string Error
        {
            get { return null; }
        }

        public string this[string columnName]
        {
            get
            {
                string result = null;

                switch (columnName)
                {
                    case "Int1":
                        if (Int1 > 9999999)
                            result = "Int1 number cannot be greater than 9999999.";
                        break;

                    case "Int2":
                        if (Int2 > 99)
                            result = "Int2 number cannot be greater than 99.";
                        break;


                    default:
                        break;
                }

                return result;
            }
        }

        #endregion
    }

}

[NOTE: the class above inherits from ViewModelBase, a class I use to implement INotifyPropertyChanged.]

And then you add a piece to your Binding syntax in XAML to activate it:

<TextBox
    x:Name="Int1TextBox"
    ValidatesOnDataError="True"
    VerticalAlignment="Center"
    HorizontalAlignment="Center"
    MinWidth="50"
    Margin="0,0,6,0"/>

This works and will by default wrap the offending textbox with a red border.  It’s not a bad experience for simple examples, but as I see it there are a couple of drawbacks.

First, IDataErrorInfo validated the data after the property has been set. So if I type in a value that is out of range, the target property is still invalidly set with the bad value.  This means that I have to do extra work to correct the error.  In other words, this approach lets me put bad data in that I must then take out.  Wouldn’t it be better if it just didn’t go in in the first place?  Second, this interface uses a custom indexer, which frankly just feels like magic.  It’s a personal preference, and a fear of the unknown, but I just don’t like it.

My other real issue with it is that it ends up being a lot of repetitive code.  Look at the example above and you’ll see two integer values that are both tested to be within a certain range.  While the ranges are different, the action is the same.  Of course, you could create a method, or better yet an Extension Method, to handle this situation.  Maybe it’s because of the hard-coded nature of the validation messages, but it still doesn’t feel right to me.

Creating Custom Validation Rules

Wouldn’t it be better to have a reusable piece of code to handle these similar situations?  One with no real hard coding?  In .NET 3.5 SP1 we have just such an animal, the Custom Validation Rule.

ValidationRule is an abstract class that we can inherit from to create a custom validation rule class of our own.  I could certainly create a custom rule class for each property in my DAL, and perhaps sometimes a custom class for a particular field would make sense, but many times all I need is a generalized validation rule. 

I mentioned previously that IDataErrorInfo checks the validation after the property has been updated.  This means if we attempt to insert a non-integer field with a non-integer value, the validity checking never occurs: instead, WPF swallows the error.  Using a validation rule causes the validation to occur before the property is updated, so we can easily prevent such an occurrence and report it to the user.  It does still allow the bad data into the TextBox, but that’s not necessarily a bad thing.

For this example, we need a validation rule that will report when the user attempts to insert a non-integer character.  We also want to be able to specify a range of acceptable values.  Additionally, we are going to add a few fields to help customize the messaging experience.  Here is the complete class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;

namespace CustomValidationRules
{
    public class IntegerValidationRule : ValidationRule
    {
        private int _min = int.MinValue;
        private int _max = int.MaxValue;
        private string _fieldName = "Field";
        private string _customMessage = String.Empty;

        public int Min
        {
            get { return _min; }
            set { _min = value; }
        }

        public int Max
        {
            get { return _max; }
            set { _max = value; }
        }

        public string FieldName
        {
            get { return _fieldName; }
            set { _fieldName = value; }
        }

        public string CustomMessage
        {
            get { return _customMessage; }
            set { _customMessage = value; }
        }


        public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
        {
            int num = 0;

            if (!int.TryParse(value.ToString(), out num))
                return new ValidationResult(false, String.Format("{0} must contain an integer value.", FieldName));

            if (num < Min || num > Max)
            {
                if (!String.IsNullOrEmpty(CustomMessage))
                    return new ValidationResult(false, CustomMessage);


                return new ValidationResult(false, String.Format("{0} must be between {1} and {2}.",
                                           FieldName, Min, Max));
            }

            return new ValidationResult(true, null);
        }
    }
}

The magic all happens in the overridden Validate method.  There are lots of additional ways you could customize this, but there should be enough in this example to get you started writing your own custom validation rules.

I put generalized rules like this in a separate project and namespace so I can reuse them in many projects.  For rules that are specific to a particular DAL or Application you can just as easily put them in those namespaces.

Consuming Custom Validation Rules

Now that we have an IntegerValidationRule class, let’s add it to our XAML so we can put it into action.  Unfortunately, there is no facility in Blend for doing so, so we’ll have to edit the XAML directly.  The first thing to do is add a namespace reference to the XAML pointing to the location of the custom validation rules.

We are going to add the validation to a TextBox.  Normally, I would use the shortened syntax to specify a binding, but in this case I’m going to use long hand because there is a lot of detail to add. Here is the completed sample:

<TextBox
    x:Name="Int1TextBox"
    Validation.ErrorTemplate="{StaticResource validationTemplate}"
    Style="{StaticResource textBoxInError}"
    VerticalAlignment="Center"
    HorizontalAlignment="Center"
    MinWidth="50"
    Margin="0,0,6,0">
    <Binding
        Path="Int1"
        UpdateSourceTrigger="PropertyChanged"
        Mode="TwoWay">
        <Binding.ValidationRules>
            <ValidationRules:IntegerValidationRule
                Min="1"
                Max="9999999"
                FieldName="Int1" />
        </Binding.ValidationRules>
    </Binding>
</TextBox>

There are a number of things going on here.  First, notice we added the Validation.ErrorTemplate property.  This specifies a template to use when the error condition exists.  I’ll cover that shortly along with the Style property. Both of these are used to customize the display of the error condition.

Next, in the TextBox.Text property we’ve added a long hand version of Binding.  I use UpdateSourceTrigger to cue the validation check whenever the property changes.  If I use the default value the change will only occur once the element loses focus, and I prefer a more real time effect.

Within the Binding property, we’ve added a subproperty section called Binding.ValidationRules.  Inside this property we add references to the rules we wish to implement. This will create an instance of the custom validation rule with the specified property values.  Since I set default values in the class, I can leave off any property and the validation will still function. As you can see in the example above, this is where I can set the property values for this particular instance of my IntegerValidationRule.  In this case we are allowing the integer range to be between 1 and 9999999, and specifying the name we want the message to associate with any error.

That’s all we need to do to consume the custom rule.  Now we’ll take a look at the ControlTemplate and Style to see how we can control the display.

Styling a Custom Validation

I took both the ControlTemplate and the Style from the MSDN article How To Implement Binding Validation.  Here are the original values:

<ControlTemplate
    x:Key="validationTemplate">
    <DockPanel>
        <TextBlock
            Foreground="Red"
            FontSize="20"><Run
                Text="!" />
        </TextBlock>
        <AdornedElementPlaceholder />
    </DockPanel>
</ControlTemplate>
<Style
    x:Key="textBoxInError"
    TargetType="{x:Type TextBox}">
    <Style.Triggers>
        <Trigger
            Property="Validation.HasError"
            Value="true">
            <Setter
                Property="ToolTip"
                Value="{Binding (Validation.Errors)[0].ErrorContent, RelativeSource={x:Static RelativeSource.Self}}" />
        </Trigger>
    </Style.Triggers>
</Style>

Insert these into the Windows.Resources section of the XAML.  If you’ve been coding along, you can run the sample now and it will work.  The only thing you really get is a red exclamation point to the left of the TextBox and a Tooltip if you hover over it.  This is OK, but I didn’t really feel it was enough to grab the user’s attention, so I’m going to change it just a little.

Before I do that, though, take a look at the ControlTemplate: there is something interesting here called an AdornedElementPlaceholder.  This nifty little element allows us to wrap the original ControlTemplate with additional elements.  In this sample, we are placing a TextBlock with the exclamation point just in front of our original control.  Feel free to experiment with this and add some code of your own around the offending TextBox.

Back to the Style: I want the TextBox to pop a little more when an error occurs.  Looking at the original Style code, we see that we have a Trigger that changes the Style when Validation.HasError is True.  In the default example, we are providing a ToolTip.  Here I’ve added some code to make the Background color of the TextBox Red and the Foreground White whenever an error occurs:

<Style
    x:Key="textBoxInError"
    TargetType="{x:Type TextBox}">
    <Style.Triggers>
        <Trigger
            Property="Validation.HasError"
            Value="true">
            <Setter
                Property="ToolTip"
                Value="{Binding (Validation.Errors)[0].ErrorContent, RelativeSource={x:Static RelativeSource.Self}}" />
            <Setter
                Property="Background"
                Value="Red" />
            <Setter
                Property="Foreground"
                Value="White" />
        </Trigger>
    </Style.Triggers>
</Style>

Now the user should definitely notice when an error occurs!  Again, I recommend you experiment and see what else you can do to spice up your validation errors.

And another thing…

Another thing that would be very useful is to be able to bind to the state of validation.  In other words, I have a button that I only want enabled if there are no Validation errors.

In WinForms, I used to process through all the Controls and check to see if any of them had a non-blank entry in the errorProvider.  In WPF, what I would really like is to be able to bind IsEnabled to a “HasErrors” property somewhere.  Unfortunately I couldn’t make something so simple work.  I did, however, come up with a solution using a RoutedCommand.  Commands and commanding are a topic for another time, but here is the short version of what I did. 

I created a static RoutedCommand object in my Window class:

public static RoutedCommand ValidateEntryCommand = new RoutedCommand();

I then added the supporting CommandBinding to my XAML file, including CanExecute:

<Window.CommandBindings>
    <CommandBinding
        Command="{x:Static ValidationRulesPlay:Window1.ValidateEntryCommand}"
        Executed="CommandBinding_Executed"
        CanExecute="CommandBinding_CanExecute" />
</Window.CommandBindings>

In the CanExecute event handler, I call Validation.HasError(DependencyObject) for each TextBox I want to validate against and set the event args CanExecute property based on the results.

private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    bool hasError = Validation.GetHasError(Int1TextBox) ||
                    Validation.GetHasError(Int2TextBox);

    e.CanExecute = !hasError;
}

Lastly, I set the Command property of the SaveButton to my Command name.

<Button
    x:Name="SaveButton"
    Content="Button"
    Command="{x:Static local:Window1.ValidateEntryCommand}"/>

Now, the button will be disabled if either of the TextBoxes have an error condition.

Conclusion

I hope this distills some of the basics for you and gets you on your way to writing your own custom validation rules.  As always, comments are encouraged.

You can download the full solution for this project, which includes some supporting code.

Categories: WPF, XAML

Another Community Update

October 9, 2009 1 comment

I can’t believe that September has come and gone so quickly.  After my most recent post, I expected my season to be winding down about now.  Little did I know that the most exciting things were yet to come.

What’s Past

Raleigh Code Camp

I’ve had the pleasure to participate in a couple great events since my last post.  In September I presented at Raleigh Code Camp, also known as RDU Code Camp.  Not that geography has much to do with it, but this was my first time speaking out of state.  I was really impressed by what a great community Raleigh has, and a special kudos to Dug Wilson who headed up the event.  I also had the chance to catch up with an old friend, Michael Marshall.  If you ever need SEO training or consulting, Mike is the genuine article and I highly recommend him.

Richmond Code Camp

Two weeks later we converged on the J. Sargeant Reynolds Community College campus for Richmond Code Camp 2009.2.  Richmond also has a great community, chock full of .NET heavyweights like Kevin Hazzard, Kevin Israel, Andy Leonard, and Justin Etheredge.  And let me tell you, they know how to throw an event!  Great facilities, fellowship, and first-class organization. Kudos to the entire staff. One of the speakers had to cancel last minute, so I got to fill in and give a second presentation.  This was my first time giving two, and I loved it!  For the first time I *almost* felt like I had enough time to speak. <grin>

As always, I cannot stress how wonderful it is to be part of such an active and awesome community.  The community presenters, organizers, and attendees all volunteer their time and knowledge – not a lot of other communities can say that.  If you have never been to a Code Camp or User Group, what are you waiting for?!?

What’s Ahead

Philly Code Camp

Oct. 17th I will be journeying out of state again, this time to present at my first Philly Code Camp.  I’ve heard nothing but great things about this event, and it will be different heading north for a change.  I will be presenting on one of my favorite topics, “Data Binding in Blend”.

CapArea Silverlight SIG – Expression Blend 3 Launch

Oct. 28th I will be presenting at the Expression Blend 3 Launch in Vienna, VA.  Thanks to Frank La Vigne for inviting me, I’m really looking forward to it!

Roanoke Valley Silverlight 3/Expression 3 Launch

Nov. 5th I will be back at my home group, RVNUG, to present at the Silverlight 3 and Expression 3 Launch.

CMAP Code Camp

Nov. 7th is Central Maryland Association of .NET Professionals (CMAP).  I have submitted two presentations, naturally on Blend, so I may be presenting there.  I will update when I know for sure.

PDC09

I’ve heard it said that everyone developing on the Microsoft stack needs to attend PDC at least once in their careers.  Well, fortune has smiled upon me and I will be attending PDC09 this year in Los Angeles, Nov 17-19. The session lineup looks incredible and I am very excited to be attending.  The only thing I already regret is that I will not be able to attend the FREE Win7 Workshop on the 16th.  I plan on blogging a lot while I’m there and sharing a lot when I get back.  I’m sure I won’t want this to be my only trip to PDC!

*WHEW*

I never intended this year to be so jam packed, but what a great year it has been for community events.  I also never intended this blog to have so many posts about myself, but don’t worry: I’ve got some exciting stuff in the pipeline, so I plan to return to more technical content once the event season calms down a bit.  Thanks for reading, hope to see you out in the Community!

Follow

Get every new post delivered to your Inbox.