Just another tidbit from my last post Reserving Judgment. I was reading Scott Guthrie's latest post on the ASP.NET MVC framework (ASP.NET MVC Framework (Part 4): Handling Form Edit and Post Scenarios), and noticed that he showed a tool which was using extension methods in much the same way Greg Young was describing. See Scott's use of the ASP.NET MVC HTML Helpers.
What can I say, I actually kind of like how they work in that scenario.
Sunday, December 9, 2007
More on Extension Method Judgment
Posted by
John Chapman
at
11:19 AM
0
comments
Labels: Visual Studio 2008
Reserving Judgment
I have been very quick to put down certain pieces of the .NET 3.5 framework (reference prior post on extension methods and partial methods), most specifically extension methods and partial methods. I'm not quite ready to give in on partial methods yet, they still just seem like a tool used to make code generators slightly easier, but I think I'm ready to reserve judgment on extension methods now.
I still think extension methods are dangerous and are going to be subject to blatant misuse. That being said, I think I need to keep an open mind regarding the potential uses for extension methods. Note that I'm referring to the use of extension methods entirely within code that you control.
I recently read a blog post by Greg Young titled A Use For Extension Methods where he basically stated that there are a lot of people out there that share my kind of viewpoint. He also wanted to show one possible way where extension methods could be used. He basically showed that extension methods were useful in controlling the context of intellisense when using a fluent interface for a pseudo-builder pattern. I call it pseudo since really there is only one type of available builder.
Basically his extension method looks like the following:
public class Builder
{
}
public class Create
{
public static Builder New = new Builder();
}
public static CostBuilder Cost(this Builder s)
{
return new CostBuilder();
}
So now when you call the Create.New you'll only see the builders which are in the namespaces which you referenced in your using directives.
Fair enough, upon seeing this I thought it seemed interesting, but went in to my pessimistic anti-extension method mode and came up with my usual answer, why not just do this:
public class Create
{
public T New
() where T : new() {
return new T();
}
}
But upon reading this, it's pretty lame isn't it? It's basically a generic factory that doesn't new anything, why not just call new CostBuilder().DoStuff().DoMoreStuff()?
But Greg's interface is a bit cleaner than what I am offering. It's not that extension methods are really needed here, it's just that it can actually make your code a little cleaner and slightly easier to read (when working on large projects), which is something I didn't expect to say regarding extension methods.
Maybe I need to wait this one out for a year to see what kinds of things developers come up with while using extension methods.
--John Chapman
Posted by
John Chapman
at
10:06 AM
0
comments
Labels: Visual Studio 2008
Saturday, December 8, 2007
C# Type Inference But Still Strongly Typed
I have spent considerable time today reviewing Linq and more specifically Linq to Sql. I'm currently working on a blog post where I'll go in to the details of what I think the pros and cons of Linq to Sql are as well as my overall opinion. In case you couldn't guess it I'll be using NHibernate for my comparisons, after all it is what I'm familiar with.
While reviewing some things I ran in to the following compile time check. It was very simple for me to resolve, but I wonder if it will cause developers to fall in to traps. Especially those developers who have some experience with weakly typed languages such as Javascript.
Take a look at the following code I wrote:
AdventureWorksDataContext context =
new AdventureWorksDataContext();
var orders = from po in context.PurchaseOrderHeaders
select po;
if (chkUseDate.Checked)
{
orders = from po in orders
where po.OrderDate > dtOrderFrom.Value
select po;
}
orders = from po in orders
orderby po.OrderDate ascending
select new
{
po.PurchaseOrderID,
po.RevisionNumber,
po.OrderDate,
po.ShipDate
};
Does anyone see what is wrong with the code above and why it failed to compile?
The compile-time error was:
Cannot implicitly convert type 'System.Linq.IQueryable<AnonymousType#1>' to 'System.Linq.IQueryable<BLL.PurchaseOrderHeader>'. An explicit conversion exists (are you missing a cast?)
After seeing that I immediately realized that I tried to use an object which type inferred to return PurchaseOrderHeader objects to return anonymous type objects instead. You can't just change a reference to be of another type in C# 3.0, hence the strong typing, I should know better.
But honestly, with the whole var keyword, I wasn't really thinking about it. It was a minor slip up, but I wonder how many developers will fall in to that trap. I think some developers may have seen the var keyword before in Javascript and they may have used it in the fashion I just did.
That being said, I have been enjoying my time with Linq today. I should have a post up within the next few days with more details.
P.S. If you're wondering what is going on with the 3 step linq ueries above, that's how you write dynamic queries in Linq. Simply reference the previously defined query in your new linq query in order to further restrict the query which you are building. Keep in mind that writing a linq query doesn't perform any operations. You have to either enumerate over the values of the query or call a method on the query like ToArray(), ToDictionary(), Select() etc...
If you're curious how to resolve the issue above you just need to declare a new variable for the last query to store the new type. var results = <Linq expression> would work just fine.
--John Chapman
Posted by
John Chapman
at
7:15 PM
5
comments
Labels: C#, LINQ, Visual Studio 2008
Solution Folders To Group Projects
While reading Scott Guthrie's blog today I stumbled upon one of his links of the day for december 8th entitled Big Solutions Can Be Organized Using Solution Folders which is at the .NET Tip of the Day web site.
Actually, this is really cool. Am I the only schmuck who didn't realize this was possible? I have used Solution Folders in the past, but it was always to group files which were not part of the actual build process. For example we always had a Libraries folder which contained the third party dlls which need to be referenced to build the project. This way all a developer needs to do to build the project is to download the latest source code, and the solution will automatically download the needed referenced assemblies. Plus any upgrade we do to a third party dll automatically propagates when someone gets latest.
But the ability to place projects within the folders to group them together? This will actually be very handy for us. At work our current solution has 33 projects, and truthfully it is growing! Being able to group the projects which are similar or somehow related will really come in handy for us.
Why didn't I realize this sooner?
--John Chapman
Posted by
John Chapman
at
4:49 PM
0
comments
Labels: Visual Studio 2005, Visual Studio 2008
Testing For Collections
Where I work we developed some foundation classes to assist with our unit testing. We make heavy use of NHibernate on our product (We work one very large application) and wanted a way to ensure that we were not breaking our mapping files over time.
In order to accomplish this goal we developed a mechanism to manage multiple sessions and then compare object graphs between the two sessions. Basically we would call a creation method for the specified object type we are testing, assign all properties with random values, and then assign many-to-one associations using the child object's creation method.
The creation method has the option of automatically saving the constructed objects to an NHibernate session or not (these creation methods are also used for non NHibernate based tests).
After the object graph has been constructed we then flush the session, construct a new session and then load the same parent object view the new session. We then wrote a base method for our tests which compares the two objects to make sure they are identical.
Our comparison method uses reflection on objects which are being compared. We reflect over all properties and ensure they are all equal. When we reach a property which is not a simple type we recursively call our comparison method on that property to ensure all child properties of these objects are compared. To ensure there are no infinite loops we keep a collection of already verified objects to make sure we don't compare the same object twice.
The one catch here is that currently we ignore collections. Collections are far more difficult to test, especially because the majority of our collections are Sets. We can't simply check the number of positions in the collection and each position of the collection since every load of the object could result in a new order (Sets are not ordered after all).
During re-factoring we accidentally broke our comparison tests. While reading the below note that all collection properties of our business objects are interfaces which are later replaced with the appropriate collection implementation. The original implementer who checked to see if the property was a collection used the following code:
if (type.ToString().ToUpper().StartsWith("SYSTEM.COLLECTIONS")
|| type.ToString().ToUpper().StartsWith("IESI.COLLECTIONS"))
There are several things I don't like about that code. To be fair, the code actually worked for our implementations, but it is extremely dirty. There are several things wrong with this.
First, you shouldn't be checking for a type based on a name. Hard coding string based namespaces is unreliable and a bad idea. There is no way to get any sort of compile time checking from this.
Second, I'm typically opposed to ToUpper(), it's almost never what anyone wants, it's just been accepted from historical practices. People either do it because they don't want to think about what it should really be, or they do not know about using a CaseInsensitiveComparer.
Third, What if we chose to use a custom collection for one of our implementations? In that case it wouldn't actually have a name which matched either of those conditions.
So obviously I thought this needed to be re-factored. My attempt is shown below:
if (type is IEnumerable)
Seems really simple right? My thinking behind this was that every collection interface we use or have even known about has mandated that it also implement the IEnumerable interface. Take a look at all of your collection interfaces, no other interface is common between them. ICollection is not enforced by the generic interfaces. It may be enforced by the non-generic interfaces, but not the generic ones. As such I determined it wasn't safe to use ICollection.
Well, a co-worker of mine found out that I actually broke our mapping tests. Worse than that they were broken in a way where we would receive false positives. This simple change resulted in our mapping tests no longer testing strings. The System.String class is IEnumerable. This makes sense upon further inspection (enumerable list of characters). However, it was not something I first thought of.
So, the IEnumerable was bad, we have learned that. We could have checked for IEnumerable but not a String, but that seems dirty.
The sad thing here is that checking for ICollection actually works. Every single collection in the .NET 2.0+ framework and the Iesi.Collections package implements the ICollection interface, our problem with that is that it is not enforced by the interfaces we are using. Does anyone know why IList
My next attempt turned out to not actually work. I really was thinking this was the answer for us, but I didn't have my thinking cap on. My code can be found below. Note that the formatting is horrible, but needed to ensure that the blogger template does not cut off my code sample. Also note that obj in the example is the object which is being checked for a collection. Also note this is not a direct copy and paste, there are optimizations in our real code which are removed for simplicity here.
Type type = obj.GetType();
Type genericCollectionType = typeof(ICollection<>);
if (obj is ICollection
|| (type.IsGenericType
&& type.GetGenericArguments().Length
== genericCollectionType.GetGenericArguments().Length
&& genericCollectionType
.MakeGenericType(type.GetGenericArguments())
.IsAssignableFrom(type)
)
)
If you see here I check for ICollection first, and then if the item being tested does not match that interface I fall back on a generic collection test. Note that I compare the length of the generic arguments since attempting to MakeGenericType with the improper number of arguments would throw an exception.
This generic check works for collections like List<t>, Iesi.Collections.Generic.HashedSet<T> but it doesn't work for Dictionary<K,V>. Basically ICollection<T> has one generic type, but IDictionary<K,V> has two and therefore won't be tested. I would need to determine a way to see if it is assignable from ICollection<NameValuePair<K,V>> since that is how the Dictionary<> impelments ICollection<>.
I'm not sure the cleanest way to check for this. Plus, it won't actually be called since there doesn't exist a collection out there which doesn't implement ICollection.
So basically I'm jumping through a number of hoops which aren't totally needed, but I really wish the IList<> and IDictionary<> enforced ICollection<> then I would feel a whole lot more comfortable with this.
Does anyone know why they were left out? Is there a really good reason I am missing?
--John Chapman
Posted by
John Chapman
at
8:45 AM
0
comments
Labels: C#, Reflection, Testing
Thursday, December 6, 2007
MS Unit Exception Testing
How do people handle exception expectations within the Microsoft unit testing framework built in to Visual Studio Team Edition? Of course there is the built in ExpectedExceptionAttribute where you mark the test itself with an exception which should be thrown while performing the unit test. If no exception is thrown it fails, if an exception of that type is thrown it passes, if the exception is of the wrong type it fails.
I don't know about others, but this rarely sits well with me. Typically when I write a test method I want the ability to test other conditions when an exception is thrown, or I want to use a single test method to test all exceptional cases within a single business object method. Do most people just write one test per case of a method? It seems like this would result in a lot of duplication of unit tests.
To address this issue I have historically used the following code:
[TestMethod]
public void TestSomeMethod()
{
try
{
SomeMethod();
Assert.Fail("Expected ExpectedException to be thrown "
+ "while calling SomeMethod.");
}
catch (ExpectedException) { }
}
Recently it occurred to me, why do I have to go through this process every time I want to test for exceptions. At first I was hopeful that an additional mechanism would be made available with Visual Studio 2008 MSUnit, but it was not. I then decided to investigating the ability to extend the framework.
Honestly, I feel a little dirty saying this, but this appears to be a good use case for Extension Methods. Yes, I know my previous statements regarding extension methods (C# 3.0 Extension Methods? A Good Idea?), but I honestly think this would be a good use of it.
Imagine writing the following code instead:
[TestMethod]
public void TestSomeMethod()
{
Assert.ThrowsException<ExpectedException>
(delegate { SomeMethod(); });
}
Wow, I really would enjoy that functionality if it was available to me. Unfortunately for me, I can not make this an extension methods. Extension methods are only allowed on instances of objects. Since Assert is a static class you can not add extension methods. So instead of using an extension method we are instead left with using our own custom static class for testing.
See the implementation of this below:
public static class CustomAssert
{
public delegate void MethodDelegate();
public static void ThrowsException<T>
(MethodDelegate method)
where T: Exception
{
ThrowsException<T>(method,
"Expected Exception of type "
+ typeof(T).ToString()
+ " was not thrown.");
}
public static void ThrowsException<T>
(MethodDelegate method,
string message)
where T : Exception
{
try
{
method.DynamicInvoke();
Assert.Fail(message);
}
catch (T) { }
}
}
and now the final tests looks like:
In all honesty when I first decided this was something I was interested in I thought extension methods would work for this. While writing the code for this blog I came to the realization that they wouldn't.
[TestMethod]
public void TestMethod1()
{
CustomAssert.ThrowsException<ApplicationException>
(delegate { SomeMethod(); });
CustomAssert.ThrowsException<ApplicationException>
(delegate { SecondMethod(5); });
}
While researching how to make this approach work I did stumble across the xUnit.Net framework. It looks like the xUnit.Net framework already supports very similar functionality to what I am describing in this post. If you're interested with a unit testing framework with functionality like what I described maybe that is where you should look. If you want to stick to the existing Microsoft MS Unit framework, this may be a good approach to better handle your exception testing needs.
--John Chapman
Posted by
John Chapman
at
7:17 PM
1 comments
Labels: Testing, Visual Studio 2008
Tuesday, December 4, 2007
DateTime.Now Precision Issues... Enter StopWatch!
I'm a little bit embarrassed by this one. Previously I bogged about DateTime.Now Precision, or the lack there of. I first discovered this phenomenon while performing my performance tests for my blog post NHibernate Access Performance, which showed the differences between property and field level access within NHibernate.
Basically I found that DateTime.Now was not very precise and worked horribly as a benchmarking tool to test relative performance. Well, now I've come to my senses and realized that the nail I was banging on was really a screw, and using the hammer wasn't really the best tool for the job. I've now come to learn of the screwdriver which answers my benchmarking needs. It turns out the System.Diagnostics.StopWatch class exists for just the purpose I was trying to use DateTime.Now for. DateTime.Now is not supposed to be accurate enough to be used for diagnostics.
The StopWatch class is very simple. To use it you basically construct an instance of the object, Call Start(), perform your operations and then call Stop(). The StopWatch will contain a property ElapsedTicks with the most accurate elapsed time available.
While I wish I knew about this before when I originally recorded the benchmarks, I know about it now, and I always say I enjoy learning new things. With this new found knowledge of the StopWatch class I decided to re-run my NHibernate Access Performance tests to see how they looked. The results were dramatically different than I found with the DateTime mechanism.
First, I re-ran the 100,000 accesses test with two threads running synchronously. This is meant to replace my original test I ran. These results can be seen below. Note that all times are in milliseconds.
I then decided to run these same tests again but this time in a synchronous manner, meaning that only one test would be running at any given time. These results can be seen below:
In truth, these last numbers are probably the most accurate representation of the true performance implications of using the various NHibernate access strategies.
Of interest though is that fact that my earlier tests for property level access were showing values of close to 600 ms for the basic property setter where as a simple switch to the StopWatch showed just 62 ms. There was a difference of a full order of magnitude? I was actually surprised by just how much these numbers varied. It turns out that the accuracy of DateTime.Now is even worse than I thought.
--John Chapman
Posted by
John Chapman
at
8:01 PM
96
comments
Labels: Benchmarking, NHibernate