Don?t you just hate it when a class in the .NET Framework or another third-party library or framework provides that particular feature you?re looking for only to realize that this class has not been made accessible? One blatant example of this is the SqlCommandSet class in the System.Data.SqlClient namespace.
One can use reflection in order to access and use this private class. An even better and more efficient approach would be to use delegates instead. But there?s also another simple and elegant solution which I briefly mentioned in a previous post namely duck typing.
Now in order to access the SqlCommandSet class we need to define an interface with all of the methods we want to access from our code. Reflector can come in handy in this case to determine which particular methods we need.
public interface ISqlCommandSet : IDisposable { SqlConnection Connection { get; set; } void Append(SqlCommand command); Int32 ExecuteNonQuery(); }
This is pretty much what we need from a SqlCommandSet. Now we can create a factory class for creating instances of the SqlCommandSet class.
public class SqlCommandSetFactory { private readonly Type _typeOfSqlCommandSet; public SqlCommandSetFactory() { var system_data = Assembly.Load("System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); _typeOfSqlCommandSet = system_data.GetType("System.Data.SqlClient.SqlCommandSet"); } public ISqlCommandSet CreateSqlCommandSet() { var instance = Activator.CreateInstance(_typeOfSqlCommandSet, true); var dynamicObject = new DynamicObject(instance); return dynamicObject.CreateDuck<ISqlCommandSet>(); } }
The System.Data assembly is loaded and the type of the SqlCommandSet is determined in the constructor of the factory class. In the factory method itself we first create an instance of the SqlCommandSet class and then use LinFu?s DynamicObject to ?cast? it to our ISqlCommandSet interface. And this is pretty much it. Now we can use a SqlCommandSet at will.
using(var connection = new SqlConnection(@"...")) { connection.Open(); using(var command1 = new SqlCommand("INSERT INTO BLABLA ...")) using(var command2 = new SqlCommand("INSERT INTO BLA BLA ...")) { var commandSet = new SqlCommandSetFactory().CreateSqlCommandSet(); commandSet.Connection = connection; commandSet.Append(command1); commandSet.Append(command2); commandSet.ExecuteNonQuery(); } }
Again I?d like to mention that I?ve been using the DynamicObject class from Linfu 1.0 which you can download here.
So what happens if this class is removed in future releases? If it is not a part of the public API, and you use it all over your codebase, you could be in a world of hurt when it’s time to upgrade.
I know, it’s not really the point of the article (which _is_ very interesting), but I can’t see doing this in my code – even if it’s a neat trick.
@David Ackerman
Why wouldn’t you use it? It can really improve the performance of your application. Even NHibernate uses it if I can remember correctly. Keeping the use of this class in a single place instead of all over the application mitigates the risks (something that I encourage for public classes as well). Instead of deprecating it, they should make it public.
Thanks Jon, a nice way of accessing a closed type. Thanks also for the introducing me to the SqlCommandSet class.
Do you know if dynamic typing in .Net 4 could produce the same result as the LinFu’s DynamicObject?
Appologies *Jan* for the typo in my comment.@Keith Bloom
@Keith Bloom I haven’t looked at dynamic typing in .NET 4.0, but I will do that in the next couple of weeks. I’m planning to do another post with my findings.
Well, here we are. Yes, using private types may make sense *at times*. With that disclaimer, I’ve a post on using C# dynamic features to create, instantiate and use private types, and to access private members.
http://amazedsaint.blogspot.com/2010/05/accessprivatewrapper-c-40-dynamic.html