1 //------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //------------------------------------------------------------
4 namespace System.ServiceModel.Persistence
5 {
6     using System;
7     using System.Collections.Generic;
8     using System.Collections.Specialized;
9     using System.Configuration;
10     using System.Data;
11     using System.Data.SqlClient;
12     using System.Diagnostics;
13     using System.Diagnostics.CodeAnalysis;
14     using System.Globalization;
15     using System.IO;
16     using System.Runtime;
17     using System.Runtime.Diagnostics;
18     using System.Runtime.Serialization;
19     using System.ServiceModel.Diagnostics;
20     using System.Text;
21     using System.Xml;
22 
23     [Obsolete("The WF3 types are deprecated.  Instead, please use the new WF4 types from System.Activities.*")]
24     public class SqlPersistenceProviderFactory : PersistenceProviderFactory
25     {
26         static readonly TimeSpan maxSecondsTimeSpan = TimeSpan.FromSeconds(int.MaxValue);
27         const string connectionStringNameParameter = "connectionStringName";
28         const string lockTimeoutParameter = "lockTimeout";
29         const string serializeAsTextParameter = "serializeAsText";
30         List<SqlCommand> activeCommands;
31         string canonicalConnectionString;
32 
33         string connectionString;
34         CreateHandler createHandler;
35         DeleteHandler deleteHandler;
36         Guid hostId;
37         LoadHandler loadHandler;
38         TimeSpan lockTimeout;
39         bool serializeAsText;
40         UnlockHandler unlockHandler;
41         UpdateHandler updateHandler;
42 
SqlPersistenceProviderFactory(string connectionString)43         public SqlPersistenceProviderFactory(string connectionString)
44             : this(connectionString, false, TimeSpan.Zero)
45         {
46         }
47 
SqlPersistenceProviderFactory(string connectionString, bool serializeAsText)48         public SqlPersistenceProviderFactory(string connectionString, bool serializeAsText)
49             : this(connectionString, serializeAsText, TimeSpan.Zero)
50         {
51         }
52 
SqlPersistenceProviderFactory(string connectionString, bool serializeAsText, TimeSpan lockTimeout)53         public SqlPersistenceProviderFactory(string connectionString, bool serializeAsText, TimeSpan lockTimeout)
54         {
55             this.ConnectionString = connectionString;
56             this.LockTimeout = lockTimeout;
57             this.SerializeAsText = serializeAsText;
58             this.loadHandler = new LoadHandler(this);
59             this.createHandler = new CreateHandler(this);
60             this.updateHandler = new UpdateHandler(this);
61             this.unlockHandler = new UnlockHandler(this);
62             this.deleteHandler = new DeleteHandler(this);
63         }
64 
SqlPersistenceProviderFactory(NameValueCollection parameters)65         public SqlPersistenceProviderFactory(NameValueCollection parameters)
66         {
67             if (parameters == null)
68             {
69                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("parameters");
70             }
71 
72             this.connectionString = null;
73             this.LockTimeout = TimeSpan.Zero;
74             this.SerializeAsText = false;
75 
76             foreach (string key in parameters.Keys)
77             {
78                 switch (key)
79                 {
80                     case connectionStringNameParameter:
81                         ConnectionStringSettings settings = ConfigurationManager.ConnectionStrings[parameters[key]];
82 
83                         if (settings == null)
84                         {
85                             throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(
86                                 SR2.GetString(SR2.ConnectionStringNameIncorrect, parameters[key]));
87                         }
88 
89                         this.connectionString = settings.ConnectionString;
90                         break;
91                     case serializeAsTextParameter:
92                         this.SerializeAsText = bool.Parse(parameters[key]);
93                         break;
94                     case lockTimeoutParameter:
95                         this.LockTimeout = TimeSpan.Parse(parameters[key], CultureInfo.InvariantCulture);
96                         break;
97                     default:
98                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(
99                             key,
100                             SR2.GetString(SR2.UnknownSqlPersistenceConfigurationParameter, key, connectionStringNameParameter, serializeAsTextParameter, lockTimeoutParameter));
101                 }
102             }
103 
104             if (this.connectionString == null)
105             {
106                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(
107                     SR2.GetString(SR2.ConnectionStringNameParameterRequired, connectionStringNameParameter));
108             }
109 
110             this.loadHandler = new LoadHandler(this);
111             this.createHandler = new CreateHandler(this);
112             this.updateHandler = new UpdateHandler(this);
113             this.unlockHandler = new UnlockHandler(this);
114             this.deleteHandler = new DeleteHandler(this);
115         }
116 
117         public string ConnectionString
118         {
119             get
120             {
121                 return this.connectionString;
122             }
123             set
124             {
125                 if (string.IsNullOrEmpty(value))
126                 {
127                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("value");
128                 }
129                 this.connectionString = value;
130             }
131         }
132 
133         public TimeSpan LockTimeout
134         {
135             get
136             {
137                 return this.lockTimeout;
138             }
139             set
140             {
141                 // Allowed values are TimeSpan.Zero (no locking), TimeSpan.MaxValue (infinite locking),
142                 // and any values between 1 and int.MaxValue seconds
143                 if (value < TimeSpan.Zero ||
144                     (value > TimeSpan.FromSeconds(int.MaxValue) && value != TimeSpan.MaxValue))
145                 {
146                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
147                         new ArgumentOutOfRangeException(
148                         "value",
149                         SR2.GetString(SR2.LockTimeoutOutOfRange)));
150                 }
151                 this.lockTimeout = value;
152             }
153         }
154 
155         public bool SerializeAsText
156         {
157             get
158             {
159                 return this.serializeAsText;
160             }
161             set
162             {
163                 this.serializeAsText = value;
164             }
165         }
166 
167         protected override TimeSpan DefaultCloseTimeout
168         {
169             get { return PersistenceProvider.DefaultOpenClosePersistenceTimout; }
170         }
171 
172         protected override TimeSpan DefaultOpenTimeout
173         {
174             get { return PersistenceProvider.DefaultOpenClosePersistenceTimout; }
175         }
176 
177         bool IsLockingTurnedOn
178         {
179             get { return this.lockTimeout != TimeSpan.Zero; }
180         }
181 
182         int LockTimeoutAsInt
183         {
184             get
185             {
186                 // Consider storing lockTimeout as int32 TotalSeconds instead
187                 if (this.lockTimeout == TimeSpan.Zero)
188                 {
189                     return -1;
190                 }
191                 else if (this.lockTimeout == TimeSpan.MaxValue)
192                 {
193                     return 0;
194                 }
195                 else
196                 {
197                     Fx.Assert(this.lockTimeout <= TimeSpan.FromSeconds(int.MaxValue),
198                         "The lockTimeout should have been checked in the constructor.");
199 
200                     return Convert.ToInt32(this.lockTimeout.TotalSeconds);
201                 }
202             }
203         }
204 
CreateProvider(Guid id)205         public override PersistenceProvider CreateProvider(Guid id)
206         {
207             base.ThrowIfDisposedOrNotOpen();
208 
209             if (Guid.Empty == id)
210             {
211                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("id", SR2.GetString(SR2.SqlPersistenceProviderRequiresNonEmptyGuid));
212             }
213 
214             return new SqlPersistenceProvider(id, this);
215         }
216 
OnAbort()217         protected override void OnAbort()
218         {
219             if (this.activeCommands != null)
220             {
221                 lock (this.activeCommands)
222                 {
223                     foreach (SqlCommand command in this.activeCommands)
224                     {
225                         command.Cancel();
226                     }
227                 }
228             }
229         }
230 
OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)231         protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
232         {
233             ValidateCommandTimeout(timeout);
234 
235             return new CloseAsyncResult(this, timeout, callback, state);
236         }
237 
OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)238         protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
239         {
240             ValidateCommandTimeout(timeout);
241 
242             return new OpenAsyncResult(this, timeout, callback, state);
243         }
244 
OnClose(TimeSpan timeout)245         protected override void OnClose(TimeSpan timeout)
246         {
247         }
248 
OnEndClose(IAsyncResult result)249         protected override void OnEndClose(IAsyncResult result)
250         {
251             if (result == null)
252             {
253                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("result");
254             }
255 
256             CloseAsyncResult.End(result);
257         }
258 
OnEndOpen(IAsyncResult result)259         protected override void OnEndOpen(IAsyncResult result)
260         {
261             if (result == null)
262             {
263                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("result");
264             }
265 
266             OpenAsyncResult.End(result);
267         }
268 
OnOpen(TimeSpan timeout)269         protected override void OnOpen(TimeSpan timeout)
270         {
271             ValidateCommandTimeout(timeout);
272 
273             try
274             {
275                 PerformOpen(timeout);
276             }
277             catch (Exception e)
278             {
279                 if (Fx.IsFatal(e))
280                 {
281                     throw;
282                 }
283 
284                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
285                     new PersistenceException(
286                     SR2.GetString(SR2.ErrorOpeningSqlPersistenceProvider),
287                     e));
288             }
289         }
290 
ConvertTimeSpanToSqlTimeout(TimeSpan timeout)291         static int ConvertTimeSpanToSqlTimeout(TimeSpan timeout)
292         {
293             if (timeout == TimeSpan.MaxValue)
294             {
295                 return 0;
296             }
297             else
298             {
299                 Fx.Assert(timeout <= TimeSpan.FromSeconds(int.MaxValue),
300                     "Timeout should have been validated before entering this method.");
301 
302                 return Convert.ToInt32(timeout.TotalSeconds);
303             }
304         }
305 
BeginCreate(Guid id, object instance, TimeSpan timeout, bool unlockInstance, AsyncCallback callback, object state)306         IAsyncResult BeginCreate(Guid id, object instance, TimeSpan timeout, bool unlockInstance, AsyncCallback callback, object state)
307         {
308             base.ThrowIfDisposedOrNotOpen();
309 
310             if (instance == null)
311             {
312                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("instance");
313             }
314 
315             ValidateCommandTimeout(timeout);
316 
317             return new OperationAsyncResult(this.createHandler, this, id, timeout, callback, state, instance, unlockInstance);
318         }
319 
320         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801")]
BeginDelete(Guid id, object instance, TimeSpan timeout, AsyncCallback callback, object state)321         IAsyncResult BeginDelete(Guid id, object instance, TimeSpan timeout, AsyncCallback callback, object state)
322         {
323             base.ThrowIfDisposedOrNotOpen();
324 
325             ValidateCommandTimeout(timeout);
326 
327             return new OperationAsyncResult(this.deleteHandler, this, id, timeout, callback, state);
328         }
329 
BeginLoad(Guid id, TimeSpan timeout, bool lockInstance, AsyncCallback callback, object state)330         IAsyncResult BeginLoad(Guid id, TimeSpan timeout, bool lockInstance, AsyncCallback callback, object state)
331         {
332             base.ThrowIfDisposedOrNotOpen();
333 
334             ValidateCommandTimeout(timeout);
335 
336             return new OperationAsyncResult(this.loadHandler, this, id, timeout, callback, state, lockInstance);
337         }
338 
BeginUnlock(Guid id, TimeSpan timeout, AsyncCallback callback, object state)339         IAsyncResult BeginUnlock(Guid id, TimeSpan timeout, AsyncCallback callback, object state)
340         {
341             base.ThrowIfDisposedOrNotOpen();
342 
343             ValidateCommandTimeout(timeout);
344 
345             return new OperationAsyncResult(this.unlockHandler, this, id, timeout, callback, state);
346         }
347 
BeginUpdate(Guid id, object instance, TimeSpan timeout, bool unlockInstance, AsyncCallback callback, object state)348         IAsyncResult BeginUpdate(Guid id, object instance, TimeSpan timeout, bool unlockInstance, AsyncCallback callback, object state)
349         {
350             base.ThrowIfDisposedOrNotOpen();
351 
352             if (instance == null)
353             {
354                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("instance");
355             }
356 
357             ValidateCommandTimeout(timeout);
358 
359             return new OperationAsyncResult(this.updateHandler, this, id, timeout, callback, state, instance, unlockInstance);
360         }
361 
CleanupCommand(SqlCommand command)362         void CleanupCommand(SqlCommand command)
363         {
364             lock (this.activeCommands)
365             {
366                 this.activeCommands.Remove(command);
367             }
368 
369             command.Dispose();
370         }
371 
Create(Guid id, object instance, TimeSpan timeout, bool unlockInstance)372         object Create(Guid id, object instance, TimeSpan timeout, bool unlockInstance)
373         {
374             base.ThrowIfDisposedOrNotOpen();
375 
376             if (instance == null)
377             {
378                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("instance");
379             }
380 
381             ValidateCommandTimeout(timeout);
382 
383             PerformOperation(this.createHandler, id, timeout, instance, unlockInstance);
384 
385             return null;
386         }
387 
CreateCommand(SqlConnection connection, TimeSpan timeout)388         SqlCommand CreateCommand(SqlConnection connection, TimeSpan timeout)
389         {
390             SqlCommand command = connection.CreateCommand();
391             command.CommandTimeout = ConvertTimeSpanToSqlTimeout(timeout);
392 
393             lock (this.activeCommands)
394             {
395                 this.activeCommands.Add(command);
396             }
397 
398             return command;
399         }
400 
401         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801")]
Delete(Guid id, object instance, TimeSpan timeout)402         void Delete(Guid id, object instance, TimeSpan timeout)
403         {
404             base.ThrowIfDisposedOrNotOpen();
405 
406             ValidateCommandTimeout(timeout);
407 
408             PerformOperation(this.deleteHandler, id, timeout);
409         }
410 
DeserializeInstance(object serializedInstance, bool isText)411         object DeserializeInstance(object serializedInstance, bool isText)
412         {
413             object instance;
414 
415             NetDataContractSerializer serializer = new NetDataContractSerializer();
416             if (isText)
417             {
418                 StringReader stringReader = new StringReader((string)serializedInstance);
419                 XmlReader xmlReader = XmlReader.Create(stringReader);
420 
421                 instance = serializer.ReadObject(xmlReader);
422 
423                 xmlReader.Close();
424                 stringReader.Close();
425             }
426             else
427             {
428                 XmlDictionaryReader dictionaryReader = XmlDictionaryReader.CreateBinaryReader((byte[])serializedInstance, XmlDictionaryReaderQuotas.Max);
429 
430                 instance = serializer.ReadObject(dictionaryReader);
431 
432                 dictionaryReader.Close();
433             }
434 
435             return instance;
436         }
437 
EndCreate(IAsyncResult result)438         object EndCreate(IAsyncResult result)
439         {
440             if (result == null)
441             {
442                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("result");
443             }
444 
445             OperationAsyncResult.End(result);
446 
447             return null;
448         }
449 
EndDelete(IAsyncResult result)450         void EndDelete(IAsyncResult result)
451         {
452             if (result == null)
453             {
454                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("result");
455             }
456 
457             OperationAsyncResult.End(result);
458         }
459 
EndLoad(IAsyncResult result)460         object EndLoad(IAsyncResult result)
461         {
462             if (result == null)
463             {
464                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("result");
465             }
466 
467             return OperationAsyncResult.End(result);
468         }
469 
EndUnlock(IAsyncResult result)470         void EndUnlock(IAsyncResult result)
471         {
472             OperationAsyncResult.End(result);
473         }
474 
EndUpdate(IAsyncResult result)475         object EndUpdate(IAsyncResult result)
476         {
477             if (result == null)
478             {
479                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("result");
480             }
481 
482             OperationAsyncResult.End(result);
483 
484             return null;
485         }
486 
GetBinarySerializedForm(object instance)487         byte[] GetBinarySerializedForm(object instance)
488         {
489             NetDataContractSerializer serializer = new NetDataContractSerializer();
490             MemoryStream memStr = new MemoryStream();
491             XmlDictionaryWriter dictionaryWriter = XmlDictionaryWriter.CreateBinaryWriter(memStr);
492 
493             serializer.WriteObject(dictionaryWriter, instance);
494             dictionaryWriter.Flush();
495 
496             byte[] bytes = memStr.ToArray();
497 
498             dictionaryWriter.Close();
499             memStr.Close();
500 
501             return bytes;
502         }
503 
GetConnectionString(TimeSpan timeout)504         string GetConnectionString(TimeSpan timeout)
505         {
506             if (this.canonicalConnectionString != null)
507             {
508                 StringBuilder sb = new StringBuilder(this.canonicalConnectionString);
509                 sb.Append(ConvertTimeSpanToSqlTimeout(timeout));
510                 return sb.ToString();
511             }
512 
513             return this.connectionString;
514         }
515 
GetXmlSerializedForm(object instance)516         string GetXmlSerializedForm(object instance)
517         {
518             NetDataContractSerializer serializer = new NetDataContractSerializer();
519             MemoryStream memStr = new MemoryStream();
520 
521             serializer.WriteObject(memStr, instance);
522 
523             string xml = UnicodeEncoding.UTF8.GetString(memStr.ToArray());
524 
525             memStr.Close();
526 
527             return xml;
528         }
529 
Load(Guid id, TimeSpan timeout, bool lockInstance)530         object Load(Guid id, TimeSpan timeout, bool lockInstance)
531         {
532             base.ThrowIfDisposedOrNotOpen();
533 
534             ValidateCommandTimeout(timeout);
535 
536             return PerformOperation(this.loadHandler, id, timeout, lockInstance);
537         }
538 
OpenConnection(TimeSpan timeout)539         SqlConnection OpenConnection(TimeSpan timeout)
540         {
541             // Do I need to do timeout decrementing?
542             SqlConnection connection = new SqlConnection(GetConnectionString(timeout));
543             connection.Open();
544 
545             return connection;
546         }
547 
PerformOpen(TimeSpan timeout)548         void PerformOpen(TimeSpan timeout)
549         {
550             string lowerCaseConnectionString = this.connectionString.ToUpper(CultureInfo.InvariantCulture);
551 
552             if (!lowerCaseConnectionString.Contains("CONNECTION TIMEOUT") &&
553                 !lowerCaseConnectionString.Contains("CONNECTIONTIMEOUT"))
554             {
555                 this.canonicalConnectionString = this.connectionString.Trim();
556 
557                 if (this.canonicalConnectionString.EndsWith(";", StringComparison.Ordinal))
558                 {
559                     this.canonicalConnectionString += "Connection Timeout=";
560                 }
561                 else
562                 {
563                     this.canonicalConnectionString += ";Connection Timeout=";
564                 }
565             }
566 
567             // Check that the connection string is valid
568             using (SqlConnection connection = new SqlConnection(GetConnectionString(timeout)))
569             {
570                 if (DiagnosticUtility.ShouldTraceInformation)
571                 {
572                     Dictionary<string, string> openParameters = new Dictionary<string, string>(2)
573                     {
574                         { "IsLocking", this.IsLockingTurnedOn ? "True" : "False" },
575                         { "LockTimeout", this.lockTimeout.ToString() }
576                     };
577 
578                     TraceRecord record = new DictionaryTraceRecord(openParameters);
579 
580                     TraceUtility.TraceEvent(TraceEventType.Information,
581                         TraceCode.SqlPersistenceProviderOpenParameters, SR.GetString(SR.TraceCodeSqlPersistenceProviderOpenParameters),
582                         record, this, null);
583                 }
584 
585                 connection.Open();
586             }
587 
588             this.activeCommands = new List<SqlCommand>();
589             this.hostId = Guid.NewGuid();
590         }
591 
PerformOperation(OperationHandler handler, Guid id, TimeSpan timeout, params object[] additionalParameters)592         object PerformOperation(OperationHandler handler, Guid id, TimeSpan timeout, params object[] additionalParameters)
593         {
594             int resultCode;
595             object returnValue = null;
596 
597             if (DiagnosticUtility.ShouldTraceInformation)
598             {
599                 string traceText = SR2.GetString(SR2.SqlPrsistenceProviderOperationAndInstanceId, handler.OperationName, id.ToString());
600                 TraceUtility.TraceEvent(TraceEventType.Information,
601                     TraceCode.SqlPersistenceProviderSQLCallStart, SR.GetString(SR.TraceCodeSqlPersistenceProviderSQLCallStart),
602                     new StringTraceRecord("OperationDetail", traceText),
603                     this, null);
604             }
605 
606             try
607             {
608                 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
609 
610                 using (SqlConnection connection = OpenConnection(timeoutHelper.RemainingTime()))
611                 {
612                     SqlCommand command = CreateCommand(connection, timeoutHelper.RemainingTime());
613 
614                     try
615                     {
616                         handler.SetupCommand(command, id, additionalParameters);
617 
618                         if (handler.ExecuteReader)
619                         {
620                             using (SqlDataReader reader = command.ExecuteReader())
621                             {
622                                 returnValue = handler.ProcessReader(reader);
623                             }
624                         }
625                         else
626                         {
627                             command.ExecuteNonQuery();
628                         }
629 
630                         resultCode = (int)command.Parameters["@result"].Value;
631                     }
632                     finally
633                     {
634                         CleanupCommand(command);
635                     }
636                 }
637             }
638             catch (Exception e)
639             {
640                 if (Fx.IsFatal(e))
641                 {
642                     throw;
643                 }
644 
645                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
646                     new PersistenceException(
647                     SR2.GetString(SR2.PersistenceOperationError, handler.OperationName),
648                     e));
649             }
650 
651             Exception toThrow = handler.ProcessResult(resultCode, id, returnValue);
652 
653             if (DiagnosticUtility.ShouldTraceInformation)
654             {
655                 string traceText = SR2.GetString(SR2.SqlPrsistenceProviderOperationAndInstanceId, handler.OperationName, id.ToString());
656                 TraceUtility.TraceEvent(TraceEventType.Information,
657                     TraceCode.SqlPersistenceProviderSQLCallEnd, SR.GetString(SR.TraceCodeSqlPersistenceProviderSQLCallEnd),
658                     new StringTraceRecord("OperationDetail", traceText),
659                     this, null);
660             }
661 
662             if (toThrow != null)
663             {
664                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(toThrow);
665             }
666 
667             return returnValue;
668         }
669 
Unlock(Guid id, TimeSpan timeout)670         void Unlock(Guid id, TimeSpan timeout)
671         {
672             base.ThrowIfDisposedOrNotOpen();
673 
674             if (this.unlockHandler.ShortcutExecution)
675             {
676                 return;
677             }
678 
679             ValidateCommandTimeout(timeout);
680 
681             PerformOperation(this.unlockHandler, id, timeout);
682         }
683 
Update(Guid id, object instance, TimeSpan timeout, bool unlockInstance)684         object Update(Guid id, object instance, TimeSpan timeout, bool unlockInstance)
685         {
686             base.ThrowIfDisposedOrNotOpen();
687 
688             if (instance == null)
689             {
690                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("instance");
691             }
692 
693             ValidateCommandTimeout(timeout);
694 
695             PerformOperation(this.updateHandler, id, timeout, instance, unlockInstance);
696 
697             return null;
698         }
699 
ValidateCommandTimeout(TimeSpan timeout)700         void ValidateCommandTimeout(TimeSpan timeout)
701         {
702             if (timeout <= TimeSpan.Zero ||
703                 (timeout > SqlPersistenceProviderFactory.maxSecondsTimeSpan && timeout != TimeSpan.MaxValue))
704             {
705                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument(
706                     "timeout",
707                     SR2.GetString(SR2.CommandTimeoutOutOfRange));
708             }
709         }
710 
711         class CloseAsyncResult : AsyncResult
712         {
CloseAsyncResult(SqlPersistenceProviderFactory provider, TimeSpan timeout, AsyncCallback callback, object state)713             public CloseAsyncResult(SqlPersistenceProviderFactory provider, TimeSpan timeout, AsyncCallback callback, object state)
714                 : base(callback, state)
715             {
716                 // There is no point in even pretending SqlConnection.Close needs async
717                 provider.OnClose(timeout);
718 
719                 Complete(true);
720             }
721 
End(IAsyncResult result)722             public static void End(IAsyncResult result)
723             {
724                 AsyncResult.End<CloseAsyncResult>(result);
725             }
726         }
727 
728         class CreateHandler : OperationHandler
729         {
CreateHandler(SqlPersistenceProviderFactory provider)730             public CreateHandler(SqlPersistenceProviderFactory provider)
731                 : base(provider)
732             {
733             }
734 
735             public override string OperationName
736             {
737                 get { return "Create"; }
738             }
739 
ProcessResult(int resultCode, Guid id, object loadedInstance)740             public override Exception ProcessResult(int resultCode, Guid id, object loadedInstance)
741             {
742                 switch (resultCode)
743                 {
744                     case 0: // Success
745                         return null;
746                     case 1: // Already exists
747                         return new PersistenceException(SR2.GetString(SR2.InstanceAlreadyExists, id));
748                     case 2: // Some other error
749                         return new PersistenceException(SR2.GetString(SR2.InsertFailed, id));
750                     default:
751                         return
752                             new PersistenceException(SR2.GetString(SR2.UnknownStoredProcResult));
753                 }
754             }
755 
SetupCommand(SqlCommand command, Guid id, params object[] additionalParameters)756             public override void SetupCommand(SqlCommand command, Guid id, params object[] additionalParameters)
757             {
758                 Fx.Assert(additionalParameters != null && additionalParameters.Length == 2,
759                     "Should have had 2 additional parameters.");
760 
761                 Fx.Assert(additionalParameters[1].GetType() == typeof(bool),
762                     "Parameter at index 1 should have been a boolean.");
763 
764                 object instance = additionalParameters[0];
765 
766                 command.CommandType = CommandType.StoredProcedure;
767                 command.CommandText = "InsertInstance";
768 
769                 SqlParameter idParameter = new SqlParameter("@id", SqlDbType.UniqueIdentifier);
770                 idParameter.Value = id;
771                 command.Parameters.Add(idParameter);
772 
773                 SqlParameter instanceParameter = new SqlParameter("@instance", SqlDbType.Image);
774                 SqlParameter instanceXmlParameter = new SqlParameter("@instanceXml", SqlDbType.Xml);
775 
776                 if (this.provider.serializeAsText)
777                 {
778                     instanceXmlParameter.Value = this.provider.GetXmlSerializedForm(instance);
779                     instanceParameter.Value = null;
780                 }
781                 else
782                 {
783                     instanceParameter.Value = this.provider.GetBinarySerializedForm(instance);
784                     instanceXmlParameter.Value = null;
785                 }
786 
787                 command.Parameters.Add(instanceParameter);
788                 command.Parameters.Add(instanceXmlParameter);
789 
790                 SqlParameter unlockInstanceParameter = new SqlParameter("@unlockInstance", SqlDbType.Bit);
791                 unlockInstanceParameter.Value = (bool)additionalParameters[1];
792                 command.Parameters.Add(unlockInstanceParameter);
793 
794                 SqlParameter lockOwnerParameter = new SqlParameter("@hostId", SqlDbType.UniqueIdentifier);
795                 lockOwnerParameter.Value = this.provider.hostId;
796                 command.Parameters.Add(lockOwnerParameter);
797 
798                 SqlParameter lockTimeoutParameter = new SqlParameter("@lockTimeout", SqlDbType.Int);
799                 lockTimeoutParameter.Value = this.provider.LockTimeoutAsInt;
800                 command.Parameters.Add(lockTimeoutParameter);
801 
802                 SqlParameter resultParameter = new SqlParameter("@result", SqlDbType.Int);
803                 resultParameter.Direction = ParameterDirection.Output;
804                 command.Parameters.Add(resultParameter);
805             }
806         }
807 
808         class DeleteHandler : OperationHandler
809         {
DeleteHandler(SqlPersistenceProviderFactory provider)810             public DeleteHandler(SqlPersistenceProviderFactory provider)
811                 : base(provider)
812             {
813             }
814 
815             public override string OperationName
816             {
817                 get { return "Delete"; }
818             }
819 
ProcessResult(int resultCode, Guid id, object loadedInstance)820             public override Exception ProcessResult(int resultCode, Guid id, object loadedInstance)
821             {
822                 switch (resultCode)
823                 {
824                     case 0: // Success
825                         return null;
826                     case 1: // Instance not found
827                         return
828                             new InstanceNotFoundException(id);
829                     case 2: // Could not acquire lock
830                         return new InstanceLockException(id, SR2.GetString(SR2.DidNotOwnLock, id, OperationName));
831                     default:
832                         return
833                             new PersistenceException(
834                             SR2.GetString(SR2.UnknownStoredProcResult));
835                 }
836             }
837 
SetupCommand(SqlCommand command, Guid id, params object[] additionalParameters)838             public override void SetupCommand(SqlCommand command, Guid id, params object[] additionalParameters)
839             {
840                 Fx.Assert(additionalParameters == null || additionalParameters.Length == 0,
841                     "Should not have gotten any additional parameters.");
842 
843                 command.CommandType = CommandType.StoredProcedure;
844                 command.CommandText = "DeleteInstance";
845 
846                 SqlParameter idParameter = new SqlParameter("@id", SqlDbType.UniqueIdentifier);
847                 idParameter.Value = id;
848                 command.Parameters.Add(idParameter);
849 
850                 SqlParameter hostIdParameter = new SqlParameter("@hostId", SqlDbType.UniqueIdentifier);
851                 hostIdParameter.Value = this.provider.hostId;
852                 command.Parameters.Add(hostIdParameter);
853 
854                 SqlParameter lockTimeoutParameter = new SqlParameter("@lockTimeout", SqlDbType.Int);
855                 lockTimeoutParameter.Value = this.provider.LockTimeoutAsInt;
856                 command.Parameters.Add(lockTimeoutParameter);
857 
858                 SqlParameter resultParameter = new SqlParameter("@result", SqlDbType.Int);
859                 resultParameter.Direction = ParameterDirection.Output;
860                 command.Parameters.Add(resultParameter);
861             }
862         }
863 
864         class LoadHandler : OperationHandler
865         {
LoadHandler(SqlPersistenceProviderFactory provider)866             public LoadHandler(SqlPersistenceProviderFactory provider)
867                 : base(provider)
868             {
869             }
870 
871             public override bool ExecuteReader
872             {
873                 get { return true; }
874             }
875 
876             public override string OperationName
877             {
878                 get { return "Load"; }
879             }
880 
ProcessReader(SqlDataReader reader)881             public override object ProcessReader(SqlDataReader reader)
882             {
883                 if (reader.Read())
884                 {
885                     bool isXml = ((int)reader["isXml"] == 0 ? false : true);
886                     object serializedInstance;
887 
888                     if (isXml)
889                     {
890                         serializedInstance = reader["instanceXml"];
891                     }
892                     else
893                     {
894                         serializedInstance = reader["instance"];
895                     }
896 
897                     if (serializedInstance != null)
898                     {
899                         return this.provider.DeserializeInstance(serializedInstance, isXml);
900                     }
901                 }
902 
903                 return null;
904             }
905 
ProcessResult(int resultCode, Guid id, object loadedInstance)906             public override Exception ProcessResult(int resultCode, Guid id, object loadedInstance)
907             {
908                 Exception toReturn = null;
909 
910                 switch (resultCode)
911                 {
912                     case 0: // Success
913                         break;
914                     case 1: // Instance not found
915                         toReturn = new InstanceNotFoundException(id);
916                         break;
917                     case 2: // Could not acquire lock
918                         toReturn = new InstanceLockException(id);
919                         break;
920                     default:
921                         toReturn =
922                             new PersistenceException(SR2.GetString(SR2.UnknownStoredProcResult));
923                         break;
924                 }
925 
926                 if (toReturn == null)
927                 {
928                     if (loadedInstance == null)
929                     {
930                         toReturn = new PersistenceException(SR2.GetString(SR2.SerializationFormatMismatch));
931                     }
932                 }
933 
934                 return toReturn;
935             }
936 
SetupCommand(SqlCommand command, Guid id, params object[] additionalParameters)937             public override void SetupCommand(SqlCommand command, Guid id, params object[] additionalParameters)
938             {
939                 Fx.Assert(additionalParameters != null && additionalParameters.Length == 1,
940                     "Should have had 1 additional parameter.");
941 
942                 Fx.Assert(additionalParameters[0].GetType() == typeof(bool),
943                     "Parameter 0 should have been a boolean.");
944 
945                 command.CommandType = CommandType.StoredProcedure;
946                 command.CommandText = "LoadInstance";
947 
948                 SqlParameter idParameter = new SqlParameter("@id", SqlDbType.UniqueIdentifier);
949                 idParameter.Value = id;
950                 command.Parameters.Add(idParameter);
951 
952                 SqlParameter lockInstanceParameter = new SqlParameter("@lockInstance", SqlDbType.Bit);
953                 lockInstanceParameter.Value = (bool)additionalParameters[0];
954                 command.Parameters.Add(lockInstanceParameter);
955 
956                 SqlParameter hostIdParameter = new SqlParameter("@hostId", SqlDbType.UniqueIdentifier);
957                 hostIdParameter.Value = this.provider.hostId;
958                 command.Parameters.Add(hostIdParameter);
959 
960                 SqlParameter lockTimeoutParameter = new SqlParameter("@lockTimeout", SqlDbType.Int);
961                 lockTimeoutParameter.Value = this.provider.LockTimeoutAsInt;
962                 command.Parameters.Add(lockTimeoutParameter);
963 
964                 SqlParameter resultParameter = new SqlParameter("@result", SqlDbType.Int);
965                 resultParameter.Direction = ParameterDirection.Output;
966                 command.Parameters.Add(resultParameter);
967             }
968         }
969 
970         class OpenAsyncResult : AsyncResult
971         {
972             SqlPersistenceProviderFactory provider;
973             TimeSpan timeout;
974 
OpenAsyncResult(SqlPersistenceProviderFactory provider, TimeSpan timeout, AsyncCallback callback, object state)975             public OpenAsyncResult(SqlPersistenceProviderFactory provider, TimeSpan timeout, AsyncCallback callback, object state)
976                 : base(callback, state)
977             {
978                 Fx.Assert(provider != null,
979                     "Provider should never be null.");
980 
981                 this.provider = provider;
982                 this.timeout = timeout;
983 
984                 ActionItem.Schedule(ScheduledCallback, null);
985             }
986 
End(IAsyncResult result)987             public static void End(IAsyncResult result)
988             {
989                 AsyncResult.End<OpenAsyncResult>(result);
990             }
991 
ScheduledCallback(object state)992             void ScheduledCallback(object state)
993             {
994                 Exception completionException = null;
995 
996                 try
997                 {
998                     this.provider.PerformOpen(this.timeout);
999                 }
1000                 catch (Exception e)
1001                 {
1002                     if (Fx.IsFatal(e))
1003                     {
1004                         throw;
1005                     }
1006 
1007                     completionException =
1008                         new PersistenceException(
1009                         SR2.GetString(SR2.ErrorOpeningSqlPersistenceProvider),
1010                         e);
1011                 }
1012 
1013                 Complete(false, completionException);
1014             }
1015         }
1016 
1017         class OperationAsyncResult : AsyncResult
1018         {
1019             protected SqlPersistenceProviderFactory provider;
1020             static AsyncCallback commandCallback = Fx.ThunkCallback(new AsyncCallback(CommandExecutionComplete));
1021 
1022             SqlCommand command;
1023             OperationHandler handler;
1024             Guid id;
1025 
1026             object instance;
1027 
1028             // We are using virtual methods from the constructor on purpose
1029             [SuppressMessage("Microsoft.Usage", "CA2214")]
OperationAsyncResult(OperationHandler handler, SqlPersistenceProviderFactory provider, Guid id, TimeSpan timeout, AsyncCallback callback, object state, params object[] additionalParameters)1030             public OperationAsyncResult(OperationHandler handler, SqlPersistenceProviderFactory provider, Guid id, TimeSpan timeout, AsyncCallback callback, object state, params object[] additionalParameters)
1031                 : base(callback, state)
1032             {
1033                 Fx.Assert(provider != null,
1034                     "Provider should never be null.");
1035 
1036                 this.handler = handler;
1037                 this.provider = provider;
1038                 this.id = id;
1039 
1040                 if (this.handler.ShortcutExecution)
1041                 {
1042                     Complete(true);
1043                     return;
1044                 }
1045 
1046                 TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
1047                 SqlConnection connection = this.provider.OpenConnection(timeoutHelper.RemainingTime());
1048 
1049                 bool completeSelf = false;
1050                 Exception delayedException = null;
1051                 try
1052                 {
1053                     this.command = this.provider.CreateCommand(connection, timeoutHelper.RemainingTime());
1054 
1055                     this.handler.SetupCommand(this.command, this.id, additionalParameters);
1056 
1057                     IAsyncResult result = null;
1058 
1059                     if (this.handler.ExecuteReader)
1060                     {
1061                         result = this.command.BeginExecuteReader(commandCallback, this);
1062                     }
1063                     else
1064                     {
1065                         result = this.command.BeginExecuteNonQuery(commandCallback, this);
1066                     }
1067 
1068                     if (result.CompletedSynchronously)
1069                     {
1070                         delayedException = CompleteOperation(result);
1071 
1072                         completeSelf = true;
1073                     }
1074                 }
1075                 catch (Exception e)
1076                 {
1077                     if (Fx.IsFatal(e))
1078                     {
1079                         throw;
1080                     }
1081 
1082                     try
1083                     {
1084                         connection.Close();
1085                         this.provider.CleanupCommand(this.command);
1086                     }
1087                     catch (Exception e1)
1088                     {
1089                         if (Fx.IsFatal(e1))
1090                         {
1091                             throw;
1092                         }
1093                         // do not rethrow non-fatal exceptions thrown from cleanup code
1094                     }
1095                     finally
1096                     {
1097                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
1098                             new PersistenceException(
1099                             SR2.GetString(SR2.PersistenceOperationError, this.handler.OperationName), e));
1100                     }
1101                 }
1102 
1103                 if (completeSelf)
1104                 {
1105                     connection.Close();
1106                     this.provider.CleanupCommand(this.command);
1107 
1108                     if (delayedException != null)
1109                     {
1110                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(delayedException);
1111                     }
1112 
1113                     Complete(true);
1114                 }
1115             }
1116 
1117             public object Instance
1118             {
1119                 get { return this.instance; }
1120             }
1121 
End(IAsyncResult result)1122             public static object End(IAsyncResult result)
1123             {
1124                 OperationAsyncResult operationResult = AsyncResult.End<OperationAsyncResult>(result);
1125 
1126                 return operationResult.Instance;
1127             }
1128 
CommandExecutionComplete(IAsyncResult result)1129             static void CommandExecutionComplete(IAsyncResult result)
1130             {
1131                 if (result.CompletedSynchronously)
1132                 {
1133                     return;
1134                 }
1135 
1136                 OperationAsyncResult operationResult = (OperationAsyncResult)result.AsyncState;
1137 
1138                 Exception completionException = null;
1139                 try
1140                 {
1141                     completionException = operationResult.CompleteOperation(result);
1142                 }
1143                 catch (Exception e)
1144                 {
1145                     if (Fx.IsFatal(e))
1146                     {
1147                         throw;
1148                     }
1149 
1150                     completionException =
1151                         new PersistenceException(
1152                         SR2.GetString(SR2.PersistenceOperationError, operationResult.handler.OperationName), e);
1153                 }
1154                 finally
1155                 {
1156                     try
1157                     {
1158                         operationResult.command.Connection.Close();
1159                         operationResult.provider.CleanupCommand(operationResult.command);
1160                     }
1161                     catch (Exception e1)
1162                     {
1163                         if (Fx.IsFatal(e1))
1164                         {
1165                             throw;
1166                         }
1167                         // do not rethrow non-fatal exceptions thrown from cleanup code
1168                     }
1169                 }
1170 
1171                 operationResult.Complete(false, completionException);
1172             }
1173 
CompleteOperation(IAsyncResult result)1174             Exception CompleteOperation(IAsyncResult result)
1175             {
1176                 Exception delayedException = null;
1177 
1178                 if (this.handler.ExecuteReader)
1179                 {
1180                     using (SqlDataReader reader = this.command.EndExecuteReader(result))
1181                     {
1182                         this.instance = this.handler.ProcessReader(reader);
1183                     }
1184                 }
1185                 else
1186                 {
1187                     this.command.EndExecuteNonQuery(result);
1188                 }
1189 
1190                 int resultCode = (int)this.command.Parameters["@result"].Value;
1191 
1192                 delayedException = this.handler.ProcessResult(resultCode, this.id, this.instance);
1193 
1194                 return delayedException;
1195             }
1196         }
1197 
1198         abstract class OperationHandler
1199         {
1200             protected SqlPersistenceProviderFactory provider;
1201 
OperationHandler(SqlPersistenceProviderFactory provider)1202             public OperationHandler(SqlPersistenceProviderFactory provider)
1203             {
1204                 this.provider = provider;
1205             }
1206 
1207             public virtual bool ExecuteReader
1208             {
1209                 get { return false; }
1210             }
1211 
1212             public abstract string OperationName
1213             { get; }
1214 
1215             public virtual bool ShortcutExecution
1216             {
1217                 get { return false; }
1218             }
1219 
1220 
ProcessReader(SqlDataReader reader)1221             public virtual object ProcessReader(SqlDataReader reader)
1222             {
1223                 return null;
1224             }
1225 
ProcessResult(int resultCode, Guid id, object loadedInstance)1226             public abstract Exception ProcessResult(int resultCode, Guid id, object loadedInstance);
1227 
SetupCommand(SqlCommand command, Guid id, params object[] additionalParameters)1228             public abstract void SetupCommand(SqlCommand command, Guid id, params object[] additionalParameters);
1229         }
1230 
1231         class SqlPersistenceProvider : LockingPersistenceProvider
1232         {
1233             SqlPersistenceProviderFactory factory;
1234 
SqlPersistenceProvider(Guid id, SqlPersistenceProviderFactory factory)1235             public SqlPersistenceProvider(Guid id, SqlPersistenceProviderFactory factory)
1236                 : base(id)
1237             {
1238                 this.factory = factory;
1239             }
1240 
1241             protected override TimeSpan DefaultCloseTimeout
1242             {
1243                 get { return TimeSpan.FromSeconds(15); }
1244             }
1245 
1246             protected override TimeSpan DefaultOpenTimeout
1247             {
1248                 get { return TimeSpan.FromSeconds(15); }
1249             }
1250 
BeginCreate(object instance, TimeSpan timeout, bool unlockInstance, AsyncCallback callback, object state)1251             public override IAsyncResult BeginCreate(object instance, TimeSpan timeout, bool unlockInstance, AsyncCallback callback, object state)
1252             {
1253                 base.ThrowIfDisposedOrNotOpen();
1254                 return this.factory.BeginCreate(this.Id, instance, timeout, unlockInstance, callback, state);
1255             }
1256 
BeginDelete(object instance, TimeSpan timeout, AsyncCallback callback, object state)1257             public override IAsyncResult BeginDelete(object instance, TimeSpan timeout, AsyncCallback callback, object state)
1258             {
1259                 base.ThrowIfDisposedOrNotOpen();
1260                 return this.factory.BeginDelete(this.Id, instance, timeout, callback, state);
1261             }
1262 
BeginLoad(TimeSpan timeout, bool lockInstance, AsyncCallback callback, object state)1263             public override IAsyncResult BeginLoad(TimeSpan timeout, bool lockInstance, AsyncCallback callback, object state)
1264             {
1265                 base.ThrowIfDisposedOrNotOpen();
1266                 return this.factory.BeginLoad(this.Id, timeout, lockInstance, callback, state);
1267             }
1268 
BeginUnlock(TimeSpan timeout, AsyncCallback callback, object state)1269             public override IAsyncResult BeginUnlock(TimeSpan timeout, AsyncCallback callback, object state)
1270             {
1271                 base.ThrowIfDisposedOrNotOpen();
1272                 return this.factory.BeginUnlock(this.Id, timeout, callback, state);
1273             }
1274 
BeginUpdate(object instance, TimeSpan timeout, bool unlockInstance, AsyncCallback callback, object state)1275             public override IAsyncResult BeginUpdate(object instance, TimeSpan timeout, bool unlockInstance, AsyncCallback callback, object state)
1276             {
1277                 base.ThrowIfDisposedOrNotOpen();
1278                 return this.factory.BeginUpdate(this.Id, instance, timeout, unlockInstance, callback, state);
1279             }
1280 
Create(object instance, TimeSpan timeout, bool unlockInstance)1281             public override object Create(object instance, TimeSpan timeout, bool unlockInstance)
1282             {
1283                 base.ThrowIfDisposedOrNotOpen();
1284                 return this.factory.Create(this.Id, instance, timeout, unlockInstance);
1285             }
1286 
Delete(object instance, TimeSpan timeout)1287             public override void Delete(object instance, TimeSpan timeout)
1288             {
1289                 base.ThrowIfDisposedOrNotOpen();
1290                 this.factory.Delete(this.Id, instance, timeout);
1291             }
1292 
EndCreate(IAsyncResult result)1293             public override object EndCreate(IAsyncResult result)
1294             {
1295                 return this.factory.EndCreate(result);
1296             }
1297 
EndDelete(IAsyncResult result)1298             public override void EndDelete(IAsyncResult result)
1299             {
1300                 this.factory.EndDelete(result);
1301             }
1302 
EndLoad(IAsyncResult result)1303             public override object EndLoad(IAsyncResult result)
1304             {
1305                 return this.factory.EndLoad(result);
1306             }
1307 
EndUnlock(IAsyncResult result)1308             public override void EndUnlock(IAsyncResult result)
1309             {
1310                 this.factory.EndUnlock(result);
1311             }
1312 
EndUpdate(IAsyncResult result)1313             public override object EndUpdate(IAsyncResult result)
1314             {
1315                 return this.factory.EndUpdate(result);
1316             }
1317 
Load(TimeSpan timeout, bool lockInstance)1318             public override object Load(TimeSpan timeout, bool lockInstance)
1319             {
1320                 base.ThrowIfDisposedOrNotOpen();
1321                 return this.factory.Load(this.Id, timeout, lockInstance);
1322             }
1323 
Unlock(TimeSpan timeout)1324             public override void Unlock(TimeSpan timeout)
1325             {
1326                 base.ThrowIfDisposedOrNotOpen();
1327                 this.factory.Unlock(this.Id, timeout);
1328             }
1329 
Update(object instance, TimeSpan timeout, bool unlockInstance)1330             public override object Update(object instance, TimeSpan timeout, bool unlockInstance)
1331             {
1332                 base.ThrowIfDisposedOrNotOpen();
1333                 return this.factory.Update(this.Id, instance, timeout, unlockInstance);
1334             }
1335 
OnAbort()1336             protected override void OnAbort()
1337             {
1338             }
1339 
OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)1340             protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
1341             {
1342                 return new CompletedAsyncResult(callback, state);
1343             }
1344 
OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)1345             protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
1346             {
1347                 return new CompletedAsyncResult(callback, state);
1348             }
1349 
OnClose(TimeSpan timeout)1350             protected override void OnClose(TimeSpan timeout)
1351             {
1352             }
1353 
OnEndClose(IAsyncResult result)1354             protected override void OnEndClose(IAsyncResult result)
1355             {
1356                 CompletedAsyncResult.End(result);
1357             }
1358 
OnEndOpen(IAsyncResult result)1359             protected override void OnEndOpen(IAsyncResult result)
1360             {
1361                 CompletedAsyncResult.End(result);
1362             }
1363 
OnOpen(TimeSpan timeout)1364             protected override void OnOpen(TimeSpan timeout)
1365             {
1366             }
1367         }
1368 
1369         class UnlockHandler : OperationHandler
1370         {
UnlockHandler(SqlPersistenceProviderFactory provider)1371             public UnlockHandler(SqlPersistenceProviderFactory provider)
1372                 : base(provider)
1373             {
1374             }
1375 
1376             public override string OperationName
1377             {
1378                 get { return "Unlock"; }
1379             }
1380 
1381             public override bool ShortcutExecution
1382             {
1383                 get { return !this.provider.IsLockingTurnedOn; }
1384             }
1385 
ProcessResult(int resultCode, Guid id, object loadedInstance)1386             public override Exception ProcessResult(int resultCode, Guid id, object loadedInstance)
1387             {
1388                 switch (resultCode)
1389                 {
1390                     case 0: // Success
1391                         return null;
1392                     case 1: // Instance not found
1393                         return
1394                             new InstanceNotFoundException(id);
1395                     case 2: // Could not acquire lock
1396                         return new InstanceLockException(id, SR2.GetString(SR2.DidNotOwnLock, id, OperationName));
1397                     default:
1398                         return
1399                             new PersistenceException(SR2.GetString(SR2.UnknownStoredProcResult));
1400                 }
1401             }
1402 
SetupCommand(SqlCommand command, Guid id, params object[] additionalParameters)1403             public override void SetupCommand(SqlCommand command, Guid id, params object[] additionalParameters)
1404             {
1405                 Fx.Assert(additionalParameters == null || additionalParameters.Length == 0,
1406                     "There should not be any additional parameters.");
1407 
1408                 command.CommandType = CommandType.StoredProcedure;
1409                 command.CommandText = "UnlockInstance";
1410 
1411                 SqlParameter idParameter = new SqlParameter("@id", SqlDbType.UniqueIdentifier);
1412                 idParameter.Value = id;
1413                 command.Parameters.Add(idParameter);
1414 
1415                 SqlParameter hostIdParameter = new SqlParameter("@hostId", SqlDbType.UniqueIdentifier);
1416                 hostIdParameter.Value = this.provider.hostId;
1417                 command.Parameters.Add(hostIdParameter);
1418 
1419                 SqlParameter lockTimeoutParameter = new SqlParameter("@lockTimeout", SqlDbType.Int);
1420                 lockTimeoutParameter.Value = this.provider.LockTimeoutAsInt;
1421                 command.Parameters.Add(lockTimeoutParameter);
1422 
1423                 SqlParameter resultParameter = new SqlParameter("@result", SqlDbType.Int);
1424                 resultParameter.Direction = ParameterDirection.Output;
1425                 command.Parameters.Add(resultParameter);
1426             }
1427         }
1428 
1429         class UpdateHandler : OperationHandler
1430         {
UpdateHandler(SqlPersistenceProviderFactory provider)1431             public UpdateHandler(SqlPersistenceProviderFactory provider)
1432                 : base(provider)
1433             {
1434             }
1435 
1436             public override string OperationName
1437             {
1438                 get { return "Update"; }
1439             }
1440 
ProcessResult(int resultCode, Guid id, object loadedInstance)1441             public override Exception ProcessResult(int resultCode, Guid id, object loadedInstance)
1442             {
1443                 switch (resultCode)
1444                 {
1445                     case 0: // Success
1446                         return null;
1447                     case 1: // Instance did not exist
1448                         return new InstanceNotFoundException(id, SR2.GetString(SR2.InstanceNotFoundForUpdate, id));
1449                     case 2: // Did not have lock
1450                         return new InstanceLockException(id, SR2.GetString(SR2.DidNotOwnLock, id, OperationName));
1451                     default:
1452                         return
1453                             new PersistenceException(SR2.GetString(SR2.UnknownStoredProcResult));
1454                 }
1455             }
1456 
SetupCommand(SqlCommand command, Guid id, params object[] additionalParameters)1457             public override void SetupCommand(SqlCommand command, Guid id, params object[] additionalParameters)
1458             {
1459                 Fx.Assert(additionalParameters != null && additionalParameters.Length == 2,
1460                     "Should have had 2 additional parameters.");
1461 
1462                 Fx.Assert(additionalParameters[1].GetType() == typeof(bool),
1463                     "Parameter at index 1 should have been a boolean.");
1464 
1465                 object instance = additionalParameters[0];
1466 
1467                 command.CommandType = CommandType.StoredProcedure;
1468                 command.CommandText = "UpdateInstance";
1469 
1470                 SqlParameter idParameter = new SqlParameter("@id", SqlDbType.UniqueIdentifier);
1471                 idParameter.Value = id;
1472                 command.Parameters.Add(idParameter);
1473 
1474                 SqlParameter instanceParameter = new SqlParameter("@instance", SqlDbType.Image);
1475                 SqlParameter instanceXmlParameter = new SqlParameter("@instanceXml", SqlDbType.Xml);
1476 
1477                 if (this.provider.serializeAsText)
1478                 {
1479                     instanceXmlParameter.Value = this.provider.GetXmlSerializedForm(instance);
1480                     instanceParameter.Value = null;
1481                 }
1482                 else
1483                 {
1484                     instanceParameter.Value = this.provider.GetBinarySerializedForm(instance);
1485                     instanceXmlParameter.Value = null;
1486                 }
1487 
1488                 command.Parameters.Add(instanceParameter);
1489                 command.Parameters.Add(instanceXmlParameter);
1490 
1491                 SqlParameter unlockInstanceParameter = new SqlParameter("@unlockInstance", SqlDbType.Bit);
1492                 unlockInstanceParameter.Value = (bool)additionalParameters[1];
1493                 command.Parameters.Add(unlockInstanceParameter);
1494 
1495                 SqlParameter lockOwnerParameter = new SqlParameter("@hostId", SqlDbType.UniqueIdentifier);
1496                 lockOwnerParameter.Value = this.provider.hostId;
1497                 command.Parameters.Add(lockOwnerParameter);
1498 
1499                 SqlParameter lockTimeoutParameter = new SqlParameter("@lockTimeout", SqlDbType.Int);
1500                 lockTimeoutParameter.Value = this.provider.LockTimeoutAsInt;
1501                 command.Parameters.Add(lockTimeoutParameter);
1502 
1503                 SqlParameter resultParameter = new SqlParameter("@result", SqlDbType.Int);
1504                 resultParameter.Direction = ParameterDirection.Output;
1505                 command.Parameters.Add(resultParameter);
1506             }
1507         }
1508     }
1509 }
1510