# Saturday, April 17, 2010

Are you a .NET developer in the Twin Cities, Minnesota area that would be interested in having a mentor?  I'm looking for .NET developers to mentor.

I don't really have any specific criteria in mind and I don't expect that enough people read my blog/twitter that I will be overwhelmed with requests.

Ambition is probably the quality I'm looking for most.

If you are interested, contact me via e-mail and we can work out whether it's a good fit.

You can learn more about me here: http://www.capprime.com/About.htm

You can contact me through my contact page here: http://www.capprime.com/Contact.htm

I will try to come back and update this blog post if/when I no longer have time to mentor additional people, so consider this offer open for the foreseeable future.

Saturday, April 17, 2010 10:42:27 AM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  | 
# Monday, March 15, 2010

Apparently SMO can't get the SqlDataType of a UserDefinedDataType.

SQL Server Management Objects (SMO) is a pretty powerful API into Microsoft SQL Server.  I've been pretty happy using it in various scenarios over the years.

Recently, I was surprised to find out that SMO can't get the SqlDataType underlying a UserDefinedDataType.  This is reproducible using the Microsoft Pubs sample database.

Attempt #1:

string databaseName = "pubs";
string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings[databaseName].ToString();

SqlConnection oneSqlConnection = new SqlConnection(connectionString);
ServerConnection oneServerConnection = new ServerConnection(oneSqlConnection);
Server oneServer = new Server(oneServerConnection);
Database oneDatabase = new Database(oneServer, databaseName);
oneDatabase.Refresh();

UserDefinedDataType tidUserDefinedDataType =
 
   new Microsoft.SqlServer.Management.Smo.UserDefinedDataType(oneDatabase, "tid", "dbo");
bool initializeDidSucceed = tidUserDefinedDataType.Initialize(true);

In this scenario, for reasons unknown to me, initializeDidSucceed is false!

Now, if we try to access tidUserDefinedDataType.SystemType which seems like the appropriate data item, we get this exception:

Microsoft.SqlServer.Management.Smo.PropertyNotSetException: To accomplish this action, set property SystemType.
   at Microsoft.SqlServer.Management.Smo.SqlSmoObject.OnPropertyMissing(String propname, Boolean useDefaultValue)
   at Microsoft.SqlServer.Management.Smo.PropertyCollection.RetrieveProperty(Int32 index, Boolean useDefaultOnMissingValue)
   at Microsoft.SqlServer.Management.Smo.PropertyCollection.GetValueWithNullReplacement(String propertyName, Boolean throwOnNullValue, Boolean useDefaultOnMissingValue)
   at Microsoft.SqlServer.Management.Smo.UserDefinedDataType.get_SystemType()


Attempt #2:

string databaseName = "pubs";
string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings[databaseName].ToString();

SqlConnection oneSqlConnection = new SqlConnection(connectionString);
ServerConnection oneServerConnection = new ServerConnection(oneSqlConnection);

Server oneServer = new Server(oneServerConnection);
Database oneDatabase = new Database(oneServer, databaseName);
oneDatabase.Refresh();

Table oneTable = new Table(oneDatabase, "titles", "dbo");
oneTable.Refresh();

Now the following values are returned:

oneTable.Columns[0].Name                             = title_id
oneTable.Columns[0].DataType.Name               = tid
oneTable.Columns[0].DataType.SqlDataType      = UserDefinedDataType
oneTable.Columns[0].DataType.MaximumLength  = 6

So, we can successfully get the length of the underlying data type, but we can't get the SqlDataType.


Attempt #3:

USE pubs

DECLARE @CurrentSchemaName VARCHAR(128)
DECLARE @UserDefinedDataTypeName VARCHAR(128)

SET @CurrentSchemaName = 'dbo'
SET @UserDefinedDataTypeName = 'tid'

SELECT name
FROM sys.types
WHERE user_type_id =
  (
   SELECT system_type_id
   FROM sys.types
     INNER JOIN sys.schemas
       ON sys.types.schema_id = sys.schemas.schema_id
   WHERE sys.schemas.name = @CurrentSchemaName
     AND sys.types.name = @UserDefinedDataTypeName
  )

Using straight SQL, we appear to be able to get the answer we are looking for: "varchar".


I wonder why SMO is missing this capability?

Monday, March 15, 2010 12:56:31 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
# Saturday, February 20, 2010

T4 Templates are really nice given that the tools are included free in Visual Studio (the Express Editions of Visual Studio appear to have some under-documented limitations when it comes to T4 support).

The primary way that T4 templates are processed is from within Visual Studio.  That works pretty well.

What if you want to process a T4 template outside of Visual Studio (as part of an automated build process, for example)?

The easiest option for running a T4 template outside of Visual Studio is through the TextTransform.exe (custom T4 host) command line tool.

The main difficulty I've encountered using the TextTransform.exe command line tool is error handling.

Here's a sample error (written to StandardError):
--
MyT4Template.tt(0,0) : error : Running transformation: System.InvalidCastException: Unable to cast object of type 'Microsoft.VisualStudio.TextTemplating.CommandLine.CommandLineHost' to type 'System.IServiceProvider'.
   at Microsoft.VisualStudio.TextTemplatingc7fe0d0277b54f7c95c25936373918ff.GeneratedTextTransformation.TransformText()
--

Notice the (0,0).  That is where the line and column numbers would normally go for where the error occurred.  Also notice the stack trace contains no useful information (the error message, at least, in this case *is* useful).

The other downsides of using TextTransform.exe are similar to situations where you are calling out to an EXE file from code as opposed to working against an API.

The main positive I've encountered using the TextTransform.exe command line tool is if the template processes okay in Visual Studio, it will likely process okay using TextTransform.exe.  The above error was generated by using a template that tried to reference EnvDTE.DTE which works fine from within Visual Studio, but doesn’t necessarily make sense to do outside of Visual Studio.


As an alternative, you might attempt to use the Visual Studio T4 engine from .NET code like so:
--
ITextTemplatingEngineHost host = new Microsoft.VisualStudio.TextTemplating.VSHost.TextTemplatingService();
ITextTemplatingEngine engine = new Microsoft.VisualStudio.TextTemplating.Engine();
string outputCode = engine.ProcessTemplate(inputContent, host);
--

But that won't work.  You will get these compile time errors:
--
The type 'Microsoft.VisualStudio.TextTemplating.VSHost.TextTemplatingService' has no constructors defined
'Microsoft.VisualStudio.TextTemplating.VSHost.TextTemplatingService' is inaccessible due to its protection level
--


The hardest option for running a T4 template outside of Visual Studio is by implementing the ITextTemplatingEngineHost interface.  There is an example of how to do that here:

Walkthrough: Creating a Custom Text Template Host

The problem with that example is that it fails for multiple distinct reasons for templates that run just fine in Visual Studio and from TextTransform.exe.  It doesn’t appear to be even close to a full featured ITextTemplatingEngineHost implementation (it’s quite a "teaser" of a sample, enough to show it has potential, but not enough to show you how far from reality you are).

Mono provides an alternative implementation of the ITextTemplatingEngineHost interface here:

http://anonsvn.mono-project.com/viewvc/trunk/monodevelop/main/src/addins/TextTemplating/Mono.TextTemplating/Mono.TextTemplating/TemplateGenerator.cs

And while that fails less often on templates that run just fine in Visual Studio, it still fails in multiple ways.  I appreciate what the Mono folks have done, but for this use case (trying to run T4 templates outside of Visual Studio), there isn’t much value add.

The advantage of using a custom ITextTemplatingEngineHost host implementation is you have significantly more power and control than you have with TextTransform.exe.  TextTransform.exe has a limited input/output/error mechanism and its internals are not very extensible/customizable.

The disadvantage of using a custom ITextTemplatingEngineHost host implementation (as opposed to TextTransform.exe) is that you possibly have to write a decent amount of code against an undocumented system before your T4 template that processes fine in Visual Studio will process equivalently from .NET code (this will depend greatly on the contents of your T4 template).

If anyone is aware of other (free or commercial) custom implementations of the ITextTemplatingEngineHost interface that are usable from .NET code, please let me know!

Update March 7, 2010: Apparently this will be much easier in Visual Studio 2010:

Generating Text Files at Run Time by Using Preprocessed Text Templates

Saturday, February 20, 2010 3:09:50 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
# Monday, January 25, 2010

The main documentation for the NHibernate Validator is here:

http://nhforge.org/wikis/validator/nhibernate-validator-1-0-0-documentation.aspx

And it alludes to the fact that you can configure the validator to be called automatically before Insert/Update.

However, it fails to clearly communicate how to do this.

The important missing piece is that "ValidatorInitializer.Initialize()" needs to be called before the session factory gets created.

Here is some working sample code:

string connectionString =
 
System.Configuration.
ConfigurationManager.ConnectionStrings["Northwind"].ToString();

NHibernate.Validator.Engine.ValidatorEngine oneValidatorEngine =
 
new NHibernate.Validator.Engine.ValidatorEngine();

NHibernate.Validator.Cfg.INHVConfiguration oneINHVConfiguration =
 
new NHibernate.Validator.Cfg.Loquacious.FluentConfiguration();
//oneINHVConfiguration.Properties[NHibernate.Validator.Cfg.Environment.AutoregisterListeners] = "true";
oneINHVConfiguration.Properties[NHibernate.Validator.Cfg.Environment.ValidatorMode] = "UseExternal";
oneINHVConfiguration.Mappings.Add(
new NHibernate.Validator.Cfg.MappingConfiguration("YourNamespaceGoesHere", null));
oneValidatorEngine.Configure(oneINHVConfiguration);

NHibernate.Cfg.Configuration nHibernateConfiguration = new NHibernate.Cfg.Configuration();
nHibernateConfiguration.SetProperty(NHibernate.Cfg.
Environment.ConnectionString,
 
connectionString);
nHibernateConfiguration.SetProperty(NHibernate.Cfg.Environment.Dialect,
 
typeof(NHibernate.Dialect.MsSql2005Dialect).AssemblyQualifiedName);
nHibernateConfiguration.SetProperty(NHibernate.Cfg.Environment.ProxyFactoryFactoryClass,
 
typeof(NHibernate.ByteCode.Castle.ProxyFactoryFactory).AssemblyQualifiedName);
nHibernateConfiguration.AddAssembly(System.Reflection.
Assembly.GetCallingAssembly());

NHibernate.Validator.Cfg.ValidatorInitializer.Initialize(nHibernateConfiguration, oneValidatorEngine);

NHibernate.ISessionFactory oneISessionFactory = nHibernateConfiguration.BuildSessionFactory();

 

This code throws an NHibernate.PropertyValueException instead of an NHibernate.Exceptions.GenericADOException / System.Data.SqlClient.SqlException, which is what we want in this case.  Another exception I've seen the NHibernate Validator throw is NHibernate.Validator.Exceptions.InvalidStateException.

It's much easier and more pleasant to try to recover from a validation error than a database error.

Monday, January 25, 2010 2:42:45 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
# Sunday, September 27, 2009

I like to store my application's configuration in app.config or web.config (depending on the project type) which is pretty standard for .NET applications.  Therefore, I'm not a big fan of ActiveRecord's XmlConfigurationSource.

That said, the InPlaceConfigurationSource is somewhat difficult to puzzle out.  After reading the source code, I was able to bend it to my will.

In particular, I like this syntax well enough:

InPlaceConfigurationSource configuration =
    InPlaceConfigurationSource.Build(DatabaseType.MSSQLServer2005, connectionString);

It lets me setup my connection string and database type and default most everything else.

The "problem" is that I can't then do something clean and simple like this:

configuration.Add("show_sql", true);

Instead I have to do this:

Castle.Core.Configuration.MutableConfiguration oneMutableConfiguration =
  (Castle.Core.Configuration.MutableConfiguration)configuration.GetConfiguration(typeof(ActiveRecordBase));
oneMutableConfiguration.Children.Add(new
  Castle.Core.Configuration.MutableConfiguration("show_sql", true.ToString()));
configuration.Add(typeof(ActiveRecordBase), oneMutableConfiguration);

Which works, but it's not the friendliest API to program against.

Be careful, the "Add" method on InPlaceConfigurationSource is not really "Add".  It is really "Replace".

The other main alternative for using InPlaceConfigurationSource is something like this, which is useful for when I can't just use the defaults:

IDictionary<string, string> properties =
  new System.Collections.Generic.Dictionary<string, string>();
properties.Add("connection.driver_class", "NHibernate.Driver.SqlClientDriver");
properties.Add("connection.provider",
  "NHibernate.Connection.DriverConnectionProvider");
properties.Add("proxyfactory.factory_class",
  "NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle");

properties.Add("dialect", "NHibernate.Dialect.MsSql2005Dialect");
properties.Add("connection.connection_string", connectionString);

properties.Add("show_sql", true.ToString());

InPlaceConfigurationSource configuration = new InPlaceConfigurationSource();
configuration.Add(typeof(ActiveRecordBase), (IDictionary<string, string>)properties);


This was written and tested against "ActiveRecord 2.0 - August 1st, 2009".

Sunday, September 27, 2009 1:05:50 PM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  | 
# Thursday, September 24, 2009

As of 08/01/2010, this post has been rewritten and is now available here:

http://www.capprime.com/software_development_weblog/PermaLink,guid,d404eb9a-57bf-46e0-976a-9f81312ef5a8.aspx

ActiveWriter was replaced in the sample with the Agility for ORMs Castle ActiveRecord code generator (which I wrote) which generates higher quality Castle ActiveRecord code which results in a much smoother development process and a production worthy result.

The unedited original post follows:

--

NHibernate is a great ORM.  It's mature.  It's feature rich.  It's free.  Source code is available if necessary.

It now also supports Linq and Linq is great!

This is a quick guide to getting up and running with NHibernate and Linq quickly.  The overall experience feels vaguely similar to LinqToSql, without all the drawbacks and lock-in of LinqToSql.

We are going to assume our database already exists.  We are going to assume that database is Northwind, and we are going to assume that we are doing database driven design (as opposed to domain driven design).  Northwind setup is described below.

We are going to use Visual Studio 2008 with Service Pack 1 and SQL Server 2008 Express.  (Note: I'm not sure if everything here will work with Visual Web Developer 2008 Express Edition (a completely free stack).  I have Visual Web Developer 2008 Express Edition installed, but I'm not sure if the Active Writer install will work with it since the file type isn't showing up for me.)

To speed the process along, we are going to use Castle Active Record to implement a basic Active Record pattern.

This means we won't be using NHibernate's famous XML mapping files.

And to really speed things along, we are going to use Castle ActiveWriter to get some basic Active Record code generation.

While I wouldn't necessarily use this stack for a production application, it's a very quick way to get up and running.  This example is built on NHibernate and so all that goodness is still available later when necessary.  You aren't really tied to any proprietary or inflexible features.


Step 1:

Download and install ActiveWriter on top of VS 2008.  The download link is available from here:

http://using.castleproject.org/display/Contrib/ActiveWriter

At the time of writing, the current version is "Preview 4.1 (26/06/2008)".

I rebooted after the install since I have been burned one too many times, but rebooting is possibly not necessary.


Step 2:

Next, we will download and install the Northwind database.

Jeff Atwood provides approximate instructions here (ask specific questions in the comments if you get stuck):

http://www.codinghorror.com/blog/archives/000434.html

Follow the path for installing the binary files from the command line.  I have tested that the SQL 2005 instructions work on SQL 2008.


Step 3:

Download Castle Active Record:

http://www.castleproject.org/castle/download.html

At the time of writing, the current version is "ActiveRecord 2.0 - August 1st, 2009".  Unzip it and remember where you put it, you'll need that info in the next step.


Step 4:

Start Visual Studio 2008

We will create a new console application project:

File -> New -> Project
 Visual C# -> Windows -> Console Application
  ConsoleApplication1 -> OK

We need to add the appropriate NHibernate & Castle Active Record references:

Solution Explorer
 Right Click ConsoleApplication1 -> References -> Add
  Browse Tab
   Go to your Castle ActiveRecord 2.0 download location and add:
    Castle.ActiveRecord.dll
    Castle.DynamicProxy2.dll
    NHibernate.dll
    NHibernate.Linq.dll
   Click OK

And add a reference to System.Configuration as well:

Solution Explorer
 Right Click ConsoleApplication1 -> References -> Add
  .NET Tab
   System.Configuration
  Click OK


Step 5:

We will add the ActiveWriter1.actiw files to the project:

Solution Explorer
 Right Click ConsoleApplication1 -> Add -> New Item...
  Visual C# Items -> ActiveWriter (it's at the bottom of the list for me)
  Add

Click OK on the Security Warning

Close the class details window


Step 6:

We will find the Northwind database in the Server Explorer:

View -> Server Explorer
 Data Connections -> Add Connection...
  Choose your Server name
  Choose the correct login info
  Select "Northwind" as the database
  Click "Test Connection"
  Assuming that succeeds, click OK
 Expand the Northwind Tables node
 Now drag and drop the following tables to the Active Writer Designer surface:
  Orders
  Shippers

Save the ActiveWriter1.actiw file
Click OK on the Security Warning
Close the ActiveWriter1.actiw file

You can look through the new ActiveWriter1.cs file in Solution Explorer to see the generated classes we are going to use later.


Step 7:

We will now add an Application Configuration File to the project and put the Northwind Connection String into it:

Solution Explorer
 Right Click ConsoleApplication1 -> Add -> New Item...
  Visual C# Items -> General -> Application Configuration File
  Add

Modify the file to look like this and use your specific DB connection info:

--
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <add name="Northwind" connectionString="Data Source=ServerName;Initial Catalog=Northwind;User ID=sa;Password=password;"/>
  </connectionStrings>
</configuration>
--


Step 8:

Open the main "Program.cs" class and add the following new methods:

        private static void InitializeNHibernateActiveRecord()
        {
            string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["Northwind"].ToString();
            InPlaceConfigurationSource configuration = InPlaceConfigurationSource.Build(DatabaseType.MSSQLServer2005, connectionString);

            ActiveRecordStarter.Initialize(configuration,
                typeof(Orders),
                typeof(Shippers)
                );
        }

        private static ISession CreateNHibernateActiveRecordSession()
        {
            // This uses the values setup during ActiveRecordStarter.Initialize() to configure and create the session
            return ActiveRecordMediator.GetSessionFactoryHolder().CreateSession(typeof(ActiveRecordBase));
        }

Add the following code to the Main Method:

        static void Main(string[] args)
        {
            try
            {
                InitializeNHibernateActiveRecord();

                using (ISession dbSession = CreateNHibernateActiveRecordSession())
                {
                    var queryToExecute = from oneShippers in dbSession.Linq<Shippers>()
                                         select oneShippers;

                    foreach (Shippers oneShipper in queryToExecute.ToList())
                    {
                        Console.WriteLine("oneShipper ShipperID=[" + oneShipper.ShipperID + "] CompanyName=[" + oneShipper.CompanyName + "]");
                    }                }
            }
            catch (Exception oneException)
            {
                Console.WriteLine("oneException=[" + oneException + "]");
                throw; // you can remove this if you'd rather the program exit "more normally"
            }
        }

Add the following using statements at the top of Program.cs:

using Castle.ActiveRecord;
using Castle.ActiveRecord.Framework.Config;

using NHibernate;
using NHibernate.Linq;


Step 9:

Copy the NHibernate.ByteCode.Castle.dll file from your Castle Active Record download unzip directory to your projects bin\Debug\ folder.


Step 10:

Run the application:

Ctrl-F5 (Debug -> Start Without Debugging)

And you should see the following output:

--
oneShipper ShipperID=[1] CompanyName=[Speedy Express]
oneShipper ShipperID=[2] CompanyName=[United Package]
oneShipper ShipperID=[3] CompanyName=[Federal Shipping]
Press any key to continue . . .
--

So, the basics are working.

I would have loved to show some Order Details instead of Shippers, but there appear to be a lot of complexities with the Northwind database that don't seem to work well with the current release versions of ActiveWriter / ActiveRecord.  Hopefully those issues will be fixed over time.


Step 11:

Now let's do a query which requires a join.

The CustomerID field on the Orders class first needs to have it's column type changed for it to work.

(Re)open the ActiveWriter1.actiw file.

Right Click on Orders -> CustomerID -> Properties
 Set the Column Type to String


Step 12:

Let's model the relationship between Orders and Shippers in the Designer.

The foreign key relationship is Orders.ShipVia <-> Shippers.ShipperID, with one shipper having 0 to many orders.

With the ActiveWriter1.actiw designer open, go to the toolbox (View -> Toolbox).

Select the "Many To One Relationship" option under "ActiveWriter".

Click (and hold the click) on the Orders table and drag the line onto the Shippers table.

Highlight the line between the two tables and right click -> Properties.

Set the Source Column to ShipVia
Set the Target Table to Orders


Step 13:

Let's add a Linq query to the Main method for Orders to exercise that new relationship:

                    var queryToExecute = from oneOrders in dbSession.Linq<Orders>()
                                         select oneOrders;

                    foreach (Orders oneOrder in queryToExecute.ToList())
                    {
                        Console.WriteLine("oneOrder OrderID=[" + oneOrder.OrderID + "] ShipVia=[" + oneOrder.ShipVia + "] Shippers.CompanyName=[" + oneOrder.Shippers.CompanyName + "]");
                    }


Step 14:

Run the application:

Ctrl-F5 (Debug -> Start Without Debugging)

And you should see the following output:

--
oneOrder OrderID=[10248] ShipVia=[3] Shippers.CompanyName=[Federal Shipping]
oneOrder OrderID=[10249] ShipVia=[1] Shippers.CompanyName=[Speedy Express]

<snip>

oneOrder OrderID=[11076] ShipVia=[2] Shippers.CompanyName=[United Package]
oneOrder OrderID=[11077] ShipVia=[2] Shippers.CompanyName=[United Package]
--


We have successfully executed a join query through Linq.

I was somewhat surprised when I found out this was running 4 queries under the covers.  Basically:

SELECT Orders.*, Shippers.* FROM Orders LEFT OUTER JOIN Shippers ON Orders.ShipVia = Shippers.ShipperID
SELECT * FROM Orders WHERE ShipVia = 2
SELECT * FROM Orders WHERE ShipVia = 1
SELECT * FROM Orders WHERE ShipVia = 3

I either expected 1 query (eager fetch) or 831 queries (lazy fetch - 1 query for the list of orders + 1 query for each order to get the Shipper Company Name).

The results of first query contain enough information to display what the code is asking for, so it's definitely eager fetch behavior.

Changing the code to only print OrderID does not appear to change which queries are run.

So don't necessarily make assumptions about how your ORM is going to be generating queries under the covers, especially if performance seems to be an issue when it shouldn't be.


Some additional things to note:

Castle ActiveRecord provides an ActiveRecordLinq.AsQueryable method that would appear to be useable in place of the ISession.Linq method (with a call to new up a SessionScope instead of calling CreateSession), but I couldn't get that to work (I didn't dig into it).

While the code sample above makes for a decent demo and a quick start, your mileage will likely vary.  When things work great, they work great.  This sample didn't end up being as I planned though because I had to work around / avoid things that I couldn't easily get to work (like Order Details).  In the end, I am very satisfied that the code sample is pretty short and straight-forward, yet so much power is happening behind the scenes.

NHibernate supports pretty much all the weirdness your database needs, but ActiveRecord supports less weirdness, and ActiveWriter supports even less.  The higher up the stack/abstraction you get, the more likely edge cases haven't been tested/implemented yet.

Now that you have the basic NHibernate Linq infrastructure in place, there are plenty of Linq examples and sample code available elsewhere.

Enjoy!

Thursday, September 24, 2009 9:53:46 AM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [1]  | 
# Monday, September 07, 2009

I'm sure it's been done before and I'm sure it will be done again.  Sometimes habits work to your advantage and sometimes they don't.

Here's a case where a habit bit me recently.

static void Main(string[] args)
{
    try
    {
        object dbConnection = GetDbConnectionWithRetry(0);
    }
    catch (Exception oneException)
    {
        Console.WriteLine("oneException=[" + oneException.ToString() + "]");
    }
}

private
static object GetDbConnectionWithRetry(int recursionCount)
{
    Console.WriteLine("GetDbConnectionWithRetry recursionCount=[" + recursionCount + "]");
    object dbConnection = null;
    if (recursionCount < 10)
    {
        dbConnection = OpenDbConnection();
        if (dbConnection == null)
        {
            dbConnection = GetDbConnectionWithRetry(recursionCount++);
        }
    }
    return dbConnection;
}

private
static object OpenDbConnection()
{
    // This is just a dummy method for sample code purposes
    return null;
}

It's humorous in retrospect.  Can you spot the bug?  What habit should I break myself of?

Monday, September 07, 2009 10:22:02 AM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [1]  | 
# Saturday, August 22, 2009

Quite a while ago, I had a fairly bad experience at Stack Overflow and wrote a blog post about it:

StackOverflow Impressions - Too Many Rough Edges

I have now returned to being a contributor at Stack Overflow.  But why?

More and more Google searches are leading me to Stack Overflow for answers.  The answers are often useful.  Stack Overflow has no meaningful direct competition.

I'm a pragmatic person and I appreciate that the service provided by Stack Overflow is useful.

Since I'm gaining value from the site on a regular basis now, it makes sense to try to help out and "give back" to the community by providing answers when possible.

Stack Overflow is "good enough" to support.

Also, while I believe the benefits of putting a Stack Overflow reputation number on a resume are overstated (and include some small amount of risk), I do think asking and answering questions on Stack Overflow is an important indirect marketing opportunity for individuals.  As one example, if a blog post I've written answers a question some one asked, answering the question on Stack Overflow with a pointer to my blog post is helpful and increases the useful life/value of that blog post.  I think blog posts are a great way to expand the programmer knowledge base, but not all blog posts will be easily found through Google searches (Providing links to your blog from Stack Overflow will not increase your blog's page rank, but people will click through none the less).

Saturday, August 22, 2009 1:59:00 PM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  | 
# Sunday, August 16, 2009

After a number of small releases focused on application infrastructure, this release is a semi-major user focused feature release.

The biggest new feature is treating function parameters as obfuscatable variables.

A user requested that function/variable names not grow in size as part of obfuscation, so function/variable names now stay the same size.  This may continue to be refined over time depending on what people think.

Since this obfuscator was originally written for my own use, it made some assumptions about coding style, especially in regards to whitespace.  A number of those types of assumptions have now been removed based on testing against code submitted by users.

While some substring type issues still remain, it is now possible to have local variables which are substrings of one another like "counter" and "counter1" without the obfuscator missing a beat.

It used to be possible to have a variable named "funct" (which is a substring of the lua reserved word "function"), but this is no longer allowed.  More work is needed in this area (not mangling lua reserved words) going forward.

The latest version of the Capprime Lua Obfuscator is available here:

http://www.capprime.com/CapprimeLuaObfuscator/CapprimeLuaObfuscator.aspx

Sunday, August 16, 2009 12:30:13 PM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  | 
# Monday, July 27, 2009

This release increases the number of characters that can be obfuscated at one time from 1000 to 3000.

The daily character obfuscation limit has also been increased from 3000 to 6000.

These limits may change again in the future depending on usage and feedback.

The Capprime Lua Obfuscator is available here:

http://www.capprime.com/CapprimeLuaObfuscator/CapprimeLuaObfuscator.aspx

Monday, July 27, 2009 10:19:07 AM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  |