diff options
author | Sureshkumar T <suresh@mono-cvs.ximian.com> | 2005-04-07 18:25:50 +0400 |
---|---|---|
committer | Sureshkumar T <suresh@mono-cvs.ximian.com> | 2005-04-07 18:25:50 +0400 |
commit | f943410d7265cb08c076852614fe80db63186c77 (patch) | |
tree | 6d7c3b7f18fb68ec0d7b5016ab762d55259a3090 /mcs/class/System.Data/System.Data.SqlClient | |
parent | 2e3eb053ab9baa399d57a9b8c6988d732ab1335d (diff) |
In System.Data/System.Data.SqlClient: Implemented asynchronous command execution.
In Mono.Data.Tds: Implemented asynchronous command execution at tds driver level.
Implemented by Sureshkumar T <tsureshkumar@novell.com> & Ankit Jain <radical@corewars.org>
svn path=/trunk/mcs/; revision=42641
Diffstat (limited to 'mcs/class/System.Data/System.Data.SqlClient')
5 files changed, 454 insertions, 0 deletions
diff --git a/mcs/class/System.Data/System.Data.SqlClient/ChangeLog b/mcs/class/System.Data/System.Data.SqlClient/ChangeLog index bfd627d25ab..11a76042605 100755 --- a/mcs/class/System.Data/System.Data.SqlClient/ChangeLog +++ b/mcs/class/System.Data/System.Data.SqlClient/ChangeLog @@ -1,3 +1,17 @@ +2005-04-07 Sureshkumar T <tsureshkumar@novell.com> + Ankit Jain <radical@corewars.org> + + * SqlConnection.cs: Implemented additional connection string + property "Asynchronous Processing". + + * SqlCommand.cs: Implemented Asynchronous command execution API. + + * SqlAsyncState.cs: A internal state object for asynchronous + operations. + + * SqlAsyncResult.cs: Added. Class to hold result for asynchronous + queries. + 2005-03-28 Sureshkumar T <tsureshkumar@novell.com> * SqlCommand.cs: Execute: Add a semicolon at the end of diff --git a/mcs/class/System.Data/System.Data.SqlClient/SqlAsyncResult.cs b/mcs/class/System.Data/System.Data.SqlClient/SqlAsyncResult.cs new file mode 100644 index 00000000000..ff85db23bac --- /dev/null +++ b/mcs/class/System.Data/System.Data.SqlClient/SqlAsyncResult.cs @@ -0,0 +1,139 @@ +// +// System.Data.SqlClient.SqlAsyncResult.cs +// +// Author: +// T Sureshkumar <tsureshkumar@novell.com> +// Ankit Jain <radical@corewars.org> +// + +// +// Copyright (C) 2004 Novell, Inc (http://www.novell.com) +// +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + + +#if NET_2_0 +using System; +using System.Threading; + +namespace System.Data.SqlClient +{ + internal class SqlAsyncResult : IAsyncResult + { + private SqlAsyncState _sqlState; + private WaitHandle _waitHandle; + private bool _completed = false; + private bool _completedSyncly = false; + private bool _ended = false; + private AsyncCallback _userCallback = null; + private object _retValue; + private string _endMethod; + private IAsyncResult _internal; + + public SqlAsyncResult (AsyncCallback userCallback, SqlAsyncState sqlState) + { + _sqlState = sqlState; + _userCallback = userCallback; + _waitHandle = new ManualResetEvent (false); + } + + public SqlAsyncResult (AsyncCallback userCallback, object state) + { + _sqlState = new SqlAsyncState (state); + _userCallback = userCallback; + _waitHandle = new ManualResetEvent (false); + } + + public object AsyncState + { + get { return _sqlState.UserState; } + } + + internal SqlAsyncState SqlAsyncState + { + get { return _sqlState; } + } + + public WaitHandle AsyncWaitHandle + { + get { return _waitHandle; } + + } + + public bool IsCompleted + { + get { return _completed; } + } + + public bool CompletedSynchronously + { + get { return _completedSyncly; } + } + + internal object ReturnValue + { + get { return _retValue; } + set { _retValue = value; } + } + + public string EndMethod + { + get { return _endMethod; } + set { _endMethod = value; } + } + + public bool Ended + { + get { return _ended; } + set { _ended = value; } + } + + + internal IAsyncResult InternalResult + { + get { return _internal; } + set { _internal = value; } + } + + public AsyncCallback BubbleCallback + { + get { return new AsyncCallback (Bubbleback); } + + } + + internal void MarkComplete () + { + _completed = true; + ((ManualResetEvent)_waitHandle).Set (); + + if (_userCallback != null) + _userCallback (this); + } + + public void Bubbleback (IAsyncResult ar) + { + this.MarkComplete (); + } + } +} + +#endif // NET_2_0 diff --git a/mcs/class/System.Data/System.Data.SqlClient/SqlAsyncState.cs b/mcs/class/System.Data/System.Data.SqlClient/SqlAsyncState.cs new file mode 100644 index 00000000000..7f733d79877 --- /dev/null +++ b/mcs/class/System.Data/System.Data.SqlClient/SqlAsyncState.cs @@ -0,0 +1,56 @@ +// +// System.Data.SqlClient.SqlAsyncState.cs +// +// Author: +// T Sureshkumar <tsureshkumar@novell.com> +// Ankit Jain <radical@corewars.org> +// + +// +// Copyright (C) 2004 Novell, Inc (http://www.novell.com) +// +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +#if NET_2_0 +using System; +using System.Net.Sockets; + + +namespace System.Data.SqlClient +{ + internal class SqlAsyncState + { + object _userState; + + public SqlAsyncState (object userState) + { + _userState = userState; + } + + public object UserState + { + get {return _userState;} + set {_userState = value;} + } + } +} +#endif // NET_2_0 diff --git a/mcs/class/System.Data/System.Data.SqlClient/SqlCommand.cs b/mcs/class/System.Data/System.Data.SqlClient/SqlCommand.cs index 0e632576e4f..50bdeb99868 100644 --- a/mcs/class/System.Data/System.Data.SqlClient/SqlCommand.cs +++ b/mcs/class/System.Data/System.Data.SqlClient/SqlCommand.cs @@ -542,6 +542,12 @@ namespace System.Data.SqlClient { throw new InvalidOperationException ("There is already an open DataReader associated with this Connection which must be closed first."); if (Connection.XmlReader != null) throw new InvalidOperationException ("There is already an open XmlReader associated with this Connection which must be closed first."); +#if NET_2_0 + if (method.StartsWith ("Begin") && !Connection.AsyncProcessing) + throw new InvalidOperationException ("This Connection object is not " + + "in Asynchronous mode. Use 'Asynchronous" + + " Processing = true' to set it."); +#endif // NET_2_0 } #if NET_2_0 @@ -579,5 +585,215 @@ namespace System.Data.SqlClient { #endif // NET_2_0 #endregion // Methods + +#if NET_2_0 + #region Asynchronous Methods + + internal IAsyncResult BeginExecuteInternal (CommandBehavior behavior, + bool wantResults, + AsyncCallback callback, + object state) + { + IAsyncResult ar = null; + Connection.Tds.RecordsAffected = 0; + TdsMetaParameterCollection parms = Parameters.MetaParameters; + if (preparedStatement == null) { + bool schemaOnly = ((behavior & CommandBehavior.SchemaOnly) > 0); + bool keyInfo = ((behavior & CommandBehavior.KeyInfo) > 0); + + StringBuilder sql1 = new StringBuilder (); + StringBuilder sql2 = new StringBuilder (); + + if (schemaOnly || keyInfo) + sql1.Append ("SET FMTONLY OFF;"); + if (keyInfo) { + sql1.Append ("SET NO_BROWSETABLE ON;"); + sql2.Append ("SET NO_BROWSETABLE OFF;"); + } + if (schemaOnly) { + sql1.Append ("SET FMTONLY ON;"); + sql2.Append ("SET FMTONLY OFF;"); + } + + switch (CommandType) { + case CommandType.StoredProcedure: + string prolog = ""; + string epilog = ""; + if (keyInfo || schemaOnly) + prolog = sql1.ToString (); + if (keyInfo || schemaOnly) + epilog = sql2.ToString (); + Connection.Tds.BeginExecuteProcedure (prolog, + epilog, + CommandText, + !wantResults, + parms, + callback, + state); + + break; + case CommandType.Text: + string sql = String.Format ("{0}{1};{2}", sql1.ToString (), CommandText, sql2.ToString ()); + if (wantResults) + ar = Connection.Tds.BeginExecuteQuery (sql, parms, + callback, state); + else + ar = Connection.Tds.BeginExecuteNonQuery (sql, parms, callback, state); + break; + } + } + else + Connection.Tds.ExecPrepared (preparedStatement, parms, CommandTimeout, wantResults); + + return ar; + + } + + internal void EndExecuteInternal (IAsyncResult ar) + { + SqlAsyncResult sqlResult = ( (SqlAsyncResult) ar); + Connection.Tds.WaitFor (sqlResult.InternalResult); + Connection.Tds.CheckAndThrowException (sqlResult.InternalResult); + } + + public IAsyncResult BeginExecuteNonQuery () + { + return BeginExecuteNonQuery (null, null); + } + + public IAsyncResult BeginExecuteNonQuery (AsyncCallback callback, object state) + { + ValidateCommand ("BeginExecuteNonQuery"); + SqlAsyncResult ar = new SqlAsyncResult (callback, state); + ar.EndMethod = "EndExecuteNonQuery"; + ar.InternalResult = BeginExecuteInternal (CommandBehavior.Default, false, ar.BubbleCallback, ar); + return ar; + } + + public int EndExecuteNonQuery (IAsyncResult ar) + { + ValidateAsyncResult (ar, "EndExecuteNonQuery"); + EndExecuteInternal (ar); + + int ret; + if (commandType == CommandType.StoredProcedure) + ret = -1; + else { + // .NET documentation says that except for INSERT, UPDATE and + // DELETE where the return value is the number of rows affected + // for the rest of the commands the return value is -1. + if ((CommandText.ToUpper().IndexOf("UPDATE")!=-1) || + (CommandText.ToUpper().IndexOf("INSERT")!=-1) || + (CommandText.ToUpper().IndexOf("DELETE")!=-1)) + ret = Connection.Tds.RecordsAffected; + else + ret = -1; + } + GetOutputParameters (); + ( (SqlAsyncResult) ar).Ended = true; + return ret; + } + + public IAsyncResult BeginExecuteReader () + { + return BeginExecuteReader (null, null, CommandBehavior.Default); + } + + public IAsyncResult BeginExecuteReader (CommandBehavior behavior) + { + return BeginExecuteReader (null, null, behavior); + } + + public IAsyncResult BeginExecuteReader (AsyncCallback callback, object state) + { + return BeginExecuteReader (callback, state, CommandBehavior.Default); + } + + public IAsyncResult BeginExecuteReader (AsyncCallback callback, object state, CommandBehavior behavior) + { + ValidateCommand ("BeginExecuteReader"); + this.behavior = behavior; + SqlAsyncResult ar = new SqlAsyncResult (callback, state); + ar.EndMethod = "EndExecuteReader"; + IAsyncResult tdsResult = BeginExecuteInternal (behavior, true, + ar.BubbleCallback, state); + ar.InternalResult = tdsResult; + return ar; + } + + public SqlDataReader EndExecuteReader (IAsyncResult ar) + { + ValidateAsyncResult (ar, "EndExecuteReader"); + EndExecuteInternal (ar); + SqlDataReader reader = null; + try { + reader = new SqlDataReader (this); + } + catch (TdsTimeoutException e) { + // if behavior is closeconnection, even if it throws exception + // the connection has to be closed. + if ((behavior & CommandBehavior.CloseConnection) != 0) + Connection.Close (); + throw SqlException.FromTdsInternalException ((TdsInternalException) e); + } catch (SqlException) { + // if behavior is closeconnection, even if it throws exception + // the connection has to be closed. + if ((behavior & CommandBehavior.CloseConnection) != 0) + Connection.Close (); + + throw; + } + + ( (SqlAsyncResult) ar).Ended = true; + return reader; + } + + public IAsyncResult BeginExecuteXmlReader (AsyncCallback callback, object state) + { + ValidateCommand ("BeginExecuteXmlReader"); + SqlAsyncResult ar = new SqlAsyncResult (callback, state); + ar.EndMethod = "EndExecuteXmlReader"; + ar.InternalResult = BeginExecuteInternal (behavior, true, + ar.BubbleCallback, state); + return ar; + } + + public XmlReader EndExecuteXmlReader (IAsyncResult ar) + { + ValidateAsyncResult (ar, "EndExecuteXmlReader"); + EndExecuteInternal (ar); + SqlDataReader reader = new SqlDataReader (this); + SqlXmlTextReader textReader = new SqlXmlTextReader (reader); + XmlReader xmlReader = new XmlTextReader (textReader); + ( (SqlAsyncResult) ar).Ended = true; + return xmlReader; + } + + + internal void ValidateAsyncResult (IAsyncResult ar, string endMethod) + { + if (ar == null) + throw new ArgumentException ("result passed is null!"); + if (! (ar is SqlAsyncResult)) + throw new ArgumentException (String.Format ("cannot test validity of types {0}", + ar.GetType () + )); + SqlAsyncResult result = (SqlAsyncResult) ar; + + if (result.EndMethod != endMethod) + throw new InvalidOperationException (String.Format ("Mismatched {0} called for AsyncResult. " + + "Expected call to {1} but {0} is called instead.", + endMethod, + result.EndMethod + )); + if (result.Ended) + throw new InvalidOperationException (String.Format ("The method {0} cannot be called " + + "more than once for the same AsyncResult.", + endMethod)); + + } + + #endregion // Asynchronous Methods +#endif // NET_2_0 } } diff --git a/mcs/class/System.Data/System.Data.SqlClient/SqlConnection.cs b/mcs/class/System.Data/System.Data.SqlClient/SqlConnection.cs index e8e223d8e4f..e603d6671e4 100644 --- a/mcs/class/System.Data/System.Data.SqlClient/SqlConnection.cs +++ b/mcs/class/System.Data/System.Data.SqlClient/SqlConnection.cs @@ -719,6 +719,11 @@ namespace System.Data.SqlClient { parameters["POOLING"] = "true"; if (null == parameters.Get ("WORKSTATION ID") && null == parameters.Get ("WSID")) parameters["WORKSTATION ID"] = Dns.GetHostName(); +#if NET_2_0 + if (null == parameters.Get ("ASYNC") && + null == parameters.Get ("ASYNCHRONOUS PROCESSING")) + parameters ["ASYNCHRONOUS PROCESSING"] = "false"; +#endif } private void SetProperties (NameValueCollection parameters) @@ -789,6 +794,10 @@ namespace System.Data.SqlClient { #if NET_2_0 case "MULTIPLEACTIVERESULTSETS": break; + case "ASYNCHRONOUS PROCESSING" : + case "ASYNC" : + async = ConvertToBoolean (name, value); + break; #endif case "NET" : case "NETWORK" : @@ -918,5 +927,25 @@ namespace System.Data.SqlClient { } #endregion // Methods + +#if NET_2_0 + #region Fields Net 2 + + bool async = false; + + #endregion // Fields Net 2 + + #region Properties Net 2 + + [DataSysDescription ("Enable Asynchronous processing, 'Asynchrouse Processing=true/false' in the ConnectionString.")] + [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] + internal bool AsyncProcessing { + get { return async; } + } + + #endregion // Properties Net 2 + +#endif // NET_2_0 + } } |