diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 019f56f..2b95506 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,7 @@ jobs: run: dotnet build --no-restore --configuration Release - name: Run unit tests - run: dotnet test --no-build --verbosity normal --configuration Release /p:CollectCoverage=true /p:Threshold=45 /p:ThresholdType=line /p:ThresholdStat=Average /p:CoverletOutput=./TestResults/ /p:ExcludeByAttribute="GeneratedCodeAttribute" + run: dotnet test --no-build --verbosity normal --configuration Release /p:CollectCoverage=true /p:Threshold=50 /p:ThresholdType=line /p:ThresholdStat=Average /p:CoverletOutput=./TestResults/ /p:ExcludeByAttribute="GeneratedCodeAttribute" publish-packages: name: Publish to GitHub Packages diff --git a/LogicBuilder.Workflow.ComponentModel.Serialization/ComponentModel/Compiler/ValidationErrorCollection.cs b/LogicBuilder.Workflow.ComponentModel.Serialization/ComponentModel/Compiler/ValidationErrorCollection.cs index dc21328..b5b52e7 100644 --- a/LogicBuilder.Workflow.ComponentModel.Serialization/ComponentModel/Compiler/ValidationErrorCollection.cs +++ b/LogicBuilder.Workflow.ComponentModel.Serialization/ComponentModel/Compiler/ValidationErrorCollection.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Linq; namespace LogicBuilder.Workflow.ComponentModel.Compiler { @@ -53,15 +54,7 @@ public bool HasErrors { get { - if (Count > 0) - { - foreach (ValidationError e in this) - { - if (e != null && !e.IsWarning) - return true; - } - } - return false; + return this.OfType().Any(e => (e?.IsWarning != true)); } } @@ -69,15 +62,7 @@ public bool HasWarnings { get { - if (Count > 0) - { - foreach (ValidationError e in this) - { - if (e != null && e.IsWarning) - return true; - } - } - return false; + return this.OfType().Any(e => e?.IsWarning == true); } } diff --git a/LogicBuilder.Workflow.ComponentModel.Serialization/ComponentModel/Serialization/ExtendedPropertyInfo.cs b/LogicBuilder.Workflow.ComponentModel.Serialization/ComponentModel/Serialization/ExtendedPropertyInfo.cs index 92239f5..055cd55 100644 --- a/LogicBuilder.Workflow.ComponentModel.Serialization/ComponentModel/Serialization/ExtendedPropertyInfo.cs +++ b/LogicBuilder.Workflow.ComponentModel.Serialization/ComponentModel/Serialization/ExtendedPropertyInfo.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Globalization; + using System.Linq; using System.Reflection; using System.Xml; @@ -17,11 +18,11 @@ internal sealed class ExtendedPropertyInfo : PropertyInfo { #region Members and Constructors - private PropertyInfo realPropertyInfo = null; - private GetValueHandler OnGetValue; - private SetValueHandler OnSetValue; - private GetQualifiedNameHandler OnGetXmlQualifiedName; - private WorkflowMarkupSerializationManager manager = null; + private readonly PropertyInfo realPropertyInfo = null; + private readonly GetValueHandler OnGetValue; + private readonly SetValueHandler OnSetValue; + private readonly GetQualifiedNameHandler OnGetXmlQualifiedName; + private readonly WorkflowMarkupSerializationManager manager = null; internal ExtendedPropertyInfo(PropertyInfo propertyInfo, GetValueHandler getValueHandler, SetValueHandler setValueHandler, GetQualifiedNameHandler qualifiedNameHandler) { @@ -115,11 +116,11 @@ public override void SetValue(object obj, object value, BindingFlags invokeAttr, OnSetValue?.Invoke(this, obj, value); } - public XmlQualifiedName GetXmlQualifiedName(WorkflowMarkupSerializationManager manager, out string prefix) + public XmlQualifiedName GetXmlQualifiedName(WorkflowMarkupSerializationManager managerLocal, out string prefix) { prefix = String.Empty; if (OnGetXmlQualifiedName != null) - return OnGetXmlQualifiedName(this, manager, out prefix); + return OnGetXmlQualifiedName(this, managerLocal, out prefix); else return null; } @@ -195,14 +196,9 @@ internal static bool IsExtendedProperty(WorkflowMarkupSerializationManager manag } internal static bool IsExtendedProperty(WorkflowMarkupSerializationManager manager, IList propInfos, XmlQualifiedName xmlQualifiedName) { - foreach (PropertyInfo propInfo in propInfos) + foreach (ExtendedPropertyInfo extendedProperty in propInfos.OfType()) { - ExtendedPropertyInfo extendedProperty = propInfo as ExtendedPropertyInfo; - if (extendedProperty == null) - continue; - - string prefix = String.Empty; - XmlQualifiedName qualifiedPropertyName = extendedProperty.GetXmlQualifiedName(manager, out prefix); + XmlQualifiedName qualifiedPropertyName = extendedProperty.GetXmlQualifiedName(manager, out _); if (qualifiedPropertyName.Name.Equals(xmlQualifiedName.Name, StringComparison.Ordinal) && qualifiedPropertyName.Namespace.Equals(xmlQualifiedName.Namespace, StringComparison.Ordinal)) { diff --git a/LogicBuilder.Workflow.ComponentModel.Serialization/ComponentModel/Serialization/MarkupExtensionSerializer.cs b/LogicBuilder.Workflow.ComponentModel.Serialization/ComponentModel/Serialization/MarkupExtensionSerializer.cs index a11f8ac..abcc234 100644 --- a/LogicBuilder.Workflow.ComponentModel.Serialization/ComponentModel/Serialization/MarkupExtensionSerializer.cs +++ b/LogicBuilder.Workflow.ComponentModel.Serialization/ComponentModel/Serialization/MarkupExtensionSerializer.cs @@ -6,6 +6,7 @@ using System.ComponentModel; using System.ComponentModel.Design.Serialization; using System.Diagnostics; + using System.Linq; using System.Reflection; using System.Text; using System.Xml; @@ -43,49 +44,46 @@ protected internal sealed override string SerializeToString(WorkflowMarkupSerial Dictionary constructorArguments = null; InstanceDescriptor instanceDescriptor = this.GetInstanceDescriptor(serializationManager, value); - if (instanceDescriptor != null) + if (instanceDescriptor != null && instanceDescriptor.MemberInfo is ConstructorInfo ctorInfo) { - if (instanceDescriptor.MemberInfo is ConstructorInfo ctorInfo) + ParameterInfo[] parameters = ctorInfo.GetParameters(); + if (parameters != null && parameters.Length == instanceDescriptor.Arguments.Count) { - ParameterInfo[] parameters = ctorInfo.GetParameters(); - if (parameters != null && parameters.Length == instanceDescriptor.Arguments.Count) + int i = 0; + foreach (object argValue in instanceDescriptor.Arguments) { - int i = 0; - foreach (object argValue in instanceDescriptor.Arguments) + constructorArguments ??= []; + // + if (argValue == null) + continue; + constructorArguments.Add(parameters[i].Name, parameters[i++].Name); + if (index++ > 0) + writer.WriteString(MarkupExtensionSerializer.CompactFormatPropertySeperator); + else + writer.WriteString(MarkupExtensionSerializer.CompactFormatTypeSeperator); + if (argValue.GetType() == typeof(string)) { - constructorArguments ??= []; - // - if (argValue == null) - continue; - constructorArguments.Add(parameters[i].Name, parameters[i++].Name); - if (index++ > 0) - writer.WriteString(MarkupExtensionSerializer.CompactFormatPropertySeperator); - else - writer.WriteString(MarkupExtensionSerializer.CompactFormatTypeSeperator); - if (argValue.GetType() == typeof(string)) - { - writer.WriteString(CreateEscapedValue(argValue as string)); - } - else if (argValue is System.Type) + writer.WriteString(CreateEscapedValue(argValue as string)); + } + else if (argValue is System.Type) + { + Type argType = argValue as Type; + if (argType?.Assembly != null) { - Type argType = argValue as Type; - if (argType?.Assembly != null) - { - XmlQualifiedName typeQualifiedName = serializationManager.GetXmlQualifiedName(argType, out _); - writer.WriteQualifiedName(XmlConvert.EncodeName(typeQualifiedName.Name), typeQualifiedName.Namespace); - } - else - { - writer.WriteString(argType?.FullName ?? string.Empty); - } + XmlQualifiedName typeQualifiedName = serializationManager.GetXmlQualifiedName(argType, out _); + writer.WriteQualifiedName(XmlConvert.EncodeName(typeQualifiedName.Name), typeQualifiedName.Namespace); } else { - string stringValue = base.SerializeToString(serializationManager, argValue); - if (stringValue != null) - writer.WriteString(stringValue); + writer.WriteString(argType?.FullName ?? string.Empty); } } + else + { + string stringValue = base.SerializeToString(serializationManager, argValue); + if (stringValue != null) + writer.WriteString(stringValue); + } } } } @@ -95,68 +93,74 @@ protected internal sealed override string SerializeToString(WorkflowMarkupSerial .. GetProperties(serializationManager, value), .. serializationManager.GetExtendedProperties(value), ]; - foreach (PropertyInfo serializableProperty in properties) + + foreach + ( + PropertyInfo serializableProperty in properties.Where + ( + p => Helpers.GetSerializationVisibility(p) != DesignerSerializationVisibility.Hidden + && p.CanRead + && p.GetValue(value, null) != null + ) + ) { - if (Helpers.GetSerializationVisibility(serializableProperty) != DesignerSerializationVisibility.Hidden && serializableProperty.CanRead && serializableProperty.GetValue(value, null) != null) + if (serializationManager.GetSerializer(serializableProperty.PropertyType, typeof(WorkflowMarkupSerializer)) is not WorkflowMarkupSerializer propSerializer) { - if (serializationManager.GetSerializer(serializableProperty.PropertyType, typeof(WorkflowMarkupSerializer)) is not WorkflowMarkupSerializer propSerializer) - { - serializationManager.ReportError(new WorkflowMarkupSerializationException(SR.GetString(SR.Error_SerializerNotAvailable, serializableProperty.PropertyType.FullName))); - continue; - } + serializationManager.ReportError(new WorkflowMarkupSerializationException(SR.GetString(SR.Error_SerializerNotAvailable, serializableProperty.PropertyType.FullName))); + continue; + } - if (constructorArguments != null) - { - object[] attributes = serializableProperty.GetCustomAttributes(typeof(ConstructorArgumentAttribute), false); - if (attributes.Length > 0 && constructorArguments.ContainsKey((attributes[0] as ConstructorArgumentAttribute).ArgumentName)) - // Skip this property, it has already been represented by a constructor parameter - continue; - } + if (constructorArguments != null) + { + object[] attributes = serializableProperty.GetCustomAttributes(typeof(ConstructorArgumentAttribute), false); + if (attributes.Length > 0 && constructorArguments.ContainsKey((attributes[0] as ConstructorArgumentAttribute).ArgumentName)) + // Skip this property, it has already been represented by a constructor parameter + continue; + } - //Get the property serializer so that we can convert the bind object to string - serializationManager.Context.Push(serializableProperty); - try + //Get the property serializer so that we can convert the bind object to string + serializationManager.Context.Push(serializableProperty); + try + { + object propValue = serializableProperty.GetValue(value, null); + if (propSerializer.ShouldSerializeValue(serializationManager, propValue)) { - object propValue = serializableProperty.GetValue(value, null); - if (propSerializer.ShouldSerializeValue(serializationManager, propValue)) + //We do not allow nested bind syntax + if (propSerializer.CanSerializeToString(serializationManager, propValue)) { - //We do not allow nested bind syntax - if (propSerializer.CanSerializeToString(serializationManager, propValue)) - { - if (index++ > 0) - writer.WriteString(MarkupExtensionSerializer.CompactFormatPropertySeperator); - else - writer.WriteString(MarkupExtensionSerializer.CompactFormatTypeSeperator); - writer.WriteString(serializableProperty.Name); - writer.WriteString(MarkupExtensionSerializer.CompactFormatNameValueSeperator); + if (index++ > 0) + writer.WriteString(MarkupExtensionSerializer.CompactFormatPropertySeperator); + else + writer.WriteString(MarkupExtensionSerializer.CompactFormatTypeSeperator); + writer.WriteString(serializableProperty.Name); + writer.WriteString(MarkupExtensionSerializer.CompactFormatNameValueSeperator); - if (propValue.GetType() == typeof(string)) - { - writer.WriteString(CreateEscapedValue(propValue as string)); - } - else - { - string stringValue = propSerializer.SerializeToString(serializationManager, propValue); - if (stringValue != null) - writer.WriteString(stringValue); - } + if (propValue.GetType() == typeof(string)) + { + writer.WriteString(CreateEscapedValue(propValue as string)); } else { - serializationManager.ReportError(new WorkflowMarkupSerializationException(SR.GetString(SR.Error_SerializerNoSerializeLogic, [serializableProperty.Name, value.GetType().FullName]))); + string stringValue = propSerializer.SerializeToString(serializationManager, propValue); + if (stringValue != null) + writer.WriteString(stringValue); } } + else + { + serializationManager.ReportError(new WorkflowMarkupSerializationException(SR.GetString(SR.Error_SerializerNoSerializeLogic, [serializableProperty.Name, value.GetType().FullName]))); + } } - catch - { - serializationManager.ReportError(new WorkflowMarkupSerializationException(SR.GetString(SR.Error_SerializerNoSerializeLogic, [serializableProperty.Name, value.GetType().FullName]))); - continue; - } - finally - { - Debug.Assert((PropertyInfo)serializationManager.Context.Current == serializableProperty, "Serializer did not remove an object it pushed into stack."); - serializationManager.Context.Pop(); - } + } + catch (Exception ex) + { + serializationManager.ReportError(new WorkflowMarkupSerializationException(SR.GetString(SR.Error_SerializerNoSerializeLogic, [serializableProperty.Name, value.GetType().FullName]), ex)); + continue; + } + finally + { + Debug.Assert((PropertyInfo)serializationManager.Context.Current == serializableProperty, "Serializer did not remove an object it pushed into stack."); + serializationManager.Context.Pop(); } } writer.WriteString(MarkupExtensionSerializer.CompactFormatEnd); diff --git a/LogicBuilder.Workflow.ComponentModel.Serialization/ComponentModel/Serialization/WorkflowMarkupSerializer.cs b/LogicBuilder.Workflow.ComponentModel.Serialization/ComponentModel/Serialization/WorkflowMarkupSerializer.cs index cc033a2..84676ed 100644 --- a/LogicBuilder.Workflow.ComponentModel.Serialization/ComponentModel/Serialization/WorkflowMarkupSerializer.cs +++ b/LogicBuilder.Workflow.ComponentModel.Serialization/ComponentModel/Serialization/WorkflowMarkupSerializer.cs @@ -14,6 +14,8 @@ using System.Text; using System.Xml; using LogicBuilder.ComponentModel.Design.Serialization; + using System.Diagnostics.CodeAnalysis; + using System.Linq; #region Class WorkflowMarkupSerializer //Main serialization class for persisting the XOML @@ -586,18 +588,16 @@ internal void SerializeContents(WorkflowMarkupSerializationManager serialization } } - foreach (PropertyInfo propInfo in properties) + foreach (PropertyInfo propInfo in properties.Where(p => p != null && !allProperties.ContainsKey(p.Name))) { // Do not serialize properties that have corresponding dynamic properties. - if (propInfo != null && !allProperties.ContainsKey(propInfo.Name)) - allProperties.Add(propInfo.Name, propInfo); + allProperties.Add(propInfo.Name, propInfo); } - foreach (EventInfo eventInfo in events) + foreach (EventInfo eventInfo in events.Where(e => e != null && !allProperties.ContainsKey(e.Name))) { // Do not serialize events that have corresponding dynamic properties. - if (eventInfo != null && !allProperties.ContainsKey(eventInfo.Name)) - allProperties.Add(eventInfo.Name, eventInfo); + allProperties.Add(eventInfo.Name, eventInfo); } using (ContentProperty contentProperty = new(serializationManager, serializer, obj)) @@ -1125,11 +1125,12 @@ protected internal virtual PropertyInfo[] GetProperties(WorkflowMarkupSerializat if (visibility == DesignerSerializationVisibility.Hidden) continue; - if (visibility != DesignerSerializationVisibility.Content && (!property.CanWrite || property.GetSetMethod() == null)) + if (visibility != DesignerSerializationVisibility.Content + && (!property.CanWrite || property.GetSetMethod() == null) + && (obj is not CodeObject || !typeof(ICollection).IsAssignableFrom(property.PropertyType))) { // work around for CodeObject which are ICollection needs to be serialized. - if (obj is not CodeObject || !typeof(ICollection).IsAssignableFrom(property.PropertyType)) - continue; + continue; } if (name == null || !name.Equals(property.Name)) @@ -1149,11 +1150,10 @@ protected internal virtual EventInfo[] GetEvents(WorkflowMarkupSerializationMana throw new ArgumentNullException("serializationManager"); List events = []; - foreach (EventInfo evt in obj.GetType().GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy)) + foreach (EventInfo evt + in obj.GetType().GetEvents(BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy) + .Where(e => Helpers.GetSerializationVisibility(e) != DesignerSerializationVisibility.Hidden)) { - if (Helpers.GetSerializationVisibility(evt) == DesignerSerializationVisibility.Hidden) - continue; - events.Add(evt); } @@ -1454,13 +1454,12 @@ private void DeserializeSimpleMember(WorkflowMarkupSerializationManager serializ { property.SetValue(obj, memberValue, null); } - else if (typeof(ICollection).IsAssignableFrom(memberValue.GetType())) + else if (typeof(ICollection).IsAssignableFrom(memberValue.GetType()) + && property.GetValue(obj, null) is ICollection propVal + && memberValue is ICollection deserializedValue) { - if (property.GetValue(obj, null) is ICollection propVal && memberValue is ICollection deserializedValue) - { - foreach (string content in deserializedValue) - propVal.Add(content); - } + foreach (string content in deserializedValue) + propVal.Add(content); } } catch (Exception e) @@ -1565,25 +1564,9 @@ private static PropertyInfo LookupProperty(IList properties, strin { if (properties != null && !string.IsNullOrEmpty(propertyName)) { - foreach (PropertyInfo property in properties) - { - if (property.Name == propertyName) - return property; - } + return properties.FirstOrDefault(p => p.Name == propertyName); } - return null; - } - private static EventInfo LookupEvent(IList events, string eventName) - { - if (events != null && !string.IsNullOrEmpty(eventName)) - { - foreach (EventInfo evt in events) - { - if (evt.Name == eventName) - return evt; - } - } return null; } #endregion @@ -1990,6 +1973,7 @@ private void RemoveEscapes(ref string value) #endregion #region ContentProperty Support + [ExcludeFromCodeCoverage] private class ContentProperty : IDisposable { private readonly WorkflowMarkupSerializationManager serializationManager; @@ -2042,13 +2026,10 @@ public ContentProperty(WorkflowMarkupSerializationManager serializationManager, this.contentProperty.SetValue(this.parentObject, contentPropertyValue, null); } - if (contentPropertyValue != null) + if (contentPropertyValue != null && reader != null) { - if (reader != null) - { - this.contentPropertySerializer.OnBeforeDeserialize(this.serializationManager, contentPropertyValue); - this.contentPropertySerializer.OnBeforeDeserializeContents(this.serializationManager, contentPropertyValue); - } + this.contentPropertySerializer.OnBeforeDeserialize(this.serializationManager, contentPropertyValue); + this.contentPropertySerializer.OnBeforeDeserializeContents(this.serializationManager, contentPropertyValue); } } catch (Exception e) @@ -2199,31 +2180,31 @@ internal void SetContents(IList contents) } } - private PropertyInfo GetContentProperty(WorkflowMarkupSerializationManager serializationManager, object parentObject) + private PropertyInfo GetContentProperty(WorkflowMarkupSerializationManager serializationManagerLocal, object parentObjectLocal) { - PropertyInfo contentProperty = null; + PropertyInfo contentPropertyLocal = null; string contentPropertyName = String.Empty; - object[] contentPropertyAttributes = parentObject.GetType().GetCustomAttributes(typeof(ContentPropertyAttribute), true); + object[] contentPropertyAttributes = parentObjectLocal.GetType().GetCustomAttributes(typeof(ContentPropertyAttribute), true); if (contentPropertyAttributes != null && contentPropertyAttributes.Length > 0) contentPropertyName = ((ContentPropertyAttribute)contentPropertyAttributes[0]).Name; if (!String.IsNullOrEmpty(contentPropertyName)) { - contentProperty = parentObject.GetType().GetProperty(contentPropertyName, BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.Public); - if (contentProperty == null) - serializationManager.ReportError(new WorkflowMarkupSerializationException(SR.GetString(SR.Error_ContentPropertyCouldNotBeFound, contentPropertyName, parentObject.GetType().FullName))); + contentPropertyLocal = parentObjectLocal.GetType().GetProperty(contentPropertyName, BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.Public); + if (contentPropertyLocal == null) + serializationManagerLocal.ReportError(new WorkflowMarkupSerializationException(SR.GetString(SR.Error_ContentPropertyCouldNotBeFound, contentPropertyName, parentObjectLocal.GetType().FullName))); } - return contentProperty; + return contentPropertyLocal; } } private struct ContentInfo(object content, int lineNumber, int linePosition) { - public int LineNumber = lineNumber; - public int LinePosition = linePosition; - public object Content = content; + public readonly int LineNumber = lineNumber; + public readonly int LinePosition = linePosition; + public readonly object Content = content; } #endregion @@ -2239,10 +2220,10 @@ internal static string EnsureMarkupExtensionTypeName(Type type) internal static string EnsureMarkupExtensionTypeName(XmlQualifiedName xmlQualifiedName) { string typeName = xmlQualifiedName.Name; - if (xmlQualifiedName.Namespace.Equals(StandardXomlKeys.Definitions_XmlNs, StringComparison.Ordinal)) + if (xmlQualifiedName.Namespace.Equals(StandardXomlKeys.Definitions_XmlNs, StringComparison.Ordinal) + && typeName.Equals(typeof(Array).Name, StringComparison.Ordinal)) { - if (typeName.Equals(typeof(Array).Name, StringComparison.Ordinal)) - typeName = typeof(ArrayExtension).Name; + typeName = typeof(ArrayExtension).Name; } return typeName; } @@ -2257,10 +2238,10 @@ private static bool IsMarkupExtension(Type type) private static bool IsMarkupExtension(XmlQualifiedName xmlQualifiedName) { bool markupExtension = false; - if (xmlQualifiedName.Namespace.Equals(StandardXomlKeys.Definitions_XmlNs, StringComparison.Ordinal)) + if (xmlQualifiedName.Namespace.Equals(StandardXomlKeys.Definitions_XmlNs, StringComparison.Ordinal) + && (xmlQualifiedName.Name.Equals(typeof(Array).Name) || string.Equals(xmlQualifiedName.Name, "Null", StringComparison.Ordinal) || string.Equals(xmlQualifiedName.Name, typeof(NullExtension).Name, StringComparison.Ordinal) || string.Equals(xmlQualifiedName.Name, "Type", StringComparison.Ordinal) || string.Equals(xmlQualifiedName.Name, typeof(TypeExtension).Name, StringComparison.Ordinal))) { - if (xmlQualifiedName.Name.Equals(typeof(Array).Name) || string.Equals(xmlQualifiedName.Name, "Null", StringComparison.Ordinal) || string.Equals(xmlQualifiedName.Name, typeof(NullExtension).Name, StringComparison.Ordinal) || string.Equals(xmlQualifiedName.Name, "Type", StringComparison.Ordinal) || string.Equals(xmlQualifiedName.Name, typeof(TypeExtension).Name, StringComparison.Ordinal)) - markupExtension = true; + markupExtension = true; } return markupExtension; } diff --git a/LogicBuilder.Workflow.ComponentModel.Serialization/ComponentModel/Serialization/WorkflowMarkupSerializerMapping.cs b/LogicBuilder.Workflow.ComponentModel.Serialization/ComponentModel/Serialization/WorkflowMarkupSerializerMapping.cs index cf80801..812d827 100644 --- a/LogicBuilder.Workflow.ComponentModel.Serialization/ComponentModel/Serialization/WorkflowMarkupSerializerMapping.cs +++ b/LogicBuilder.Workflow.ComponentModel.Serialization/ComponentModel/Serialization/WorkflowMarkupSerializerMapping.cs @@ -314,14 +314,12 @@ private static string GetPrefix(WorkflowMarkupSerializationManager manager, Asse object[] xmlnsPrefixes = assembly.GetCustomAttributes(typeof(XmlnsPrefixAttribute), true); if (xmlnsPrefixes != null) { - foreach (XmlnsPrefixAttribute xmlnsPrefix in xmlnsPrefixes.OfType()) - { - if (xmlnsPrefix.XmlNamespace.Equals(xmlNamespace, StringComparison.Ordinal)) - { - prefix = xmlnsPrefix.Prefix; - break; - } - } + XmlnsPrefixAttribute xmlnsPrefix = xmlnsPrefixes.OfType().FirstOrDefault + ( + p => p.XmlNamespace.Equals(xmlNamespace, StringComparison.Ordinal) + ); + if (xmlnsPrefix != null) + prefix = xmlnsPrefix.Prefix; } if (String.IsNullOrEmpty(prefix) || !IsNamespacePrefixUnique(prefix, manager.PrefixBasedMappings.Keys)) diff --git a/LogicBuilder.Workflow.ComponentModel.Serialization/ComponentModel/SynchronizationHandlesTypeConverter.cs b/LogicBuilder.Workflow.ComponentModel.Serialization/ComponentModel/SynchronizationHandlesTypeConverter.cs index b33551a..85bf8a3 100644 --- a/LogicBuilder.Workflow.ComponentModel.Serialization/ComponentModel/SynchronizationHandlesTypeConverter.cs +++ b/LogicBuilder.Workflow.ComponentModel.Serialization/ComponentModel/SynchronizationHandlesTypeConverter.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.ComponentModel; using System.Globalization; +using System.Linq; +using System.Text; namespace LogicBuilder.Workflow.ComponentModel { @@ -44,30 +46,29 @@ internal static string Stringify(ICollection synchronizationHandles) if (synchronizationHandles == null) return stringifiedValue; - foreach (string handle in synchronizationHandles) + StringBuilder stringList = new(); + foreach (string handle in synchronizationHandles.Where(h => h != null)) { - if (handle == null) - continue; - if (stringifiedValue != string.Empty) - stringifiedValue += ", "; - stringifiedValue += handle.Replace(",", "\\,"); + if (stringList.Length != 0) + stringList.Append(", "); + stringList.Append(handle.Replace(",", "\\,")); } - return stringifiedValue; + return stringList.ToString(); } internal static ICollection UnStringify(string stringifiedValue) { - ICollection synchronizationHandles = []; - stringifiedValue = stringifiedValue?.Replace("\\,", ">") ?? ""; - foreach (string handle in stringifiedValue.Split([',', '\r', '\n'], StringSplitOptions.RemoveEmptyEntries)) - { - string realHandle = handle.Trim().Replace('>', ','); - if (realHandle != string.Empty && !synchronizationHandles.Contains(realHandle)) - synchronizationHandles.Add(realHandle); - } + return (stringifiedValue?.Replace("\\,", ">") ?? "") + .Split([',', '\r', '\n'], StringSplitOptions.RemoveEmptyEntries) + .Aggregate(new List(), (list, handle) => + { + string realHandle = handle.Trim().Replace('>', ','); + if (realHandle != string.Empty && !list.Contains(realHandle)) + list.Add(realHandle); - return synchronizationHandles; + return list; + }); } } } diff --git a/Workflow.ComponentModel.Serialization.Tests/ComponentModel/Design/HelpersTest.cs b/Workflow.ComponentModel.Serialization.Tests/ComponentModel/Design/HelpersTest.cs index f5b6569..0f5d9b8 100644 --- a/Workflow.ComponentModel.Serialization.Tests/ComponentModel/Design/HelpersTest.cs +++ b/Workflow.ComponentModel.Serialization.Tests/ComponentModel/Design/HelpersTest.cs @@ -242,7 +242,7 @@ private class TestClassWithAttributes public string ContentProperty { get; set; } = string.Empty; [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] - public string HiddenField = string.Empty; + public readonly string HiddenField = string.Empty; public static void NormalMethod() { } } diff --git a/Workflow.ComponentModel.Serialization.Tests/ComponentModel/Serialization/MarkupExtensionSerializerTest.cs b/Workflow.ComponentModel.Serialization.Tests/ComponentModel/Serialization/MarkupExtensionSerializerTest.cs index 097176d..aaef6c0 100644 --- a/Workflow.ComponentModel.Serialization.Tests/ComponentModel/Serialization/MarkupExtensionSerializerTest.cs +++ b/Workflow.ComponentModel.Serialization.Tests/ComponentModel/Serialization/MarkupExtensionSerializerTest.cs @@ -94,7 +94,7 @@ public void SerializeToString_ThrowsArgumentNullException_WhenValueIsNull() { // Arrange var sb = new StringBuilder(); - var writer = XmlWriter.Create(sb); + using var writer = XmlWriter.Create(sb); _serializationManager.WorkflowMarkupStack.Push(writer); try @@ -105,7 +105,6 @@ public void SerializeToString_ThrowsArgumentNullException_WhenValueIsNull() finally { _serializationManager.WorkflowMarkupStack.Pop(); - writer.Dispose(); } }