Tag: C#
Type object code smells
by bhartsock on Nov.02, 2009, under Uncategorized
I have been playing around with the Enyim Memcached client for .NET over the past few days. It has a lot of really cool functionality, but one big smell that has been bugging me. Extensibility is used through the Type class, not through dependency injection. Here is the interface for the configuration object.
public class MemcachedClientConfiguration : IMemcachedClientConfiguration { public MemcachedClientConfiguration(); public Type NodeLocator { get; set; } public Type KeyTransformer { get; set; } public IList<IPEndPoint> Servers { get; } public ISocketPoolConfiguration SocketPool { get; } public Type Transcoder { get; set; } }
Note that KeyTransformer, NodeLocator, and Transcoder are extension points for the library (really cool extension points at that). What sucks is they are Type objects, not the real implementation classes. Here is an example of how you configure the NodeLocator.
config.NodeLocator = typeof(MyNodeLocator);
Try to implement the decorator pattern in a scenario like this. You can’t, because the library is in complete control of the life-cycle of the object. There are two solutions.
public class MemcachedClientConfiguration : IMemcachedClientConfiguration { public MemcachedClientConfiguration(); //Notice the change to an interface public IMemcachedNodeLocator NodeLocator { get; set; } public Type KeyTransformer { get; set; } public IList<IPEndPoint> Servers { get; } public ISocketPoolConfiguration SocketPool { get; } public Type Transcoder { get; set; } } //And then configure like follows. config.NodeLocator = new MyNodeLocator();
But what if controlling the life-cycle is important to the library? Well, that’s why the factory pattern exists.
public class MyNodeLocatorFactory : IMemcachedNodeLocatorFactory { IMemcachedNodeLocator Create(); }
So try to avoid referencing Type objects. As you can see, it smells of an inflexible design. Not to mention leaves the possibility open to runtime errors for no good reason.
When TDD Goes Bad, NUnit wasted 2 hours of my night
by bhartsock on Oct.16, 2009, under Uncategorized
[TestFixture] public class IHateNunit { [Test] public void A_list_works() { IList<string> strs = new List<string>() { "asdf" }; //Double Pass Assert.That(strs.Count, Is.EqualTo(1)); Assert.That(strs, Has.Count(1)); } [Test] public void An_array_doesnt() { IList<string> strs = new string[] { "asdf" }; //Pass Assert.That(strs.Count, Is.EqualTo(1)); //#FAIL Assert.That(strs, Has.Count(1)); } }
NHibernate CodeCamp Talk – Source Code
by bhartsock on Oct.04, 2009, under Uncategorized
I have published the source of my Code Camp talk here. Check it out and post any questions.
Thanks to everyone who showed up for the NHibernate talk. It went really well and everyone had tons of great questions. Feel free to post feedback here, both positive and negative, so the next time I give this talk, it will be even better.
For those of you that just want a little more guidance on NHibernate, here are some good links:
Also, thanks to the coordinators of Richmond CC. I had a great time.
Interesting SQL Server Mirroring Problem
by bhartsock on Sep.29, 2009, under Uncategorized
I have been trying to narrow down a SQL Server connection timeout problem, that only occurs when a database is mirrored. I have yet to be able to really figure out the cause, but here is the code that describes the problem.
[TestFixture] public class SqlMirroringTests { const string connectionString = "Data Source=some_server;Initial Catalog=some_db; User ID=*****;Password=*****;"; private static Random rand = new Random(); private ArrayList times = ArrayList.Synchronized(new ArrayList()); private ArrayList erroredTimes = ArrayList.Synchronized(new ArrayList()); private void DbScenario(int identifier) { var sleepTime = Random(); WriteInfo(identifier, "Sleeping " + sleepTime + "ms"); Thread.Sleep(sleepTime); using (var conn = new SqlConnection(connectionString)) { var stopWatch = new Stopwatch(); stopWatch.Start(); bool isErrored = false; try { conn.Open(); } catch(Exception e) { Console.WriteLine(e); isErrored = true; throw e; } finally { var timespan = stopWatch.Elapsed; WriteInfo(identifier, "Open time: " + timespan); times.Add(timespan); if (isErrored) { erroredTimes.Add(timespan); } } //We have to actually query for something to get the error, and it needs to be a decent chunk of data var command = conn.CreateCommand(); command.CommandText = "select * from <Insert any table here with some data>"; command.ExecuteReader(); conn.Close(); } WriteInfo(identifier, "DONE"); } [Test] public void SlamDatabase() { var exceptions = new List<Exception>(); var action = new Action<int>(DbScenario); var results = Enumerable.Range(0, 250) .Select(i => action.BeginInvoke(i, null, null)) .ToList(); results.ForEach(ar => { try { action.EndInvoke(ar); } catch (Exception e) { exceptions.Add(e); } }); Console.WriteLine("Number of Exceptions: " + exceptions.Count); WriteTimeResults("All times - ", times); WriteTimeResults("Error times - ", erroredTimes); } private void WriteTimeResults(string prefix, ArrayList results) { Console.WriteLine(prefix + "Max Open time: " + results.Cast<TimeSpan>().Max()); Console.WriteLine(prefix + "Avg Open time: " + results.Cast<TimeSpan>().Average(t => t.TotalSeconds)); } private int Random() { return rand.Next(1000); } private void WriteInfo(object state, string message) { Console.WriteLine("Thread " + state.ToString() + " - " + message); } }
It throws two possible errors:
System.Data.SqlClient.SqlException: Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding. at System.Data.ProviderBase.DbConnectionPool.GetConnection(DbConnection owningObject) at System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection) at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory) at System.Data.SqlClient.SqlConnection.Open() at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory) at System.Data.SqlClient.SqlConnection.Open() at System.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject) at System.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject) at System.Data.ProviderBase.DbConnectionPool.GetConnection(DbConnection owningObject) at System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection) at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory) at System.Data.SqlClient.SqlConnection.Open()
And …
System.Data.SqlClient.SqlException: Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding. at System.Data.ProviderBase.DbConnectionPool.GetConnection(DbConnection owningObject) at System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection) at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory) at System.Data.SqlClient.SqlConnection.Open()
The odd thing is. Here are the results:
Number of Exceptions: 118 All times - Max Open time: 00:00:02.7301659 All times - Avg Open time: 0.0934956888 Error times - Max Open time: 00:00:02.7301659 Error times - Avg Open time: 0.0855163728813559
118 errors out of 250! After one happens, a ton of others happen. I am guessing the pool get’s jacked up and starts looking at the mirror to login temporarily or something, but I can’t figure out why. Increasing the Connect Timeout resolves the problem, but I HATE increasing timeouts. The funny thing is, according to my results, the average timed out connection is .08s. That 625x less than the connect timeout.
Finally, to make matters even more confusing, if you look at my connection string, I didn’t setup the connection for failover. Although I believe that failover still works, except on the initial connection.
Any ideas would be greatly appreciated.
Why you should use NHibernate.Linq, not Criteria or HQL
by bhartsock on Sep.28, 2009, under Uncategorized
What’s wrong with the following?
return s.CreateQuery("from CodeCamp c where c.Name = :name order by c.Name") .SetParameter("name", "test") .List<CodeCamp>();
return s.CreateCriteria<CodeCamp>() .Add(Expression.Eq("Name", "test")) .AddOrder(Order.Asc("Name")) .List<CodeCamp>();
There are string references to .NET properties. That is a no-no. If there is a typo, you get runtime errors and not compile time errors. Now, good tests would catch it, but if you want the absolute fastest way to catch typos, NHibernate.Linq is the way to go.
return (from camp in s.Linq<CodeCamp>() where camp.Name == "test" orderby camp.Name select camp).ToList();
With Linq, you get that compile time checking that is going to help you move a little bit faster and make your code more robust. Combine this with Fluent NHibernate and you have a solid solution. Your mappings still have to be correct of course, but these projects take two variables out of the equation and use the compiler as a safety blanket.