Thursday, August 30, 2007

Don't forget to close your Linq to Sql connections

Today I was working on some Linq to Sql code that dealt with an event management system. There are events people can attend, and attendees fill out a form to register for the event. The fields for each event are configurable by the person that creates the event. When I save a set of event fields I want to do it in a transaction so I don't end up with a partial "record". Here's what my code looked like originally:

public void SaveEventAttendeeFields(EventAttendeeField[] fields)
{
using (LinqToSqlDataContext dc = new LinqToSqlDataContext())
{
dc.DeferredLoadingEnabled = false;

try { dc.Connection.Open(); dc.Transaction = dc.Connection.BeginTransaction(); foreach (EventAttendeeField f in fields)
{
if (f.EventAddendeeFieldId > 0)
dc.EventAttendeeFields.Attach(f, true);
else dc.EventAttendeeFields.Add(f); } dc.SubmitChanges(); dc.Transaction.Commit(); } catch { if (null != dc.Transaction)
dc.Transaction.Rollback();

throw;
}
}
}

I assumed this was perfectly fine, until I ran it through my unit test library. My test library automatically creates a new database, tables, sprocs, and then populates it with seed test data. At the end of the test run it drops the database that was created. Well, the drop was failing because the database was in use! I thought "hmm, I wonder if my connections aren't being closed." Sure enough, if you manually open the connection in the DataContext you had better manually close it as well!

public void SaveEventAttendeeFields(EventAttendeeField[] fields)
{
using (LinqToSqlDataContext dc = new LinqToSqlDataContext())
{
dc.DeferredLoadingEnabled = false;

try { dc.Connection.Open(); dc.Transaction = dc.Connection.BeginTransaction(); foreach (EventAttendeeField f in fields)
{
if (f.EventAddendeeFieldId > 0)
dc.EventAttendeeFields.Attach(f, true);
else dc.EventAttendeeFields.Add(f); } dc.SubmitChanges(); dc.Transaction.Commit(); } finally { if (null != dc.Connection && dc.Connection.State != System.Data.ConnectionState.Closed)
dc.Connection.Close();
}
}
}

This code didn't cause any leaks. So, moral of the story, close your connections in your DataContext if you open them.

Tuesday, August 28, 2007

Clean Up a String for a Url

This is a quickie. Yesterday I was doing some Url rewriting for a project and accepting user input for said Url. It's basically like this blog system. I can specify the friendly url of each post. So, I wrote a little extension method to clean up a string and make it URL friendly. Yes, you can just escape everything someone enters, but it's much friendlier to just make it work. Be lenient in what you accept! Here it is:

public static string ToUrlString(this string s)
{
if (null == s)
throw new NullReferenceException();

string tmp = Regex.Replace(s.ToLower(), @"\s", @"-");
tmp = Regex.Replace(tmp, @"[^-_\.\w]", @"");

return tmp;
}

This simply replaces all whitespace with a "-", and all characters that are not "-", "_", ".", or Word Characters with an empty string.

In the end, if your input is "this is a @#)(*&*@$^ crock!" you'll get "this-is-a--crock". As a guy who was, at one time, scared of regular expressions I hope this helps someone.

Wednesday, August 22, 2007

using (wcfclient) { } kaboom!

I've been doing a lot of WCF development lately (love it) and ran into a bit of a stumbling block. The proxy client generated by Visual Studio 2008 Beta 2 doesn't handle fault conditions and dispose well. I have to admit, I haven't used WCF in Visual Studio 2005. If you're impatient here's the project (Visual Studio 2008 Beta 2 project): wcfwrapper.zip The situation unfolds:

The Problem - Dispose Isn't Safe!

  1. Create a WCF service.
    [ServiceContract]
    public interface IGoodBadService
    {
    [OperationContract]
    void Good();

    [OperationContract]
    void Bad();
    }
  2. Add a service reference to your service in another project.
  3. Create a service client and use it, with a "using" block (it is IDisposable, afterall).
    try
    {
    using (GoodBadServiceClient c =
    new GoodBadServiceClient())
    {
    c.Bad();
    }
    }
    catch (Exception ex)
    {
    Console.WriteLine(ex.Message);
    }
  4. A call to your service, for whatever reason, throws an exception.
    public class GoodBadService
    : IGoodBadService
    {
    public void Good()
    {
    throw new Exception("GOOD!");
    }

    public void Bad()
    {
    throw new Exception("BAD!");
    }
    }
  5. You get a cryptic System.ServiceModel.CommunicationObjectFaultedException instead of the real exception when using calls Dispose() on your proxy client. It reads: The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it is in the Faulted state.

The Solution - A Simple Generic Wrapper

public class ServiceProxy<TClient>
: IDisposable
where TClient : ICommunicationObject
{
public ServiceProxy(TClient client)
{
Client = client;
}

public TClient Client { get; private set; }

public void Dispose()
{
if (null != Client &&
Client.State != CommunicationState.Closed &&
Client.State != CommunicationState.Faulted)
{
Client.Close();
}
}
}

Using the wrapper is pretty straightforward, but a tad more cryptic than just using the client directly. Some of this can be avoided with a generic factory.

So, here's how you use it:

try
{
using (ServiceProxy<GoodBadServiceClient> c
= new ServiceProxy<GoodBadServiceClient>(
new GoodBadServiceClient()))
{
c.Client.Good();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Run the project and check out the results. The wrapper gives you the actual exception, whereas "using" the client directly causes the CommunicationObjectFaultedException. Here's the project (Visual Studio 2008 Beta 2 project): wcfwrapper.zip

Tuesday, August 21, 2007

Relocation

Well, I've officially relocated my blog here, to jdconley.com. The old blogging engine I was using really sucked. This one seems a lot better. And if I don't like something, the source code looks pretty clean so I can get my hands dirty and change it. I'm also a fan of one of the Haacks that wrote it -- well, his blog at least.

In addition to a new blog engine my new location has implicitly granted me a little freedom. Fear not, my blog will no longer be 100% about Coversant related items. I've been exploring the wide world of .NET 3.5, Visual Studio 2008, and everything in between. I've got a few articles that need some polish and I'll be posting them here.

About the Author

Wow, you made it to the bottom! That means we're destined to be life long friends. Follow Me on Twitter.

I am an entrepreneur and hacker. I'm a Cofounder at RealCrowd. Most recently I was CTO at Hive7, a social gaming startup that sold to Playdom and then Disney. These are my stories.

You can find far too much information about me on linkedin: http://linkedin.com/in/jdconley. No, I'm not interested in an amazing Paradox DBA role in the Antarctic with an excellent culture!