Adding IDbCommand Parameters with Anonymous Types
To take my previous post to the next level, I decided to try a different approach to adding parameters to an IDbCommand by using anonymous types. This is inspired by the way you set up routes in ASP.NET MVC, and example of which you can see here on ScottGu's blog. The idea is that you simply pass in an anonymous type, whose properties match the parameter names you want to define.
First, here's the extension method:
internal static void AddInputParameters<T>(this IDbCommand cmd, T parameters) where T : class { foreach (var prop in parameters.GetType().GetProperties()) { object val = prop.GetValue(parameters, null); var p = cmd.CreateParameter(); p.ParameterName = prop.Name; p.Value = val ?? DBNull.Value; cmd.Parameters.Add(p); } }
So I'm iterating through the properties of "parameters", and for each one I'm adding an input parameter based on its value. I'm supporting null by simply adding a parameter with DBNull.Value.
Note that this has a slightly different behaviour with strings than my previous attempt, in that it will not treat an empty string as DBNull, but I think this is probably preferable.
Here's how you'd use it:
cmd.AddInputParameters(new
{
Date = eventDate,
Operator = oper,
Weight = weight
});
Pretty succinct! Of course it's no more compile-checked than literal strings (except for the fact that it only allows valid identifiers as parameter names), but it's much more readable than a small chunk of code for each parameter.
# Trackback from Named Parameters in C# 4.0 on 16/11/2008 5:04 PM
Comments
# Jason Bock
8/07/2008 4:44 AM
I love stuff like this :). One suggestion to take it even further would be to make a DynamicMethod based on the props you find, cache that method, and invoke it instead of getting that little Reflection hit every time. It's a minor tweak, but IMHO it's a cool tweak :)
# mabster
8/07/2008 8:24 AM
That sounds similar to what compiled regular expressions do - is that right Jason?
I'd love to see the code. I don't think it would buy much in this instance (it's not something you do over and over again in a loop or anything) but I'd still be interested in learning how it's done.
# Jason Bock
8/07/2008 1:28 PM
Here's an example I did to make a generic ToString() implementation:
www.jasonbock.net/.../Default.aspx
With your stuff, you may actually get some perf improvements because you'll figure out the types of the properties and it'll all be strongly-typed in the DynamicMethod, and if that command is hit a lot, once you've cached it, you get all the benefits.
# mabster
8/07/2008 1:36 PM
Wow, that's pretty hardcore, Jason! I haven't used any of the Emit stuff to generate IL at runtime. Fascinating! Thanks for the link.
# Jason Bock
10/07/2008 6:46 AM
I got it working with a DynamicMethod approach - check it out:
www.jasonbock.net/.../Default.aspx
# Eric
12/07/2008 4:12 AM
Interesting... but what penalty (if any?) comes from 'littering' your managed assembly with all those anonymous types? My understanding is that a new one is created for each call with a unique signature?
For example, the following 4 calls would create 3 anonymous types.
AddInputParameters(new { Name = "Eric", Age = 31 });
AddInputParameters(new { Name = "Joe", Age = 25 });
AddInputParameters(new { Name = "Joe", Age = 25, FavoriteColor = "red" });
AddInputParameters(new { FavoriteColor = "red" });
This definitely isn't scientific, but I commented out the last 3 calls, re-compiled and checked the size of the generated assembly and it went down by roughly 1000 bytes.
Maybe I'm making something out of nothing, but I would think this would add some sort of overhead (both in memory footprint and time required to JIT compile each class) to a considerable project that did a lot of data access?
Maybe Jason could offer his two cents?
Also, could the following line:
p.Value = val == null ? DBNull.Value : val;
be shortened to:
p.Value = val ?? DBNull.Value;
The null coalescing operator www.yoda.arachsys.com/.../nullable.html
# mabster
12/07/2008 9:51 AM
I think you're right about the null-coalescing operator, eric. I'll update the code sample once I've had a chance to make sure it still works! ;)