profile
viewpoint
If you are wondering where the data of this site comes from, please visit https://api.github.com/users/drewnoakes/events. GitMemory does not store any data, but only uses NGINX to cache data for a period of time. The idea behind GitMemory is simply to give users a better reading experience.
Drew Noakes drewnoakes @Microsoft Melbourne, Australia

drewnoakes/dependency-analyser 16

Graphical .NET assembly dependency analyser

drewnoakes/adobe-xmp-core 6

Reconstructed history of Adobe's Java XMPCore library from Maven sources

deckchair-technicians/toshtogo 4

An asynchronous job management framework

deckchair-technicians/vice 3

Prismatic schema extensions. Coercions attached to schemas. Use schema as Midje checkers...

andysturrock/cassettej 2

Partial Java port of the .NET content addressable storage library Cassette

drewnoakes/attached-collections-sample 2

An example of extending Visual Studio's Solution Explorer via the attached collections APIs

drewnoakes/beep 2

Machine that goes ping, for your command line

drewnoakes/biggus-griddus-demo 1

Demonstrates usage of the biggus-griddus library.

drewnoakes/Colorful.Console 1

Style your C# console output!

drewnoakes/corefx 1

This repo contains the .NET Core foundational libraries, called CoreFX. It includes classes for collections, file systems, console, XML, async and many others. We welcome contributions.

push eventdotnet/roslyn-analyzers

Jonathon Marolf

commit sha 79d72ceed2109d4560873f03a85f3f040572ec42

improve perf scripts

view details

push time in 2 minutes

delete branch dotnet/roslyn-analyzers

delete branch : merges/release/6.0.1xx-preview7-to-release/6.0.1xx

delete time in 9 minutes

delete branch dotnet/roslyn-analyzers

delete branch : merges/release/6.0.1xx-preview6-to-release/6.0.1xx-preview7

delete time in 9 minutes

create barnchdotnet/roslyn-analyzers

branch : merges/2.9.x-to-main

created branch time in 9 minutes

delete branch dotnet/roslyn-analyzers

delete branch : merges/2.9.x-to-main

delete time in 9 minutes

create barnchdotnet/project-system

branch : merges/dev16.11.x-to-main

created branch time in 10 minutes

delete branch dotnet/project-system

delete branch : merges/dev16.11.x-to-main

delete time in 10 minutes

create barnchdotnet/project-system

branch : merges/dev16.10.x-to-dev16.11.x

created branch time in 10 minutes

delete branch dotnet/project-system

delete branch : merges/dev16.10.x-to-dev16.11.x

delete time in 10 minutes

create barnchdotnet/project-system

branch : merges/dev16.9.x-to-dev16.10.x

created branch time in 10 minutes

delete branch dotnet/project-system

delete branch : merges/dev16.9.x-to-dev16.10.x

delete time in 10 minutes

create barnchdotnet/project-system

branch : merges/dev16.8.x-to-dev16.9.x

created branch time in 11 minutes

delete branch dotnet/project-system

delete branch : merges/dev16.8.x-to-dev16.9.x

delete time in 11 minutes

delete branch dotnet/project-system

delete branch : merges/dev16.0.x-to-dev16.7.x

delete time in 11 minutes

create barnchdotnet/project-system

branch : merges/dev16.0.x-to-dev16.7.x

created branch time in 11 minutes

create barnchdotnet/project-system

branch : merges/dev15.9.x-to-dev16.0.x

created branch time in 11 minutes

delete branch dotnet/project-system

delete branch : merges/dev15.9.x-to-dev16.0.x

delete time in 11 minutes

create barnchdotnet/project-system

branch : merges/dev15.0.x-to-dev15.9.x

created branch time in 11 minutes

delete branch dotnet/project-system

delete branch : merges/dev15.0.x-to-dev15.9.x

delete time in 11 minutes

Pull request review commentdotnet/roslyn-analyzers

Analyzer to detect ReqeuiresPreviewFeatures attribute

+// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.++using System.Collections.Concurrent;+using System.Collections.Immutable;+using System.Linq;+using Analyzer.Utilities;+using Analyzer.Utilities.Extensions;+using Microsoft.CodeAnalysis;+using Microsoft.CodeAnalysis.Diagnostics;+using Microsoft.CodeAnalysis.Operations;++namespace Microsoft.NetCore.Analyzers.Runtime+{+    /// <summary>+    /// Detect the use of [RequiresPreviewFeatures] in assemblies that have not opted into preview features+    /// </summary>+    [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]+    public sealed class DetectPreviewFeatureAnalyzer : DiagnosticAnalyzer+    {+        internal const string RuleId = "CA2250";+        private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.DetectPreviewFeaturesTitle), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources));+        private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.DetectPreviewFeaturesMessage), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources));+        private static readonly LocalizableString s_staticAbstractMessage = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.StaticAndAbstractRequiresPreviewFeatures), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources));+        private static readonly LocalizableString s_implementsEmptyPreviewInterface = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.ImplementsEmptyPreviewInterface), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources));+        private static readonly LocalizableString s_localizableDescription = new LocalizableResourceString(nameof(MicrosoftNetCoreAnalyzersResources.DetectPreviewFeaturesDescription), MicrosoftNetCoreAnalyzersResources.ResourceManager, typeof(MicrosoftNetCoreAnalyzersResources));+        private static readonly ImmutableArray<SymbolKind> s_symbols = ImmutableArray.Create(SymbolKind.NamedType, SymbolKind.Method, SymbolKind.Property, SymbolKind.Field, SymbolKind.Event);++#pragma warning disable RS0030 // The symbol 'DiagnosticDescriptor.DiagnosticDescriptor.#ctor' is banned in this project: Use 'DiagnosticDescriptorHelper.Create' instead+        internal static DiagnosticDescriptor GeneralPreviewFeatureAttributeRule = new(RuleId,+                                                                                      s_localizableTitle,+                                                                                      s_localizableMessage,+                                                                                      DiagnosticCategory.Usage,+                                                                                      DiagnosticSeverity.Error,+                                                                                      isEnabledByDefault: true,+                                                                                      s_localizableDescription,+                                                                                      helpLinkUri: @" https://aka.ms/dotnet-warnings/preview-features");++        internal static DiagnosticDescriptor StaticAbstractIsPreviewFeatureRule = new(RuleId,+                                                                                      s_localizableTitle,+                                                                                      s_staticAbstractMessage,+                                                                                      DiagnosticCategory.Usage,+                                                                                      DiagnosticSeverity.Error,+                                                                                      isEnabledByDefault: true,+                                                                                      s_localizableDescription,+                                                                                      helpLinkUri: @" https://aka.ms/dotnet-warnings/preview-features");+        internal static DiagnosticDescriptor ImplementsEmptyPreviewInterfaceRule = new(RuleId,+                                                                                      s_localizableTitle,+                                                                                      s_implementsEmptyPreviewInterface,+                                                                                      DiagnosticCategory.Usage,+                                                                                      DiagnosticSeverity.Error,+                                                                                      isEnabledByDefault: true,+                                                                                      s_localizableDescription,+                                                                                      helpLinkUri: @" https://aka.ms/dotnet-warnings/preview-features");++#pragma warning restore RS0030+        private const string RequiresPreviewFeaturesAttribute = nameof(RequiresPreviewFeaturesAttribute);++        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(GeneralPreviewFeatureAttributeRule, StaticAbstractIsPreviewFeatureRule, ImplementsEmptyPreviewInterfaceRule);++        private enum PreviewFeatureUsageType+        {+            General = 0,+            StaticAbstract,+            EmptyInterface+        }++        public override void Initialize(AnalysisContext context)+        {+            context.EnableConcurrentExecution();+            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);+            context.RegisterCompilationStartAction(context =>+            {+                Compilation compilation = context.Compilation;+                IAssemblySymbol? thisAssembly = compilation.Assembly;+                ImmutableArray<AttributeData> attributes = thisAssembly.GetAttributes();++                AttributeData? attribute = attributes.FirstOrDefault(x => x.AttributeClass.Name == RequiresPreviewFeaturesAttribute);+                if (!(attribute is null))+                {+                    // This assembly has enabled preview attributes.+                    return;+                }++                ConcurrentDictionary<ISymbol, bool> requiresPreviewFeaturesSymbols = new ConcurrentDictionary<ISymbol, bool>();+                ConcurrentDictionary<ISymbol, PreviewFeatureUsageType> requiresPreviewFeaturesSymbolsToUsageType = new ConcurrentDictionary<ISymbol, PreviewFeatureUsageType>();++                if (context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeCompilerServicesRuntimeFeature, out INamedTypeSymbol? runtimeFeatureType))+                {+                    if (runtimeFeatureType != null)+                    {+                        HasPreviewAttribute(runtimeFeatureType, requiresPreviewFeaturesSymbols);+                    }+                }++                // Handle user side invocation/references to preview features+                context.RegisterOperationAction(context => BuildSymbolInformationFromOperations(context, requiresPreviewFeaturesSymbols),+                    OperationKind.Invocation,+                    OperationKind.ObjectCreation,+                    OperationKind.PropertyReference,+                    OperationKind.FieldReference,+                    OperationKind.DelegateCreation,+                    OperationKind.EventReference,+                    OperationKind.Unary,+                    OperationKind.Binary,+                    OperationKind.ArrayCreation,+                    OperationKind.CatchClause+                    );++                // Handle library side definitions of preview features+                context.RegisterSymbolAction(context => AnalyzeSymbol(context, requiresPreviewFeaturesSymbols, requiresPreviewFeaturesSymbolsToUsageType, runtimeFeatureType), s_symbols);+            });+        }++        private static bool TypeSymbolHasAttribute(ITypeSymbol symbol, ConcurrentDictionary<ISymbol, bool> requiresPreviewFeaturesSymbols, ConcurrentDictionary<ISymbol, PreviewFeatureUsageType> requiresPreviewFeaturesSymbolsToUsageType)+        {+            ImmutableArray<INamedTypeSymbol> interfaces = symbol.Interfaces;+            foreach (INamedTypeSymbol? anInterface in interfaces)+            {+                var interfaceMembers = anInterface.GetMembers();+                if (interfaceMembers.Length == 0)+                {+                    // Only tag empty interfaces to prevent breaking changes in the future+                    requiresPreviewFeaturesSymbolsToUsageType.GetOrAdd(symbol, PreviewFeatureUsageType.EmptyInterface);+                    return true;+                }+            }++            INamedTypeSymbol? baseType = symbol.BaseType;+            if (baseType != null)+            {+                return HasPreviewAttribute(baseType, requiresPreviewFeaturesSymbols);+            }++            return false;+        }++        private static bool ContainingTypeIsPreview(ISymbol symbol, ConcurrentDictionary<ISymbol, bool> requiresPreviewFeaturesSymbols)+        {+            INamedTypeSymbol? containingType = symbol.ContainingType;+            if (containingType != null)+            {+                return HasPreviewAttribute(containingType, requiresPreviewFeaturesSymbols);+            }++            return false;+        }++        private static bool SymbolIsStaticAndAbstract(ISymbol symbol, ConcurrentDictionary<ISymbol, bool> requiresPreviewFeaturesSymbols)+        {+            bool isStaticAndAbstract = symbol.IsStatic && symbol.IsAbstract;++            return isStaticAndAbstract && !ContainingTypeIsPreview(symbol, requiresPreviewFeaturesSymbols);+        }++        private static bool PropertyOrMethodSymbolHasAttribute(ISymbol propertyOrMethodSymbol, ConcurrentDictionary<ISymbol, bool> requiresPreviewFeaturesSymbols, ConcurrentDictionary<ISymbol, PreviewFeatureUsageType> requiresPreviewFeaturesSymbolsToUsageType, INamedTypeSymbol? runtimeFeatureType)+        {+            if (SymbolIsStaticAndAbstract(propertyOrMethodSymbol, requiresPreviewFeaturesSymbols))+            {+                if (runtimeFeatureType != null && HasPreviewAttribute(runtimeFeatureType, requiresPreviewFeaturesSymbols))+                {+                    requiresPreviewFeaturesSymbolsToUsageType.GetOrAdd(propertyOrMethodSymbol, PreviewFeatureUsageType.StaticAbstract);+                    return true;+                }+            }++            if (propertyOrMethodSymbol.IsImplementationOfAnyImplicitInterfaceMember(out ISymbol baseInterfaceMember))+            {+                if (HasPreviewAttribute(baseInterfaceMember, requiresPreviewFeaturesSymbols) || ContainingTypeIsPreview(baseInterfaceMember, requiresPreviewFeaturesSymbols))+                {+                    return true;+                }+            }++            if (propertyOrMethodSymbol.IsOverride)+            {+                ISymbol? overridden = propertyOrMethodSymbol.GetOverriddenMember();+                if (overridden != null && HasPreviewAttribute(overridden, requiresPreviewFeaturesSymbols))+                {+                    return true;+                }+            }++            return false;+        }++        private static void AnalyzeSymbol(SymbolAnalysisContext context, ConcurrentDictionary<ISymbol, bool> requiresPreviewFeaturesSymbols, ConcurrentDictionary<ISymbol, PreviewFeatureUsageType> requiresPreviewFeaturesSymbolsToUsageType, INamedTypeSymbol? runtimeFeatureType)+        {+            ISymbol symbol = context.Symbol;+            if (symbol == null)+            {+                return;+            }++            // Don't report diagnostics on types that are marked as Preview+            if (HasPreviewAttribute(symbol, requiresPreviewFeaturesSymbols))+            {+                return;+            }++            bool reportDiagnostic = false;++            // Look for attributes on base classes that this type may extend+            if (symbol is ITypeSymbol typeSymbol)+            {+                reportDiagnostic = TypeSymbolHasAttribute(typeSymbol, requiresPreviewFeaturesSymbols, requiresPreviewFeaturesSymbolsToUsageType);+            }++            if (!reportDiagnostic)+            {+                if (symbol is IMethodSymbol || symbol is IPropertySymbol)+                {+                    reportDiagnostic = PropertyOrMethodSymbolHasAttribute(symbol, requiresPreviewFeaturesSymbols, requiresPreviewFeaturesSymbolsToUsageType, runtimeFeatureType);+                }+            }++            if (reportDiagnostic)+            {+                if (requiresPreviewFeaturesSymbolsToUsageType.TryGetValue(symbol, out PreviewFeatureUsageType usageType))+                {+                    if (usageType == PreviewFeatureUsageType.StaticAbstract)+                    {+                        context.ReportDiagnostic(symbol.CreateDiagnostic(StaticAbstractIsPreviewFeatureRule));+                    }+                    else if (usageType == PreviewFeatureUsageType.EmptyInterface)+                    {+                        context.ReportDiagnostic(symbol.CreateDiagnostic(ImplementsEmptyPreviewInterfaceRule, symbol.Name));+                    }+                }+                else+                {+                    context.ReportDiagnostic(symbol.CreateDiagnostic(GeneralPreviewFeatureAttributeRule, symbol.Name));+                }+            }+        }++        private static void BuildSymbolInformationFromOperations(OperationAnalysisContext context, ConcurrentDictionary<ISymbol, bool> requiresPreviewFeaturesSymbols)+        {+            IOperation operation = context.Operation;+            if (OperationUsesPreviewFeatures(operation, requiresPreviewFeaturesSymbols, out ISymbol? symbol))+            {+                if (operation is ICatchClauseOperation catchClauseOperation)+                {+                    operation = catchClauseOperation.ExceptionDeclarationOrExpression;+                }++                context.ReportDiagnostic(operation.CreateDiagnostic(GeneralPreviewFeatureAttributeRule, symbol!.Name));

Nice catch. Fixed.

pgovind

comment created time in an hour

Pull request review commentdotnet/project-system

Implement Hot Reload Support

+// Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. See the LICENSE.md file in the project root for more information.++using System.Collections.Generic;+using System.Threading;+using System.Threading.Tasks;++namespace Microsoft.VisualStudio.ProjectSystem.VS.HotReload+{+    public interface IProjectHotReloadSession+    {+        bool IsSupported { get; }++        Task StartSessionAsync(CancellationToken cancellationToken);++        Task StopSessionAsync(CancellationToken cancellationToken);++        Task ApplyChangesAsync(CancellationToken cancellationToken);++        Task<bool> ApplyLaunchVariablesAsync(IDictionary<string, string> envVars, string configuration, CancellationToken cancellationToken);

FYI, it looks like we really need to check for the following:

<Optimize>false</Optimize>
<EmitDebugInformation>true</EmitDebugInformation>
tmeschter

comment created time in an hour

Pull request review commentmicrosoft/vs-threading

Update VSTHRD110 to handle conditional access expressions

 internal void AnalyzeInvocation(SyntaxNodeAnalysisContext context)                  // Only consider invocations that are direct statements. Otherwise, we assume their                 // result is awaited, assigned, or otherwise consumed.-                if (invocation.Parent?.GetType().Equals(typeof(ExpressionStatementSyntax)) ?? false)+                if (invocation.Parent is ExpressionStatementSyntax || invocation.Parent is ConditionalAccessExpressionSyntax)

Thanks! Good to know.

bluetarpmedia

comment created time in an hour

issue closeddotnet/msbuild

Condition attributes appear to work incorrectly for PropertyGroup elements in Directory.Build.props

Basically, I am building a solution that has several projects, and I have a Directory.Build.props in the .sln folder.

Inside the Directory.Build.props file, I have the following markup:

<PropertyGroup Condition="'$(Configuration)|$(Platform)|$(IsTestProject)' == 'Debug|AnyCPU|true'"> <PackageIcon>Assets\NuGet\Tests %28debug%29.png</PackageIcon> </PropertyGroup>

<ItemGroup Condition="'$(Configuration)|$(Platform)|$(IsTestProject)' == 'Debug|AnyCPU|true'"> <None Include="..\..\..\BluBlaze.Labs.Assets\Icons\ADO\Tests %28debug%29.png" Link="Assets\NuGet\Tests %28debug%29.png" Pack="true" PackagePath="Assets\NuGet\Tests %28debug%29.png" /> </ItemGroup>

Now the issue is, the PropertyGroup condition is not working properly, but the ItemGroup condition is. The conditions are identical. In all of my test projects, they properly link the file Tests (debug).png, and non-test projects properly link the file Package API (debug).png. However, When compiling the project, test projects are not able to find the PackageIcon markup specified above. In fact, after some experimenting, I noticed that test projects are consuming this markup instead:

<PropertyGroup Condition="'$(Configuration)|$(Platform)|$(IsTestProject)' == 'Debug|AnyCPU|'"> <PackageIcon>Assets\NuGet\Package API %28debug%29.png</PackageIcon> </PropertyGroup>

which indicates for whatever reason, $(IsTestProject) is resolving to null or empty. But the weird thing is, $(IsTestProject) resolves to true inside the ItemGroup element condition, in the same Directory.Build.props file.

Finally, the kicker. If I copy and paste, verbatim exactly the PropertyGroup and ItemGroup markup specified above for test projects directly into the .csproj file itself, it works entirely as intended. It just seems for whatever strange reason, the condition in the ItemGroup element doesn't work correctly, and only while inside the Directory.Build.props file.

I don't know if this issue is scoped to just the PropertyGroup element, the $(IsTestProject) element, or any other factors.

For now I have a workaround and can just place the markup in the .csproj file, but some insight as to why this happens and if it'll be address would be greatly appreciated.

The test projects are multi-framework, and have .NET Framework and .NET Core builds, if that's important. Any other info I can provide feel free to let me know.

closed time in an hour

BlazingArdor

issue commentdotnet/msbuild

Condition attributes appear to work incorrectly for PropertyGroup elements in Directory.Build.props

The issue you're running into has to do with the order in which MSBuild evaluates your project, and the order of default imports on your project.

Basically, MSBuild evaluates properties before items and it hasn't found a definition for IsTestProject when parsing your PropertyGroup. It turns out this is because IsTestProject is defined in Microsoft.NET.Test.Sdk.props which is imported after Directory.Build.props. By the time it starts parsing ItemGroups, it's gotten a value for IsTestProject and the condition succeeds.

If you want your logic to run for each project in your sln, you'll want your checks either in: a. Each project (like you've done as a workaround) b. In a Directory.Build.targets file. .targets files are imported after .props files.

Closing the issue but feel free to ask other questions!

BlazingArdor

comment created time in an hour

pull request commentdotnet/msbuild

Default to transitively copying content items

@drewnoakes, this has incremental-build/FUTD implications. We'd really like the copy behavior to be consistent between full builds and incremental builds (see the linked issue; it's currently a mess). Fixing it will require some additional project traversals, though, so we'll do some building of projects that were skipped by FUTD (not significant building; should just be output collection). The set of files copied to a project's outputs will then be consistent but not easy to determine via evaluation alone, because it'll include transitive content (it does today . . . if the transitive projects actually built and weren't skipped).

BenVillalobos

comment created time in 2 hours

push eventdotnet/project-system

Michael Yanni

commit sha 872d3f9521ee9d2d297d70f628f5f1aee2fe5585

Replace BinaryFormatter usage (#7316) * In progress on testing replacing BinaryFormatter with JSON serialization. * In progress trying to add separate serialization via shared C# assembly. * Making progress on getting some serialization to work. Deserialization doesn't work. * Fix debug issue. * Finally, FINALLY, got undo/redo working. 🤯 * In progress of getting Undo/Redo working for Resource editor resources. * Fixed bug with redo not working on resources. * Cleanup recursive de/serialization logic. * Investigating how to do Bitmap serialization. * Added basic implementation for serializing ISerializable types. * Got drag+drop/copy-paste for resources working. * Code cleanup of current working solution. * Initial attempt at replacing property walking recursive algorithm with JsonConverters. * Got the object array converter implemented. Basic properties seem to be working. More testing needed. * Cleanup of current implementation. Removed prior property recursion implementation. * More implementation cleanup. Reuse of options and settings for serializers. Less duplicate code. * More minor cleanup. Remove unused files. * Made MultipleValuesStore use ISerializable. * Remove commented code. Commented code to write files to disk for analysis. Removed fully custom Resource serialization logic since it is no longer used. Instead, ISerializable (pre-existing) is used for it. * More minor code cleanup. * Replaced custom serialization logic using JsonConverter and System.Text.Json with solution using DataContractSerializer proposed by Drew. * Removed custom ISerializable logic for ResourcesDataFormat and MultipleValuesStore. Removed entire set of custom logic for serialization using JsonConverter and System.Text.Json. * Wrote the new solution in the VB code directly. * Removed VB project dependency on C# project. Removed SerializationTest.cs implementation. * Remove fully qualified names since the C# project is no longer referenced. * Minor changes after looking at repo diff. * Adding unit test project for the Editors project. * Minor update to make build not error. * Added initial set of tests. * Almost got the baseline tests working. One issue with deserialization. * Used Base64String instead since this fixes the test issue with UTF8 encoding the BinaryWriter data. * Fixed logic for handling null or empty streams. Added tests for these situations. * Renamed SerializationProvider to ObjectSerializer. * Added named parameters for leaveOpen. * Updated null/empty logic to match that of BinaryFormatter. * Removed setting stream position. * Updated KnownTypes since it was causing a build warning. * Cleaned up project file. * Remove unnecessary changes. Update UnitTest project to use static version number for tests with serialized type information to work properly. * Added BinaryFormatter as a banned symbol. Simplified version notation for the test assembly. * Changed Version to AssemblyVersion. * Added comments for changes that are non-obvious. Changed icon for test to be smaller in filesize. Used auto properties in test class.

view details

push time in 2 hours

PR merged dotnet/project-system

Replace BinaryFormatter usage

After many attempts and investigation, this solution to replace BinaryFormatter seems the most simple and covers 99% of prior scenarios. After reviewing my previous solution, @drewnoakes pointed this solution out to me. Original solutions I made used System.Text.Json as the main serialization driver, as it is the library I've used for the last 3 years or so. This solution doesn't use that, and uses a mix of BinaryReader/Writer and DataContractSerializer.

How is works is it encodes the AssemblyQualifiedName of the value's type as a string into the stream. When using BinaryReader/Writer, it encodes strings including the length of the string. That is important because of the second step. The second step is to use DataContractSerializer to serialize the actual value we want to keep. This is encoded as XML. I don't have a very strong understanding of the mechanics of this, since it must include some type information in the XML. I'm assuming it makes some schema of XML that is similar to how BinaryFormatter worked, but safer? Again, since type information is stored in these serialized values, this allows classes that define Object properties to deserialize (since Object can hold any type). My original solution(s) had to manually encapsulate these Object properties to store type information, since most serializers I'm used to do not use their own schema for serialization that includes type information.

On deserialization, the AssemblyQualifiedName string is read (using the known string length). This is then turned into a Type. What remains in the stream is the XML node of the class's value. This is deserialized using DataContractSerializer. This mechanism works because we only leave 1 XML root node in the stream. Trying to use DataContractSerializer for both the type and value information creates 2 root nodes, which won't work. The other option was to make a type that simply holds both a Type property and a Value property. My original solution did something to this, which I had called SerializationContainer. Here, we are using the BinaryReader/Writer mechanics to encode 2 different serialization types into the same stream, and read them each correctly. As long as the string is read first and then the XML, there aren't any issues.

Note: I will 'Squash and Merge' this PR so that it doesn't have my commit history of the multiple solutions I tried. If you're in for looking at my madness, go look through the commit history. Otherwise, it can be ignored.

Microsoft Reviewers: Open in CodeFlow
+240 -24

5 comments

14 changed files

MiYanni

pr closed time in 2 hours

pull request commentdotnet/designs

Adding a rough draft of the "minimum viable product" for the .NET Libraries APIs to support generic math

PR is up here: https://github.com/dotnet/runtime/pull/54650

tannergooding

comment created time in 2 hours