1 //----------------------------------------------------------------------------- 2 // Copyright (c) Microsoft Corporation. All rights reserved. 3 //----------------------------------------------------------------------------- 4 5 namespace System.Activities.DurableInstancing 6 { 7 using System.Collections.Generic; 8 using System.Collections.ObjectModel; 9 using System.ComponentModel; 10 using System.Globalization; 11 using System.IO; 12 using System.IO.Compression; 13 using System.Runtime; 14 using System.Runtime.DurableInstancing; 15 using System.Runtime.Serialization; 16 using System.Linq; 17 using System.Xml.Linq; 18 using System.Text; 19 using System.Xml; 20 using System.Xml.Serialization; 21 22 static class SerializationUtilities 23 { CreateKeyBinaryBlob(List<CorrelationKey> correlationKeys)24 public static byte[] CreateKeyBinaryBlob(List<CorrelationKey> correlationKeys) 25 { 26 long memoryRequired = correlationKeys.Sum(i => i.BinaryData.Count); 27 byte[] concatenatedBlob = null; 28 29 if (memoryRequired > 0) 30 { 31 concatenatedBlob = new byte[memoryRequired]; 32 long insertLocation = 0; 33 34 foreach (CorrelationKey correlationKey in correlationKeys) 35 { 36 Buffer.BlockCopy(correlationKey.BinaryData.Array, 0, concatenatedBlob, Convert.ToInt32(insertLocation), Convert.ToInt32(correlationKey.BinaryData.Count)); 37 correlationKey.StartPosition = insertLocation; 38 insertLocation += correlationKey.BinaryData.Count; 39 } 40 } 41 42 return concatenatedBlob; 43 } 44 CreateCorrelationKeyXmlBlob(List<CorrelationKey> correlationKeys)45 public static object CreateCorrelationKeyXmlBlob(List<CorrelationKey> correlationKeys) 46 { 47 if (correlationKeys == null || correlationKeys.Count == 0) 48 { 49 return DBNull.Value; 50 } 51 52 StringBuilder stringBuilder = new StringBuilder(SqlWorkflowInstanceStoreConstants.DefaultStringBuilderCapacity); 53 54 using (XmlWriter xmlWriter = XmlWriter.Create(stringBuilder)) 55 { 56 xmlWriter.WriteStartElement("CorrelationKeys"); 57 58 foreach (CorrelationKey correlationKey in correlationKeys) 59 { 60 correlationKey.SerializeToXmlElement(xmlWriter); 61 } 62 63 xmlWriter.WriteEndElement(); 64 } 65 66 return stringBuilder.ToString(); 67 } 68 IsPropertyTypeSqlVariantCompatible(InstanceValue value)69 public static bool IsPropertyTypeSqlVariantCompatible(InstanceValue value) 70 { 71 if ((value.IsDeletedValue) || 72 (value.Value == null) || 73 (value.Value is string && ((string)value.Value).Length <= 4000) || 74 (value.Value is Guid) || 75 (value.Value is DateTime) || 76 (value.Value is int) || 77 (value.Value is double) || 78 (value.Value is float) || 79 (value.Value is long) || 80 (value.Value is short) || 81 (value.Value is byte) || 82 (value.Value is decimal && CanDecimalBeStoredAsSqlVariant((decimal)value.Value))) 83 { 84 return true; 85 } 86 else 87 { 88 return false; 89 } 90 } 91 DeserializeMetadataPropertyBag(byte[] serializedMetadataProperties, InstanceEncodingOption instanceEncodingOption)92 public static Dictionary<XName, InstanceValue> DeserializeMetadataPropertyBag(byte[] serializedMetadataProperties, InstanceEncodingOption instanceEncodingOption) 93 { 94 Dictionary<XName, InstanceValue> metadataProperties = new Dictionary<XName, InstanceValue>(); 95 96 if (serializedMetadataProperties != null) 97 { 98 IObjectSerializer serializer = ObjectSerializerFactory.GetObjectSerializer(instanceEncodingOption); 99 Dictionary<XName, object> propertyBag = serializer.DeserializePropertyBag(serializedMetadataProperties); 100 101 foreach (KeyValuePair<XName, object> property in propertyBag) 102 { 103 metadataProperties.Add(property.Key, new InstanceValue(property.Value)); 104 } 105 } 106 107 return metadataProperties; 108 } 109 SerializeMetadataPropertyBag(SaveWorkflowCommand saveWorkflowCommand, InstancePersistenceContext context, InstanceEncodingOption instanceEncodingOption)110 public static ArraySegment<byte> SerializeMetadataPropertyBag(SaveWorkflowCommand saveWorkflowCommand, 111 InstancePersistenceContext context, InstanceEncodingOption instanceEncodingOption) 112 { 113 IObjectSerializer serializer = ObjectSerializerFactory.GetObjectSerializer(instanceEncodingOption); 114 Dictionary<XName, object> propertyBagToSerialize = new Dictionary<XName, object>(); 115 116 if (context.InstanceView.InstanceMetadataConsistency == InstanceValueConsistency.None) 117 { 118 foreach (KeyValuePair<XName, InstanceValue> metadataProperty in context.InstanceView.InstanceMetadata) 119 { 120 if ((metadataProperty.Value.Options & InstanceValueOptions.WriteOnly) == 0) 121 { 122 propertyBagToSerialize.Add(metadataProperty.Key, metadataProperty.Value.Value); 123 } 124 } 125 } 126 127 foreach (KeyValuePair<XName, InstanceValue> metadataChange in saveWorkflowCommand.InstanceMetadataChanges) 128 { 129 if (metadataChange.Value.IsDeletedValue) 130 { 131 if (context.InstanceView.InstanceMetadataConsistency == InstanceValueConsistency.None) 132 { 133 propertyBagToSerialize.Remove(metadataChange.Key); 134 } 135 else 136 { 137 propertyBagToSerialize[metadataChange.Key] = new DeletedMetadataValue(); 138 } 139 } 140 else if ((metadataChange.Value.Options & InstanceValueOptions.WriteOnly) == 0) 141 { 142 propertyBagToSerialize[metadataChange.Key] = metadataChange.Value.Value; 143 } 144 } 145 146 if (propertyBagToSerialize.Count > 0) 147 { 148 return serializer.SerializePropertyBag(propertyBagToSerialize); 149 } 150 151 return new ArraySegment<byte>(); 152 } 153 SerializePropertyBag(IDictionary<XName, InstanceValue> properties, InstanceEncodingOption encodingOption)154 public static ArraySegment<byte>[] SerializePropertyBag(IDictionary<XName, InstanceValue> properties, InstanceEncodingOption encodingOption) 155 { 156 ArraySegment<byte>[] dataArrays = new ArraySegment<byte>[4]; 157 158 if (properties.Count > 0) 159 { 160 IObjectSerializer serializer = ObjectSerializerFactory.GetObjectSerializer(encodingOption); 161 XmlPropertyBag primitiveProperties = new XmlPropertyBag(); 162 XmlPropertyBag primitiveWriteOnlyProperties = new XmlPropertyBag(); 163 Dictionary<XName, object> complexProperties = new Dictionary<XName, object>(); 164 Dictionary<XName, object> complexWriteOnlyProperties = new Dictionary<XName, object>(); 165 Dictionary<XName, object>[] propertyBags = new Dictionary<XName, object>[] { primitiveProperties, complexProperties, 166 primitiveWriteOnlyProperties, complexWriteOnlyProperties }; 167 168 foreach (KeyValuePair<XName, InstanceValue> property in properties) 169 { 170 bool isComplex = (XmlPropertyBag.GetPrimitiveType(property.Value.Value) == PrimitiveType.Unavailable); 171 bool isWriteOnly = (property.Value.Options & InstanceValueOptions.WriteOnly) == InstanceValueOptions.WriteOnly; 172 int index = (isWriteOnly ? 2 : 0) + (isComplex ? 1 : 0); 173 propertyBags[index].Add(property.Key, property.Value.Value); 174 } 175 176 // Remove the properties that are already stored as individual columns from the serialized blob 177 primitiveWriteOnlyProperties.Remove(SqlWorkflowInstanceStoreConstants.StatusPropertyName); 178 primitiveWriteOnlyProperties.Remove(SqlWorkflowInstanceStoreConstants.LastUpdatePropertyName); 179 primitiveWriteOnlyProperties.Remove(SqlWorkflowInstanceStoreConstants.PendingTimerExpirationPropertyName); 180 181 complexWriteOnlyProperties.Remove(SqlWorkflowInstanceStoreConstants.BinaryBlockingBookmarksPropertyName); 182 183 for (int i = 0; i < propertyBags.Length; i++) 184 { 185 if (propertyBags[i].Count > 0) 186 { 187 if (propertyBags[i] is XmlPropertyBag) 188 { 189 dataArrays[i] = serializer.SerializeValue(propertyBags[i]); 190 } 191 else 192 { 193 dataArrays[i] = serializer.SerializePropertyBag(propertyBags[i]); 194 } 195 } 196 } 197 } 198 199 return dataArrays; 200 } 201 SerializeKeyMetadata(IDictionary<XName, InstanceValue> metadataProperties, InstanceEncodingOption encodingOption)202 public static ArraySegment<byte> SerializeKeyMetadata(IDictionary<XName, InstanceValue> metadataProperties, InstanceEncodingOption encodingOption) 203 { 204 if (metadataProperties != null && metadataProperties.Count > 0) 205 { 206 Dictionary<XName, object> propertyBag = new Dictionary<XName, object>(); 207 208 foreach (KeyValuePair<XName, InstanceValue> property in metadataProperties) 209 { 210 if ((property.Value.Options & InstanceValueOptions.WriteOnly) != InstanceValueOptions.WriteOnly) 211 { 212 propertyBag.Add(property.Key, property.Value.Value); 213 } 214 } 215 216 IObjectSerializer serializer = ObjectSerializerFactory.GetObjectSerializer(encodingOption); 217 return serializer.SerializePropertyBag(propertyBag); 218 } 219 220 return new ArraySegment<byte>(); 221 } 222 DeserializeKeyMetadata(byte[] serializedKeyMetadata, InstanceEncodingOption encodingOption)223 public static Dictionary<XName, InstanceValue> DeserializeKeyMetadata(byte[] serializedKeyMetadata, InstanceEncodingOption encodingOption) 224 { 225 return DeserializeMetadataPropertyBag(serializedKeyMetadata, encodingOption); 226 } 227 DeserializePropertyBag(byte[] primitiveDataProperties, byte[] complexDataProperties, InstanceEncodingOption encodingOption)228 public static Dictionary<XName, InstanceValue> DeserializePropertyBag(byte[] primitiveDataProperties, byte[] complexDataProperties, InstanceEncodingOption encodingOption) 229 { 230 IObjectSerializer serializer = ObjectSerializerFactory.GetObjectSerializer(encodingOption); 231 Dictionary<XName, InstanceValue> properties = new Dictionary<XName, InstanceValue>(); 232 Dictionary<XName, object>[] propertyBags = new Dictionary<XName, object>[2]; 233 234 if (primitiveDataProperties != null) 235 { 236 propertyBags[0] = (Dictionary<XName, object>)serializer.DeserializeValue(primitiveDataProperties); 237 } 238 239 if (complexDataProperties != null) 240 { 241 propertyBags[1] = serializer.DeserializePropertyBag(complexDataProperties); 242 } 243 244 foreach (Dictionary<XName, object> propertyBag in propertyBags) 245 { 246 if (propertyBag != null) 247 { 248 foreach (KeyValuePair<XName, object> property in propertyBag) 249 { 250 properties.Add(property.Key, new InstanceValue(property.Value)); 251 } 252 } 253 } 254 255 return properties; 256 } 257 CanDecimalBeStoredAsSqlVariant(decimal value)258 static bool CanDecimalBeStoredAsSqlVariant(decimal value) 259 { 260 string decimalAsString = value.ToString("G", CultureInfo.InvariantCulture); 261 return ((decimalAsString.Length - decimalAsString.IndexOf(".", StringComparison.Ordinal)) - 1 <= 18); 262 } 263 GetIdentityHash(WorkflowIdentity id)264 static Guid GetIdentityHash(WorkflowIdentity id) 265 { 266 byte[] identityHashBuffer = Encoding.Unicode.GetBytes(id.ToString()); 267 return new Guid(HashHelper.ComputeHash(identityHashBuffer)); 268 } 269 GetIdentityAnyRevisionFilterHash(WorkflowIdentity id)270 static Guid GetIdentityAnyRevisionFilterHash(WorkflowIdentity id) 271 { 272 if (id.Version != null) 273 { 274 Version version; 275 if (id.Version.Build >= 0) 276 { 277 version = new Version(id.Version.Major, id.Version.Minor, id.Version.Build); 278 } 279 else 280 { 281 version = new Version(id.Version.Minor, id.Version.Minor); 282 } 283 284 return GetIdentityHash(new WorkflowIdentity(id.Name, version, id.Package)); 285 } 286 else 287 { 288 return GetIdentityHash(id); 289 } 290 } 291 GetIdentityMetadataXml(InstancePersistenceCommand command)292 public static string GetIdentityMetadataXml(InstancePersistenceCommand command) 293 { 294 StringBuilder stringBuilder = new StringBuilder(512); 295 Guid idHash = Guid.Empty; 296 Guid idAnyRevisionHash = Guid.Empty; 297 int workflowIdentityFilter = (int)WorkflowIdentityFilter.Exact; 298 299 IList<WorkflowIdentity> identityCollection = null; 300 301 if (command is CreateWorkflowOwnerWithIdentityCommand) 302 { 303 InstanceValue instanceValueIdentityCollection = null; 304 CreateWorkflowOwnerWithIdentityCommand ownerCommand = command as CreateWorkflowOwnerWithIdentityCommand; 305 306 if (ownerCommand.InstanceOwnerMetadata.TryGetValue(Workflow45Namespace.DefinitionIdentities, out instanceValueIdentityCollection)) 307 { 308 if (instanceValueIdentityCollection.Value != null) 309 { 310 identityCollection = instanceValueIdentityCollection.Value as IList<WorkflowIdentity>; 311 if (identityCollection == null) 312 { 313 string typeName = typeof(IList<>).Name.Replace("`1", "<" + typeof(WorkflowIdentity).Name + ">"); 314 throw FxTrace.Exception.AsError(new InstancePersistenceCommandException(SR.InvalidMetadataValue(Workflow45Namespace.DefinitionIdentities, typeName))); 315 } 316 } 317 } 318 319 InstanceValue instanceValue = null; 320 if (ownerCommand.InstanceOwnerMetadata.TryGetValue(Workflow45Namespace.DefinitionIdentityFilter, out instanceValue)) 321 { 322 if (instanceValue.Value != null) 323 { 324 if (instanceValue.Value is WorkflowIdentityFilter) 325 { 326 workflowIdentityFilter = (int)instanceValue.Value; 327 } 328 else 329 { 330 workflowIdentityFilter = -1; 331 } 332 333 if (workflowIdentityFilter != (int)WorkflowIdentityFilter.Exact && 334 workflowIdentityFilter != (int)WorkflowIdentityFilter.Any && 335 workflowIdentityFilter != (int)WorkflowIdentityFilter.AnyRevision) 336 { 337 throw FxTrace.Exception.AsError(new InstancePersistenceCommandException(SR.InvalidMetadataValue(Workflow45Namespace.DefinitionIdentityFilter, typeof(WorkflowIdentityFilter).Name))); 338 } 339 } 340 } 341 } 342 else if (command is SaveWorkflowCommand) 343 { 344 InstanceValue instanceValue = null; 345 SaveWorkflowCommand saveCommand = command as SaveWorkflowCommand; 346 if (saveCommand.InstanceMetadataChanges.TryGetValue(Workflow45Namespace.DefinitionIdentity, out instanceValue)) 347 { 348 if (!instanceValue.IsDeletedValue && instanceValue.Value != null) 349 { 350 identityCollection = new Collection<WorkflowIdentity>(); 351 if (!(instanceValue.Value is WorkflowIdentity)) 352 { 353 throw FxTrace.Exception.AsError(new InstancePersistenceCommandException(SR.InvalidMetadataValue(Workflow45Namespace.DefinitionIdentity, typeof(WorkflowIdentity).Name))); 354 } 355 356 identityCollection.Add((WorkflowIdentity)instanceValue.Value); 357 } 358 } 359 else 360 { 361 // If identity isn't specified, we preserve the instance's existing identity 362 return null; 363 } 364 } 365 else 366 { 367 return null; 368 } 369 370 if (identityCollection == null) 371 { 372 // Assume NULL Identity 373 identityCollection = new Collection<WorkflowIdentity>(); 374 identityCollection.Add(null); 375 } 376 377 using (XmlWriter xmlWriter = XmlWriter.Create(stringBuilder)) 378 { 379 xmlWriter.WriteStartDocument(); 380 xmlWriter.WriteStartElement("IdentityMetadata"); 381 382 // Write the Identity Collection 383 xmlWriter.WriteStartElement("IdentityCollection"); 384 foreach (WorkflowIdentity id in identityCollection) 385 { 386 xmlWriter.WriteStartElement("Identity"); 387 388 if (id == null) 389 { 390 xmlWriter.WriteElementString("DefinitionIdentityHash", Guid.Empty.ToString()); 391 xmlWriter.WriteElementString("DefinitionIdentityAnyRevisionHash", Guid.Empty.ToString()); 392 } 393 else 394 { 395 idHash = GetIdentityHash(id); 396 idAnyRevisionHash = GetIdentityAnyRevisionFilterHash(id); 397 398 xmlWriter.WriteElementString("DefinitionIdentityHash", idHash.ToString()); 399 xmlWriter.WriteElementString("DefinitionIdentityAnyRevisionHash", idAnyRevisionHash.ToString()); 400 xmlWriter.WriteElementString("Name", id.Name); 401 402 if (id.Package != null) 403 { 404 xmlWriter.WriteElementString("Package", id.Package); 405 } 406 407 if (id.Version != null) 408 { 409 xmlWriter.WriteElementString("Major", id.Version.Major.ToString(CultureInfo.InvariantCulture)); 410 xmlWriter.WriteElementString("Minor", id.Version.Minor.ToString(CultureInfo.InvariantCulture)); 411 if (id.Version.Build >= 0) 412 { 413 xmlWriter.WriteElementString("Build", id.Version.Build.ToString(CultureInfo.InvariantCulture)); 414 if (id.Version.Revision >= 0) 415 { 416 xmlWriter.WriteElementString("Revision", id.Version.Revision.ToString(CultureInfo.InvariantCulture)); 417 } 418 } 419 } 420 } 421 422 xmlWriter.WriteEndElement(); 423 } 424 xmlWriter.WriteEndElement(); 425 426 // Write the WorkflowIdentityFilter 427 xmlWriter.WriteElementString("WorkflowIdentityFilter", workflowIdentityFilter.ToString(CultureInfo.InvariantCulture)); 428 429 xmlWriter.WriteEndElement(); 430 } 431 return stringBuilder.ToString(); 432 } 433 434 } 435 } 436