//----------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- namespace System.Activities.DurableInstancing { using System.Activities.Hosting; using System.Collections; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Globalization; using System.Linq; using System.Runtime; using System.Runtime.DurableInstancing; using System.Text; using System.Threading; using System.Transactions; using System.Xml; using System.Xml.Linq; sealed class SaveWorkflowAsyncResult : SqlWorkflowInstanceStoreAsyncResult { const string createServiceDeploymentStoredProcedureParameters = @"@serviceDeploymentHash, @siteName, @relativeServicePath, @relativeApplicationPath, @serviceName, @serviceNamespace, @serviceDeploymentId output"; const string storedProcedureParameters40 = @"@instanceId, @surrogateLockOwnerId, @handleInstanceVersion, @handleIsBoundToLock, @primitiveDataProperties, @complexDataProperties, @writeOnlyPrimitiveDataProperties, @writeOnlyComplexDataProperties, @metadataProperties, @metadataIsConsistent, @encodingOption, @timerDurationMilliseconds, @suspensionStateChange, @suspensionReason, @suspensionExceptionName, @keysToAssociate, @keysToComplete, @keysToFree, @concatenatedKeyProperties, @unlockInstance, @isReadyToRun, @isCompleted, @singleKeyId, @lastMachineRunOn, @executionStatus, @blockingBookmarks, @workflowHostType, @serviceDeploymentId, @operationTimeout"; const string storedProcedureParameters = @"@instanceId, @surrogateLockOwnerId, @handleInstanceVersion, @handleIsBoundToLock, @primitiveDataProperties, @complexDataProperties, @writeOnlyPrimitiveDataProperties, @writeOnlyComplexDataProperties, @metadataProperties, @metadataIsConsistent, @encodingOption, @timerDurationMilliseconds, @suspensionStateChange, @suspensionReason, @suspensionExceptionName, @keysToAssociate, @keysToComplete, @keysToFree, @concatenatedKeyProperties, @unlockInstance, @isReadyToRun, @isCompleted, @singleKeyId, @lastMachineRunOn, @executionStatus, @blockingBookmarks, @workflowHostType, @serviceDeploymentId, @operationTimeout, @identityMetadata"; static Dictionary serviceDeploymentIdsCache = new Dictionary(); static ReaderWriterLockSlim serviceDeploymentIdsCacheLock = new ReaderWriterLockSlim(); string commandText; Guid serviceDeploymentHash; long serviceDeploymentId; public SaveWorkflowAsyncResult ( InstancePersistenceContext context, InstancePersistenceCommand command, SqlWorkflowInstanceStore store, SqlWorkflowInstanceStoreLock storeLock, Transaction currentTransaction, TimeSpan timeout, AsyncCallback callback, object state ) : base(context, command, store, storeLock, currentTransaction, timeout, callback, state) { if (((SaveWorkflowCommand)command).InstanceKeyMetadataChanges.Count > 0) { throw FxTrace.Exception.AsError(new InstancePersistenceCommandException(SR.InstanceKeyMetadataChangesNotSupported)); } } protected override void GenerateSqlCommand(SqlCommand command) { SaveWorkflowCommand saveWorkflowCommand = base.InstancePersistenceCommand as SaveWorkflowCommand; StringBuilder commandTextBuilder = new StringBuilder(SqlWorkflowInstanceStoreConstants.DefaultStringBuilderCapacity); double operationTimeout = this.TimeoutHelper.RemainingTime().TotalMilliseconds; SqlParameterCollection parameters = command.Parameters; string suspensionReason; string suspensionExceptionName; parameters.Add(new SqlParameter { ParameterName = "@instanceId", SqlDbType = SqlDbType.UniqueIdentifier, Value = base.InstancePersistenceContext.InstanceView.InstanceId }); parameters.Add(new SqlParameter { ParameterName = "@surrogateLockOwnerId", SqlDbType = SqlDbType.BigInt, Value = base.StoreLock.SurrogateLockOwnerId }); parameters.Add(new SqlParameter { ParameterName = "@handleInstanceVersion", SqlDbType = SqlDbType.BigInt, Value = base.InstancePersistenceContext.InstanceVersion }); parameters.Add(new SqlParameter { ParameterName = "@handleIsBoundToLock", SqlDbType = SqlDbType.Bit, Value = base.InstancePersistenceContext.InstanceView.IsBoundToLock }); parameters.Add(new SqlParameter { ParameterName = "@timerDurationMilliseconds", SqlDbType = SqlDbType.BigInt, Value = (object)GetPendingTimerExpiration(saveWorkflowCommand) ?? DBNull.Value }); parameters.Add(new SqlParameter { ParameterName = "@unlockInstance", SqlDbType = SqlDbType.Bit, Value = saveWorkflowCommand.UnlockInstance }); parameters.Add(new SqlParameter { ParameterName = "@suspensionStateChange", SqlDbType = SqlDbType.TinyInt, Value = GetSuspensionReason(saveWorkflowCommand, out suspensionReason, out suspensionExceptionName) }); parameters.Add(new SqlParameter { ParameterName = "@suspensionReason", SqlDbType = SqlDbType.NVarChar, Value = (object)suspensionReason ?? DBNull.Value }); parameters.Add(new SqlParameter { ParameterName = "@suspensionExceptionName", SqlDbType = SqlDbType.NVarChar, Size = 450, Value = (object)suspensionExceptionName ?? DBNull.Value }); parameters.Add(new SqlParameter { ParameterName = "@isCompleted", SqlDbType = SqlDbType.Bit, Value = saveWorkflowCommand.CompleteInstance }); parameters.Add(new SqlParameter { ParameterName = "@isReadyToRun", SqlDbType = SqlDbType.Bit, Value = IsReadyToRun(saveWorkflowCommand) }); parameters.Add(new SqlParameter { ParameterName = "@workflowHostType", SqlDbType = SqlDbType.UniqueIdentifier, Value = (object)GetWorkflowHostType(saveWorkflowCommand) ?? DBNull.Value }); parameters.Add(new SqlParameter { ParameterName = "@operationTimeout", SqlDbType = SqlDbType.Int, Value = (operationTimeout < Int32.MaxValue) ? Convert.ToInt32(operationTimeout) : Int32.MaxValue }); string parameterNames = null; if (base.Store.DatabaseVersion >= StoreUtilities.Version45) { string identityMetadataXml = SerializationUtilities.GetIdentityMetadataXml(saveWorkflowCommand); parameters.Add(new SqlParameter { ParameterName = "@identityMetadata", SqlDbType = SqlDbType.Xml, Value = (object)identityMetadataXml ?? DBNull.Value }); parameterNames = SaveWorkflowAsyncResult.storedProcedureParameters; } else { parameterNames = SaveWorkflowAsyncResult.storedProcedureParameters40; } commandTextBuilder.AppendLine(@"set nocount on set transaction isolation level read committed set xact_abort on begin transaction"); ExtractServiceDeploymentInformation(saveWorkflowCommand, commandTextBuilder, parameters); commandTextBuilder.AppendLine("declare @result int"); commandTextBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, "exec @result = {0}.[SaveInstance] {1} ;", SqlWorkflowInstanceStoreConstants.DefaultSchema, parameterNames)); commandTextBuilder.AppendLine("if (@result = 0)"); commandTextBuilder.AppendLine("begin"); SerializeAssociatedData(parameters, saveWorkflowCommand, commandTextBuilder); commandTextBuilder.AppendLine("commit transaction"); commandTextBuilder.AppendLine("end"); commandTextBuilder.AppendLine("else"); commandTextBuilder.AppendLine("rollback transaction"); this.commandText = commandTextBuilder.ToString(); } protected override string GetSqlCommandText() { return this.commandText; } protected override CommandType GetSqlCommandType() { return CommandType.Text; } protected override Exception ProcessSqlResult(SqlDataReader reader) { Exception exception = StoreUtilities.GetNextResultSet(base.InstancePersistenceCommand.Name, reader); if (exception == null) { SaveWorkflowCommand saveWorkflowCommand = base.InstancePersistenceCommand as SaveWorkflowCommand; InstanceLockTracking instanceLockTracking = (InstanceLockTracking)(base.InstancePersistenceContext.UserContext); if ((this.serviceDeploymentHash != Guid.Empty) && (this.serviceDeploymentId == 0)) { this.serviceDeploymentId = reader.GetInt64(1); PutServiceDeploymentId(); exception = StoreUtilities.GetNextResultSet(base.InstancePersistenceCommand.Name, reader); } if (exception == null) { if (!base.InstancePersistenceContext.InstanceView.IsBoundToLock) { long instanceVersion = reader.GetInt64(1); instanceLockTracking.TrackStoreLock(base.InstancePersistenceContext.InstanceView.InstanceId, instanceVersion, this.DependentTransaction); base.InstancePersistenceContext.BindAcquiredLock(instanceVersion); } if (saveWorkflowCommand.InstanceData.Count > 0) { base.InstancePersistenceContext.PersistedInstance(saveWorkflowCommand.InstanceData); } SaveWorkflowAsyncResult.UpdateKeyData(base.InstancePersistenceContext, saveWorkflowCommand); foreach (KeyValuePair property in saveWorkflowCommand.InstanceMetadataChanges) { base.InstancePersistenceContext.WroteInstanceMetadataValue(property.Key, property.Value); } if (saveWorkflowCommand.CompleteInstance) { base.InstancePersistenceContext.CompletedInstance(); } if (saveWorkflowCommand.UnlockInstance || saveWorkflowCommand.CompleteInstance) { instanceLockTracking.TrackStoreUnlock(this.DependentTransaction); base.InstancePersistenceContext.InstanceHandle.Free(); } } else if (exception is InstanceLockLostException) { base.InstancePersistenceContext.InstanceHandle.Free(); } } return exception; } static void AddSerializedProperty(ArraySegment source, SqlParameterCollection parameters, string parameterName) { int parameterSize = source.Count > 8000 ? source.Count : -1; object parameterValue = (parameterSize == -1 ? SaveWorkflowAsyncResult.GenerateByteArray(source) : source.Array) ?? (object)DBNull.Value; parameters.Add(new SqlParameter { ParameterName = parameterName, SqlDbType = SqlDbType.VarBinary, Size = parameterSize, Value = parameterValue }); } static byte[] GenerateByteArray(ArraySegment source) { if (source.Array != null) { byte[] destination = new byte[source.Count]; Buffer.BlockCopy(source.Array, 0, destination, 0, source.Count); return destination; } return null; } static string GetBlockingBookmarks(SaveWorkflowCommand saveWorkflowCommand) { string blockingBookmarks = null; InstanceValue binaryBlockingBookmarks; if (saveWorkflowCommand.InstanceData.TryGetValue(SqlWorkflowInstanceStoreConstants.BinaryBlockingBookmarksPropertyName, out binaryBlockingBookmarks)) { StringBuilder bookmarkListBuilder = new StringBuilder(SqlWorkflowInstanceStoreConstants.DefaultStringBuilderCapacity); IEnumerable activeBookmarks = binaryBlockingBookmarks.Value as IEnumerable; foreach (BookmarkInfo bookmarkInfo in activeBookmarks) { bookmarkListBuilder.AppendFormat(CultureInfo.InvariantCulture, "[{0}: {1}]{2}", bookmarkInfo.BookmarkName, bookmarkInfo.OwnerDisplayName, Environment.NewLine); } blockingBookmarks = bookmarkListBuilder.ToString(); } return blockingBookmarks; } static string GetExecutionStatus(SaveWorkflowCommand saveWorkflowCommand) { string executionStatus = null; InstanceValue executionStatusProperty; if (saveWorkflowCommand.InstanceData.TryGetValue(SqlWorkflowInstanceStoreConstants.StatusPropertyName, out executionStatusProperty)) { executionStatus = (string)executionStatusProperty.Value; } return executionStatus; } static Int64? GetPendingTimerExpiration(SaveWorkflowCommand saveWorkflowCommand) { InstanceValue pendingTimerExpirationPropertyValue; if (saveWorkflowCommand.InstanceData.TryGetValue(SqlWorkflowInstanceStoreConstants.PendingTimerExpirationPropertyName, out pendingTimerExpirationPropertyValue)) { DateTime pendingTimerExpiration = ((DateTime)pendingTimerExpirationPropertyValue.Value).ToUniversalTime(); TimeSpan datetimeOffset = pendingTimerExpiration - DateTime.UtcNow; return (Int64)datetimeOffset.TotalMilliseconds; } return null; } static SuspensionStateChange GetSuspensionReason(SaveWorkflowCommand saveWorkflowCommand, out string suspensionReason, out string suspensionExceptionName) { IDictionary instanceMetadataChanges = saveWorkflowCommand.InstanceMetadataChanges; SuspensionStateChange suspensionStateChange = SuspensionStateChange.NoChange; InstanceValue propertyValue; suspensionReason = null; suspensionExceptionName = null; if (instanceMetadataChanges.TryGetValue(WorkflowServiceNamespace.SuspendReason, out propertyValue)) { if (!propertyValue.IsDeletedValue) { suspensionStateChange = SuspensionStateChange.SuspendInstance; suspensionReason = (string)propertyValue.Value; if (instanceMetadataChanges.TryGetValue(WorkflowServiceNamespace.SuspendException, out propertyValue) && !propertyValue.IsDeletedValue) { suspensionExceptionName = ((Exception)propertyValue.Value).GetType().ToString(); } } else { suspensionStateChange = SuspensionStateChange.UnsuspendInstance; } } return suspensionStateChange; } static Guid? GetWorkflowHostType(SaveWorkflowCommand saveWorkflowCommand) { InstanceValue instanceValue; if (saveWorkflowCommand.InstanceMetadataChanges.TryGetValue(WorkflowNamespace.WorkflowHostType, out instanceValue)) { XName workflowHostType = instanceValue.Value as XName; if (workflowHostType == null) { throw FxTrace.Exception.AsError(new InstancePersistenceCommandException(SR.InvalidMetadataValue(WorkflowNamespace.WorkflowHostType, typeof(XName).Name))); } byte[] workflowHostTypeBuffer = Encoding.Unicode.GetBytes(((XName)instanceValue.Value).ToString()); return new Guid(HashHelper.ComputeHash(workflowHostTypeBuffer)); } return null; } static bool IsReadyToRun(SaveWorkflowCommand saveWorkflowCommand) { InstanceValue statusPropertyValue; if (saveWorkflowCommand.InstanceData.TryGetValue(SqlWorkflowInstanceStoreConstants.StatusPropertyName, out statusPropertyValue) && ((string)statusPropertyValue.Value) == SqlWorkflowInstanceStoreConstants.ExecutingStatusPropertyValue) { return true; } return false; } static void UpdateKeyData(InstancePersistenceContext context, SaveWorkflowCommand saveWorkflowCommand) { InstanceView instanceView = context.InstanceView; foreach (KeyValuePair> keyEntry in saveWorkflowCommand.InstanceKeysToAssociate) { if (!instanceView.InstanceKeys.ContainsKey(keyEntry.Key)) { context.AssociatedInstanceKey(keyEntry.Key); if (keyEntry.Value != null) { foreach (KeyValuePair property in keyEntry.Value) { context.WroteInstanceKeyMetadataValue(keyEntry.Key, property.Key, property.Value); } } } } foreach (Guid key in saveWorkflowCommand.InstanceKeysToComplete) { InstanceKeyView existingKeyView; if (instanceView.InstanceKeys.TryGetValue(key, out existingKeyView)) { if (existingKeyView.InstanceKeyState != InstanceKeyState.Completed) { context.CompletedInstanceKey(key); } } } foreach (Guid key in saveWorkflowCommand.InstanceKeysToFree) { InstanceKeyView existingKeyView; if (instanceView.InstanceKeys.TryGetValue(key, out existingKeyView)) { context.UnassociatedInstanceKey(key); } } foreach (KeyValuePair> keyEntry in saveWorkflowCommand.InstanceKeyMetadataChanges) { if (keyEntry.Value != null) { foreach (KeyValuePair property in keyEntry.Value) { context.WroteInstanceKeyMetadataValue(keyEntry.Key, property.Key, property.Value); } } } if (saveWorkflowCommand.CompleteInstance) { foreach (KeyValuePair instanceKeys in instanceView.InstanceKeys) { if (instanceKeys.Value != null) { if (instanceKeys.Value.InstanceKeyState == InstanceKeyState.Associated) { context.CompletedInstanceKey(instanceKeys.Key); } } } } } void ExtractServiceDeploymentInformation(SaveWorkflowCommand saveWorkflowCommand, StringBuilder commandTextBuilder, SqlParameterCollection parameters) { InstanceValue instanceValue; //Extract the activation parameters string serviceName = null; string serviceNamespace = null; string site = null; string relativeApplicationPath = null; string relativeServicePath = null; if (saveWorkflowCommand.InstanceMetadataChanges.TryGetValue(PersistenceMetadataNamespace.ActivationType, out instanceValue)) { if (PersistenceMetadataNamespace.ActivationTypes.WAS.Equals(instanceValue.Value)) { if (saveWorkflowCommand.InstanceMetadataChanges.TryGetValue(WorkflowServiceNamespace.Service, out instanceValue)) { serviceName = ((XName)instanceValue.Value).LocalName; serviceNamespace = ((XName)instanceValue.Value).Namespace.NamespaceName; } if (saveWorkflowCommand.InstanceMetadataChanges.TryGetValue(WorkflowServiceNamespace.SiteName, out instanceValue)) { site = (string)instanceValue.Value; } if (saveWorkflowCommand.InstanceMetadataChanges.TryGetValue(WorkflowServiceNamespace.RelativeApplicationPath, out instanceValue)) { relativeApplicationPath = (string)instanceValue.Value; } if (saveWorkflowCommand.InstanceMetadataChanges.TryGetValue(WorkflowServiceNamespace.RelativeServicePath, out instanceValue)) { relativeServicePath = (string)instanceValue.Value; } byte[] serviceDeploymentHashBuffer = Encoding.Unicode.GetBytes(string.Format(CultureInfo.InvariantCulture, "{0}#{1}#{2}#{3}#{4}", serviceName ?? string.Empty, serviceNamespace ?? string.Empty, site ?? string.Empty, relativeApplicationPath ?? string.Empty, relativeServicePath ?? string.Empty)); this.serviceDeploymentHash = new Guid(HashHelper.ComputeHash(serviceDeploymentHashBuffer)); //Get the service id has been seen before, get it from the cache GetServiceDeploymentId(); } else { throw FxTrace.Exception.AsError(new InstancePersistenceCommandException(SR.NonWASActivationNotSupported)); } } if ((this.serviceDeploymentHash != Guid.Empty) && (this.serviceDeploymentId == 0)) { //This is the first time we see this service deployment so we need to create a new entry for it before creating the instance commandTextBuilder.AppendLine("declare @serviceDeploymentId bigint"); commandTextBuilder.AppendLine(string.Format(CultureInfo.InvariantCulture, "exec {0}.[CreateServiceDeployment] {1} ;", SqlWorkflowInstanceStoreConstants.DefaultSchema, SaveWorkflowAsyncResult.createServiceDeploymentStoredProcedureParameters)); parameters.Add(new SqlParameter { ParameterName = "@serviceDeploymentHash", SqlDbType = SqlDbType.UniqueIdentifier, Value = this.serviceDeploymentHash }); parameters.Add(new SqlParameter { ParameterName = "@serviceName", Size = -1, SqlDbType = SqlDbType.NVarChar, Value = serviceName ?? (object)DBNull.Value }); parameters.Add(new SqlParameter { ParameterName = "@serviceNamespace", Size = -1, SqlDbType = SqlDbType.NVarChar, Value = serviceNamespace ?? (object)DBNull.Value }); parameters.Add(new SqlParameter { ParameterName = "@siteName", Size = -1, SqlDbType = SqlDbType.NVarChar, Value = site ?? (object)DBNull.Value }); parameters.Add(new SqlParameter { ParameterName = "@relativeServicePath", Size = -1, SqlDbType = SqlDbType.NVarChar, Value = relativeServicePath ?? (object)DBNull.Value }); parameters.Add(new SqlParameter { ParameterName = "@relativeApplicationPath", Size = -1, SqlDbType = SqlDbType.NVarChar, Value = relativeApplicationPath ?? (object)DBNull.Value }); } else { parameters.Add(new SqlParameter { ParameterName = "@serviceDeploymentId", SqlDbType = SqlDbType.BigInt, Value = (this.serviceDeploymentId != 0) ? (object)this.serviceDeploymentId : (object)DBNull.Value }); } } void GetServiceDeploymentId() { try { SaveWorkflowAsyncResult.serviceDeploymentIdsCacheLock.EnterReadLock(); SaveWorkflowAsyncResult.serviceDeploymentIdsCache.TryGetValue(this.serviceDeploymentHash, out this.serviceDeploymentId); } finally { SaveWorkflowAsyncResult.serviceDeploymentIdsCacheLock.ExitReadLock(); } } void PutServiceDeploymentId() { try { serviceDeploymentIdsCacheLock.EnterWriteLock(); serviceDeploymentIdsCache[this.serviceDeploymentHash] = this.serviceDeploymentId; } finally { serviceDeploymentIdsCacheLock.ExitWriteLock(); } } void SerializeAssociatedData(SqlParameterCollection parameters, SaveWorkflowCommand saveWorkflowCommand, StringBuilder commandTextBuilder) { if (saveWorkflowCommand.CompleteInstance && base.Store.InstanceCompletionAction == InstanceCompletionAction.DeleteAll) { parameters.Add(new SqlParameter { ParameterName = "@keysToAssociate", SqlDbType = SqlDbType.Xml, Value = DBNull.Value }); parameters.Add(new SqlParameter { ParameterName = "@singleKeyId", SqlDbType = SqlDbType.UniqueIdentifier, Value = DBNull.Value }); parameters.Add(new SqlParameter { ParameterName = "@keysToComplete", SqlDbType = SqlDbType.Xml, Value = DBNull.Value }); parameters.Add(new SqlParameter { ParameterName = "@keysToFree", SqlDbType = SqlDbType.Xml, Value = DBNull.Value }); parameters.Add(new SqlParameter { ParameterName = "@concatenatedKeyProperties", SqlDbType = SqlDbType.VarBinary, Value = DBNull.Value }); parameters.Add(new SqlParameter { ParameterName = "@primitiveDataProperties", SqlDbType = SqlDbType.VarBinary, Value = DBNull.Value }); parameters.Add(new SqlParameter { ParameterName = "@complexDataProperties", SqlDbType = SqlDbType.VarBinary, Value = DBNull.Value }); parameters.Add(new SqlParameter { ParameterName = "@writeOnlyPrimitiveDataProperties", SqlDbType = SqlDbType.VarBinary, Value = DBNull.Value }); parameters.Add(new SqlParameter { ParameterName = "@writeOnlyComplexDataProperties", SqlDbType = SqlDbType.VarBinary, Value = DBNull.Value }); parameters.Add(new SqlParameter { ParameterName = "@metadataProperties", SqlDbType = SqlDbType.VarBinary, Value = DBNull.Value }); parameters.Add(new SqlParameter { ParameterName = "@metadataIsConsistent", SqlDbType = SqlDbType.Bit, Value = DBNull.Value }); parameters.Add(new SqlParameter { ParameterName = "@encodingOption", SqlDbType = SqlDbType.TinyInt, Value = DBNull.Value }); parameters.Add(new SqlParameter { ParameterName = "@lastMachineRunOn", SqlDbType = SqlDbType.NVarChar, Value = DBNull.Value }); parameters.Add(new SqlParameter { ParameterName = "@executionStatus", SqlDbType = SqlDbType.NVarChar, Value = DBNull.Value }); parameters.Add(new SqlParameter { ParameterName = "@blockingBookmarks", SqlDbType = SqlDbType.NVarChar, Value = DBNull.Value }); return; } List keysToAssociate = CorrelationKey.BuildKeyList(saveWorkflowCommand.InstanceKeysToAssociate, base.Store.InstanceEncodingOption); List keysToComplete = CorrelationKey.BuildKeyList(saveWorkflowCommand.InstanceKeysToComplete); List keysToFree = CorrelationKey.BuildKeyList(saveWorkflowCommand.InstanceKeysToFree); ArraySegment[] dataProperties = SerializationUtilities.SerializePropertyBag(saveWorkflowCommand.InstanceData, base.Store.InstanceEncodingOption); ArraySegment metadataProperties = SerializationUtilities.SerializeMetadataPropertyBag(saveWorkflowCommand, base.InstancePersistenceContext, base.Store.InstanceEncodingOption); byte[] concatenatedKeyProperties = SerializationUtilities.CreateKeyBinaryBlob(keysToAssociate); bool metadataConsistency = (base.InstancePersistenceContext.InstanceView.InstanceMetadataConsistency == InstanceValueConsistency.None); bool singleKeyToAssociate = (keysToAssociate != null && keysToAssociate.Count == 1); parameters.Add(new SqlParameter { ParameterName = "@keysToAssociate", SqlDbType = SqlDbType.Xml, Value = singleKeyToAssociate ? DBNull.Value : SerializationUtilities.CreateCorrelationKeyXmlBlob(keysToAssociate) }); parameters.Add(new SqlParameter { ParameterName = "@singleKeyId", SqlDbType = SqlDbType.UniqueIdentifier, Value = singleKeyToAssociate ? keysToAssociate[0].KeyId : (object)DBNull.Value }); parameters.Add(new SqlParameter { ParameterName = "@keysToComplete", SqlDbType = SqlDbType.Xml, Value = SerializationUtilities.CreateCorrelationKeyXmlBlob(keysToComplete) }); parameters.Add(new SqlParameter { ParameterName = "@keysToFree", SqlDbType = SqlDbType.Xml, Value = SerializationUtilities.CreateCorrelationKeyXmlBlob(keysToFree) }); parameters.Add(new SqlParameter { ParameterName = "@concatenatedKeyProperties", SqlDbType = SqlDbType.VarBinary, Size = -1, Value = (object)concatenatedKeyProperties ?? DBNull.Value }); parameters.Add(new SqlParameter { ParameterName = "@metadataIsConsistent", SqlDbType = SqlDbType.Bit, Value = metadataConsistency }); parameters.Add(new SqlParameter { ParameterName = "@encodingOption", SqlDbType = SqlDbType.TinyInt, Value = base.Store.InstanceEncodingOption }); parameters.Add(new SqlParameter { ParameterName = "@lastMachineRunOn", SqlDbType = SqlDbType.NVarChar, Size = 450, Value = SqlWorkflowInstanceStoreConstants.MachineName }); parameters.Add(new SqlParameter { ParameterName = "@executionStatus", SqlDbType = SqlDbType.NVarChar, Size = 450, Value = GetExecutionStatus(saveWorkflowCommand) ?? (object)DBNull.Value }); parameters.Add(new SqlParameter { ParameterName = "@blockingBookmarks", SqlDbType = SqlDbType.NVarChar, Size = -1, Value = GetBlockingBookmarks(saveWorkflowCommand) ?? (object)DBNull.Value }); ArraySegment[] properties = { dataProperties[0], dataProperties[1], dataProperties[2], dataProperties[3], metadataProperties }; string[] dataPropertyParameters = { "@primitiveDataProperties", "@complexDataProperties", "@writeOnlyPrimitiveDataProperties", @"writeOnlyComplexDataProperties", "@metadataProperties" }; for (int i = 0; i < 5; i++) { SaveWorkflowAsyncResult.AddSerializedProperty(properties[i], parameters, dataPropertyParameters[i]); } this.SerializePromotedProperties(parameters, commandTextBuilder, saveWorkflowCommand); } void SerializePromotedProperties(SqlParameterCollection parameters, StringBuilder commandTextBuilder, SaveWorkflowCommand saveWorkflowCommand) { const int SqlVariantStartColumn = 1; const string promotionNameParameter = "@promotionName="; const string instanceIdParameter = "@instanceId="; int promotionNumber = 0; foreach (KeyValuePair, List>> promotion in base.Store.Promotions) { StringBuilder storedProcInvocationBuilder = new StringBuilder(SqlWorkflowInstanceStoreConstants.DefaultStringBuilderCapacity); int column = SqlVariantStartColumn; bool addPromotion = false; string promotionNameArgument = string.Format(CultureInfo.InvariantCulture, "@promotionName{0}", promotionNumber); string instanceIdArgument = string.Format(CultureInfo.InvariantCulture, "@instanceId{0}", promotionNumber); storedProcInvocationBuilder.Append(string.Format(CultureInfo.InvariantCulture, "exec {0}.[InsertPromotedProperties] ", SqlWorkflowInstanceStoreConstants.DefaultSchema)); storedProcInvocationBuilder.Append(promotionNameParameter); storedProcInvocationBuilder.Append(promotionNameArgument); storedProcInvocationBuilder.Append(","); storedProcInvocationBuilder.Append(instanceIdParameter); storedProcInvocationBuilder.Append(instanceIdArgument); foreach (XName name in promotion.Value.Item1) { InstanceValue propertyValue; if (saveWorkflowCommand.InstanceData.TryGetValue(name, out propertyValue)) { if (!SerializationUtilities.IsPropertyTypeSqlVariantCompatible(propertyValue)) { throw FxTrace.Exception.AsError(new InstancePersistenceException(SR.CannotPromoteAsSqlVariant(propertyValue.Value.GetType().ToString(), name.ToString()))); } string parameterName = string.Format(CultureInfo.InvariantCulture, "@value{0}=", column); string argumentName = string.Format(CultureInfo.InvariantCulture, "@value{0}_promotion{1}", column, promotionNumber); parameters.Add(new SqlParameter() { SqlDbType = SqlDbType.Variant, ParameterName = argumentName, Value = propertyValue.Value ?? DBNull.Value }); storedProcInvocationBuilder.Append(", "); storedProcInvocationBuilder.Append(parameterName); storedProcInvocationBuilder.Append(argumentName); addPromotion = true; } column++; } column = SqlVariantStartColumn + SqlWorkflowInstanceStoreConstants.MaximumPropertiesPerPromotion; foreach (XName name in promotion.Value.Item2) { InstanceValue propertyValue; IObjectSerializer serializer = ObjectSerializerFactory.GetObjectSerializer(base.Store.InstanceEncodingOption); if (saveWorkflowCommand.InstanceData.TryGetValue(name, out propertyValue)) { string parameterName = string.Format(CultureInfo.InvariantCulture, "@value{0}=", column); string argumentName = string.Format(CultureInfo.InvariantCulture, "@value{0}_promotion{1}", column, promotionNumber); SaveWorkflowAsyncResult.AddSerializedProperty(serializer.SerializeValue(propertyValue.Value), parameters, argumentName); storedProcInvocationBuilder.Append(", "); storedProcInvocationBuilder.Append(parameterName); storedProcInvocationBuilder.Append(argumentName); addPromotion = true; } column++; } if (addPromotion) { parameters.Add(new SqlParameter() { SqlDbType = SqlDbType.NVarChar, Size = 400, ParameterName = promotionNameArgument, Value = promotion.Key }); parameters.Add(new SqlParameter() { SqlDbType = SqlDbType.UniqueIdentifier, ParameterName = instanceIdArgument, Value = base.InstancePersistenceContext.InstanceView.InstanceId }); storedProcInvocationBuilder.Append(";"); commandTextBuilder.AppendLine(storedProcInvocationBuilder.ToString()); promotionNumber++; } } } } }