PageRenderTime 426ms CodeModel.GetById 162ms app.highlight 61ms RepoModel.GetById 160ms app.codeStats 0ms

/Rhino.Etl.Core/Infrastructure/SqlCommandSet.cs

http://github.com/ayende/rhino-etl
C# | 200 lines | 110 code | 18 blank | 72 comment | 6 complexity | 9f676ef3ffc2aec4d61952f5f0385537 MD5 | raw file
  1#region license
  2// Copyright (c) 2005 - 2007 Ayende Rahien (ayende@ayende.com)
  3// All rights reserved.
  4// 
  5// Redistribution and use in source and binary forms, with or without modification,
  6// are permitted provided that the following conditions are met:
  7// 
  8//     * Redistributions of source code must retain the above copyright notice,
  9//     this list of conditions and the following disclaimer.
 10//     * Redistributions in binary form must reproduce the above copyright notice,
 11//     this list of conditions and the following disclaimer in the documentation
 12//     and/or other materials provided with the distribution.
 13//     * Neither the name of Ayende Rahien nor the names of its
 14//     contributors may be used to endorse or promote products derived from this
 15//     software without specific prior written permission.
 16// 
 17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 18// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 19// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 20// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
 21// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 22// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 23// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 24// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 25// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 26// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 27#endregion
 28
 29
 30using System;
 31using System.Data.SqlClient;
 32using System.Diagnostics;
 33using System.Reflection;
 34
 35namespace Rhino.Etl.Core.Infrastructure
 36{
 37    /// <summary>
 38    /// Expose the batch functionality in ADO.Net 2.0
 39    /// Microsoft in its wisdom decided to make my life hard and mark it internal.
 40    /// Through the use of Reflection and some delegates magic, I opened up the functionality.
 41    /// 
 42    /// There is NO documentation for this, and likely zero support.
 43    /// Use at your own risk, etc...
 44    /// 
 45    /// Observable performance benefits are 50%+ when used, so it is really worth it.
 46    /// </summary>
 47    public class SqlCommandSet : IDisposable
 48    {
 49        private static readonly Type sqlCmdSetType;
 50        private readonly object instance;
 51        private readonly PropSetter<SqlConnection> connectionSetter;
 52        private readonly PropSetter<SqlTransaction> transactionSetter;
 53        private readonly PropSetter<int> timeoutSetter;
 54        private readonly PropGetter<SqlConnection> connectionGetter;
 55        private readonly PropGetter<SqlCommand> commandGetter;
 56        private readonly AppendCommand doAppend;
 57        private readonly ExecuteNonQueryCommand doExecuteNonQuery;
 58        private readonly DisposeCommand doDispose;
 59        private int countOfCommands = 0;
 60
 61        static SqlCommandSet()
 62        {
 63            Assembly sysData = Assembly.Load("System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
 64            sqlCmdSetType = sysData.GetType("System.Data.SqlClient.SqlCommandSet");
 65            Guard.Against(sqlCmdSetType == null, "Could not find SqlCommandSet!");
 66        }
 67
 68        /// <summary>
 69        /// Creates a new instance of SqlCommandSet
 70        /// </summary>
 71        public SqlCommandSet()
 72        {
 73
 74            instance = Activator.CreateInstance(sqlCmdSetType, true);
 75
 76            timeoutSetter = (PropSetter<int>)
 77                               Delegate.CreateDelegate(typeof(PropSetter<int>),
 78                                                       instance, "set_CommandTimeout");
 79            connectionSetter = (PropSetter<SqlConnection>)
 80                               Delegate.CreateDelegate(typeof(PropSetter<SqlConnection>),
 81                                                       instance, "set_Connection");
 82            transactionSetter = (PropSetter<SqlTransaction>)
 83                                Delegate.CreateDelegate(typeof(PropSetter<SqlTransaction>),
 84                                                        instance, "set_Transaction");
 85            connectionGetter = (PropGetter<SqlConnection>)
 86                               Delegate.CreateDelegate(typeof(PropGetter<SqlConnection>),
 87                                                       instance, "get_Connection");
 88            commandGetter =
 89                (PropGetter<SqlCommand>)Delegate.CreateDelegate(typeof(PropGetter<SqlCommand>), instance, "get_BatchCommand");
 90            doAppend = (AppendCommand)Delegate.CreateDelegate(typeof(AppendCommand), instance, "Append");
 91            doExecuteNonQuery = (ExecuteNonQueryCommand)
 92                                Delegate.CreateDelegate(typeof(ExecuteNonQueryCommand),
 93                                                        instance, "ExecuteNonQuery");
 94            doDispose = (DisposeCommand)Delegate.CreateDelegate(typeof(DisposeCommand), instance, "Dispose");
 95
 96        }
 97
 98        /// <summary>
 99        /// Append a command to the batch
100        /// </summary>
101        /// <param name="command"></param>
102        public void Append(SqlCommand command)
103        {
104            AssertHasParameters(command);
105            doAppend(command);
106            countOfCommands++;
107        }
108
109        /// <summary>
110        /// This is required because SqlClient.SqlCommandSet will throw if 
111        /// the command has no parameters.
112        /// </summary>
113        /// <param name="command"></param>
114        private static void AssertHasParameters(SqlCommand command)
115        {
116            if (command.Parameters.Count == 0 &&
117                (RuntimeInfo.Version.Contains("2.0") || RuntimeInfo.Version.Contains("1.1")))
118            {
119                throw new ArgumentException(
120                    "A command in SqlCommandSet must have parameters. You can't pass hardcoded sql strings.");
121            }
122        }
123        
124        /// <summary>
125        /// Return the batch command to be executed
126        /// </summary>
127        public SqlCommand BatchCommand
128        {
129            get
130            {
131                return commandGetter();
132            }
133        }
134        
135        /// <summary>
136        /// The number of commands batched in this instance
137        /// </summary>
138        public int CountOfCommands
139        {
140            get { return countOfCommands; }
141        }
142
143        /// <summary>
144        /// Executes the batch
145        /// </summary>
146        /// <returns>
147        /// This seems to be returning the total number of affected rows in all queries
148        /// </returns>
149        public int ExecuteNonQuery()
150        {
151            Guard.Against<ArgumentException>(Connection == null,
152                                             "Connection was not set! You must set the connection property before calling ExecuteNonQuery()");
153            if(CountOfCommands==0)
154                return 0;
155            return doExecuteNonQuery();
156        }
157
158        ///<summary>
159        /// The connection the batch will use
160        ///</summary>
161        public SqlConnection Connection
162        {
163            get { return connectionGetter(); }
164            set { connectionSetter(value); }
165        }
166
167        /// <summary>
168        /// Set the timeout of the commandSet
169        /// </summary>
170        public int CommandTimeout
171        {
172            set { timeoutSetter(value); }
173        }
174
175        /// <summary>
176        /// The transaction the batch will run as part of
177        /// </summary>
178        public SqlTransaction Transaction
179        {
180            set { transactionSetter(value); }
181        }
182
183        ///<summary>
184        ///Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
185        ///</summary>
186        ///<filterpriority>2</filterpriority>
187        public void Dispose()
188        {
189            doDispose();
190        }
191
192        #region Delegate Definations
193        private delegate void PropSetter<T>(T item);
194        private delegate T PropGetter<T>();
195        private delegate void AppendCommand(SqlCommand command);
196        private delegate int ExecuteNonQueryCommand();
197        private delegate void DisposeCommand();
198        #endregion
199    }
200}