Skip to content

Commit

Permalink
ObjectDumper v1.5.0:
Browse files Browse the repository at this point in the history
            ATTENTION: possible breaking change.
            The methods registering metadata information - the overloaded family 'ClassMetadataRegistrar.Register' - were added a new parameter `bool replace = false`.
            If the parameter is false (the default) and the method is invoked to replace existing mapping of a type to different pair of dump metadata type and `DumpAttribute` instance, it will throw `InvalidOperationException`.
            This will allow to detect confusing dump behavior, when one registrar registers some metadata and another replaces it with different metadata. To preserve the old behavior or to allow overriding of dump behavior, set
            the parameter 'replace = true'.
  • Loading branch information
vmelamed committed Jan 1, 2016
1 parent 2dd2699 commit f131b9c
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 34 deletions.
44 changes: 34 additions & 10 deletions Aspects/Diagnostics/ClassMetadataRegistrar.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Configuration;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
//using System.Data;
Expand Down Expand Up @@ -55,24 +54,31 @@ public static ClassMetadataRegistrar RegisterMetadata()
}

/// <summary>
/// Registers the dump metadata and <see cref="DumpAttribute"/> instance related to the specified type.
/// Registers the dump metadata and <see cref="DumpAttribute" /> instance related to the specified type.
/// </summary>
/// <param name="type">The type for which the metadata is being registered.</param>
/// <param name="metadataType">The dump metadata type.</param>
/// <param name="dumpAttribute">The dump attribute.</param>
/// <param name="replace">
/// If set to <see langword="false" /> and there is already dump metadata associated with the <paramref name="type"/>
/// the method will throw exception of type <see cref="InvalidOperationException"/>;
/// otherwise it will silently override the existing metadata with <paramref name="metadataType"/> and <paramref name="dumpAttribute"/>.
/// </param>
/// <returns>The current instance of ClassMetadataRegistrar.</returns>
/// <exception cref="T:System.ArgumentNullException">
/// Thrown if <paramref name="type"/> is <see langword="null"/>.
/// <exception cref="T:System.ArgumentNullException">Thrown if <paramref name="type" /> is <see langword="null" />.</exception>
/// <exception cref="InvalidOperationException">
/// Thrown if <paramref name="replace"/> is <see langword="false"/> and there is already metadata associated with the <paramref name="type"/>.
/// </exception>
public ClassMetadataRegistrar Register(
Type type,
Type metadataType,
DumpAttribute dumpAttribute = null)
DumpAttribute dumpAttribute = null,
bool replace = false)
{
Contract.Requires<ArgumentNullException>(type != null, "type");
Contract.Ensures(Contract.Result<ClassMetadataRegistrar>() != null);

ClassMetadataResolver.SetClassDumpData(type, metadataType, dumpAttribute);
ClassMetadataResolver.SetClassDumpData(type, metadataType, dumpAttribute, replace);
return this;
}

Expand All @@ -82,29 +88,47 @@ public ClassMetadataRegistrar Register(
/// <typeparam name="T">The type for which the metadata is being registered.</typeparam>
/// <typeparam name="TMetadata">The dump metadata type.</typeparam>
/// <param name="dumpAttribute">The dump attribute.</param>
/// <param name="replace">
/// If set to <see langword="false" /> and there is already dump metadata associated with the <typeparamref name="T"/>
/// the method will throw exception of type <see cref="InvalidOperationException"/>;
/// otherwise it will silently override the existing metadata with <typeparamref name="TMetadata"/> and the <paramref name="dumpAttribute"/>.
/// </param>
/// <returns>The current instance of ClassMetadataRegistrar.</returns>
/// <exception cref="InvalidOperationException">
/// Thrown if <paramref name="replace"/> is <see langword="false"/> and there is already metadata associated with the <typeparamref name="T"/>.
/// </exception>
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
public ClassMetadataRegistrar Register<T, TMetadata>(
DumpAttribute dumpAttribute = null)
DumpAttribute dumpAttribute = null,
bool replace = false)
{
Contract.Ensures(Contract.Result<ClassMetadataRegistrar>() != null);

return Register(typeof(T), typeof(TMetadata), dumpAttribute);
return Register(typeof(T), typeof(TMetadata), dumpAttribute, replace);
}

/// <summary>
/// Registers the specified dump attribute.
/// </summary>
/// <typeparam name="T">The type for which the dump attribute is being registered.</typeparam>
/// <param name="dumpAttribute">The dump attribute.</param>
/// <param name="replace">
/// If set to <see langword="false" /> and there is already dump metadata associated with the <typeparamref name="T"/>
/// the method will throw exception of type <see cref="InvalidOperationException"/>;
/// otherwise it will silently override the existing metadata with itself - <typeparamref name="T"/> and the <paramref name="dumpAttribute"/>.
/// </param>
/// <returns>The current instance of ClassMetadataRegistrar.</returns>
/// <exception cref="InvalidOperationException">
/// Thrown if <paramref name="replace"/> is <see langword="false"/> and there is already metadata associated with the <typeparamref name="T"/>.
/// </exception>
[SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
public ClassMetadataRegistrar Register<T>(
DumpAttribute dumpAttribute)
DumpAttribute dumpAttribute,
bool replace = false)
{
Contract.Ensures(Contract.Result<ClassMetadataRegistrar>() != null);

return Register(typeof(T), null, dumpAttribute);
return Register(typeof(T), null, dumpAttribute, replace);
}
}
}
51 changes: 41 additions & 10 deletions Aspects/Diagnostics/ClassMetadataResolver.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Threading;

namespace vm.Aspects.Diagnostics
Expand Down Expand Up @@ -42,10 +42,19 @@ static Dictionary<Type, ClassDumpData> TypesDumpData
/// <param name="type">The type for which to set buddy type and dump attribute.</param>
/// <param name="metadata">The metadata type (buddy class).</param>
/// <param name="dumpAttribute">The dump attribute.</param>
/// <param name="replace">
/// If set to <see langword="false" /> and there is already dump metadata associated with the <paramref name="type"/>
/// the method will throw exception of type <see cref="InvalidOperationException"/>;
/// otherwise it will silently override the existing metadata with <paramref name="metadata"/> and <paramref name="dumpAttribute"/>.
/// </param>
/// <exception cref="InvalidOperationException">
/// Thrown if <paramref name="replace"/> is <see langword="false"/> and there is already metadata associated with the <paramref name="type"/>.
/// </exception>
public static void SetClassDumpData(
Type type,
Type metadata = null,
DumpAttribute dumpAttribute = null)
DumpAttribute dumpAttribute = null,
bool replace = false)
{
Contract.Requires<ArgumentNullException>(type != null, "type");

Expand All @@ -58,7 +67,7 @@ public static void SetClassDumpData(
: type;
}

AddClassDumpData(type, metadata, dumpAttribute);
AddClassDumpData(type, metadata, dumpAttribute, replace);
}

/// <summary>
Expand All @@ -80,10 +89,17 @@ public static ClassDumpData GetClassDumpData(
// extract the dump data from the type
dumpData = ExtractClassDumpData(type);

AddClassDumpData(type, dumpData.Value);
try
{
AddClassDumpData(type, dumpData.Value, false);

// return what we found
return dumpData.Value;
// return what we found
return dumpData.Value;
}
catch (InvalidOperationException)
{
return TryGetClassDumpData(type).Value;
}
}

static ClassDumpData ExtractClassDumpData(Type type)
Expand Down Expand Up @@ -121,20 +137,35 @@ static ClassDumpData ExtractClassDumpData(Type type)
return null;
}

static void AddClassDumpData(Type type, Type buddy, DumpAttribute dumpAttribute)
static void AddClassDumpData(Type type, Type buddy, DumpAttribute dumpAttribute, bool replace)
{
Contract.Requires<ArgumentNullException>(type != null, nameof(type));

AddClassDumpData(type, new ClassDumpData(buddy, dumpAttribute));
AddClassDumpData(type, new ClassDumpData(buddy, dumpAttribute), replace);
}

static void AddClassDumpData(Type type, ClassDumpData classDumpData)
static void AddClassDumpData(Type type, ClassDumpData classDumpData, bool replace)
{
Contract.Requires<ArgumentNullException>(type != null, nameof(type));

TypesDumpDataSync.EnterWriteLock();
try
{
TypesDumpDataSync.EnterWriteLock();
ClassDumpData dumpData;

if (!replace && TypesDumpData.TryGetValue(type, out dumpData))
{
if (dumpData == classDumpData)
return;

throw new InvalidOperationException(
string.Format(
CultureInfo.InvariantCulture,
"The type {0} is already associated with metadata type {1} and a DumpAttribute instance.",
type.FullName,
TypesDumpData[type].Metadata.FullName));
}

TypesDumpData[type] = classDumpData;
}
finally
Expand Down
8 changes: 6 additions & 2 deletions Aspects/Diagnostics/NuGet/ObjectDumper.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>AspectObjectDumper</id>
<version>1.4.2</version>
<version>1.5.0</version>
<authors>Val Melamed</authors>
<owners>Val Melamed</owners>
<summary>
Expand All @@ -28,7 +28,11 @@
* Build and tested with .NET 4.0, 4.6. This package targets .NET 4.0.
</description>
<releaseNotes>
Added the XML documentation.
ATTENTION: possible breaking change.
The methods registering metadata information - the overloaded family 'ClassMetadataRegistrar.Register' - were added a new parameter `bool replace = false`.
If the parameter is false (the default) and the method is invoked to replace existing mapping of a type to different pair of dump metadata type and `DumpAttribute` instance, it will throw `InvalidOperationException`.
This will allow to detect confusing dump behavior, when one registrar registers some metadata and another replaces it with different metadata. To preserve the old behavior or to allow overriding of dump behavior, set
the parameter 'replace = true'.
</releaseNotes>
<licenseUrl>https://aspectobjectdumper.codeplex.com/license</licenseUrl>
<projectUrl>https://aspectobjectdumper.codeplex.com/</projectUrl>
Expand Down
6 changes: 3 additions & 3 deletions Aspects/Diagnostics/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

[assembly: AssemblyTitle("vm.Aspects.Diagnostics.ObjectDumper")]
[assembly: AssemblyDescription("Dumps the properties' and fields' values of any .NET object in a text form.")]
[assembly: AssemblyVersion("1.4.2")]
[assembly: AssemblyFileVersion("1.4.2")]
[assembly: AssemblyInformationalVersion("1.4.2")]
[assembly: AssemblyVersion("1.5.0")]
[assembly: AssemblyFileVersion("1.5.0")]
[assembly: AssemblyInformationalVersion("1.5.0")]

[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(
"vm.Aspects.Diagnostics.ObjectDumper.Tests, " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ static Dictionary<Type, ClassDumpData> TypesDumpData
[TestMethod]
public void TestSetClassDumpData_NullArg2n3()
{
ClassMetadataResolver.SetClassDumpData(typeof(ClassMetadataCacheTest), null, null);
ClassMetadataResolver.SetClassDumpData(typeof(ClassMetadataCacheTest), null, null, true);

Assert.AreEqual(initialCacheSize+1, TypesDumpData.Count());
Assert.AreEqual(
Expand All @@ -32,7 +32,7 @@ public void TestSetClassDumpData_NullArg2n3()
[TestMethod]
public void TestSetClassDumpData_NullArg2()
{
ClassMetadataResolver.SetClassDumpData(typeof(ClassMetadataCacheTest), null, new DumpAttribute(false));
ClassMetadataResolver.SetClassDumpData(typeof(ClassMetadataCacheTest), null, new DumpAttribute(false), true);

Assert.AreEqual(initialCacheSize+1, TypesDumpData.Count());
Assert.AreEqual(
Expand All @@ -43,7 +43,7 @@ public void TestSetClassDumpData_NullArg2()
[TestMethod]
public void TestSetClassDumpData_NullArg3()
{
ClassMetadataResolver.SetClassDumpData(typeof(ClassMetadataCacheTest), typeof(ClassMetadataResolver), null);
ClassMetadataResolver.SetClassDumpData(typeof(ClassMetadataCacheTest), typeof(ClassMetadataResolver), null, true);

Assert.AreEqual(initialCacheSize+1, TypesDumpData.Count());
Assert.AreEqual(
Expand All @@ -54,14 +54,50 @@ public void TestSetClassDumpData_NullArg3()
[TestMethod]
public void TestSetClassDumpData()
{
ClassMetadataResolver.SetClassDumpData(typeof(ClassMetadataCacheTest), typeof(ClassMetadataResolver), new DumpAttribute(false));
ClassMetadataResolver.SetClassDumpData(typeof(ClassMetadataCacheTest), typeof(ClassMetadataResolver), new DumpAttribute(false), true);

Assert.AreEqual(initialCacheSize+1, TypesDumpData.Count());
Assert.AreEqual(
new ClassDumpData(typeof(ClassMetadataResolver), new DumpAttribute(false)),
TypesDumpData[typeof(ClassMetadataCacheTest)]);
}

//////////////////////////////////////////////////////////////////////////////

[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void TestSetClassDumpData_NullArg2n3_Exception()
{
ClassMetadataResolver.SetClassDumpData(typeof(ClassMetadataCacheTest), null, null, true);
ClassMetadataResolver.SetClassDumpData(typeof(ClassMetadataCacheTest), typeof(ClassMetadataResolver), null, false);
}

[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void TestSetClassDumpData_NullArg2_Exception()
{
ClassMetadataResolver.SetClassDumpData(typeof(ClassMetadataCacheTest), null, new DumpAttribute(false), true);
ClassMetadataResolver.SetClassDumpData(typeof(ClassMetadataCacheTest), null, new DumpAttribute(true), false);
}

[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void TestSetClassDumpData_NullArg3_Exception()
{
ClassMetadataResolver.SetClassDumpData(typeof(ClassMetadataCacheTest), typeof(ClassMetadataResolver), null, true);
ClassMetadataResolver.SetClassDumpData(typeof(ClassMetadataCacheTest), typeof(ClassMetadataCacheTest), null, false);
}

[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void TestSetClassDumpData_Exception()
{
ClassMetadataResolver.SetClassDumpData(typeof(ClassMetadataCacheTest), typeof(ClassMetadataResolver), new DumpAttribute(false), true);
ClassMetadataResolver.SetClassDumpData(typeof(ClassMetadataCacheTest), typeof(ClassMetadataCacheTest), new DumpAttribute(true), false);
}

//////////////////////////////////////////////////////////////////////////////

[TestMethod]
public void TestGetClassDumpAttribute_InCache()
{
Expand Down
2 changes: 1 addition & 1 deletion Aspects/NuGet/vm.Aspects.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ This will allow the inheritors to modify the behavior of the property Id, withou
version="6.1.3" />

<dependency id="AspectObjectDumper"
version="1.4.2" />
version="1.5.0" />
</dependencies>
<tags></tags>
</metadata>
Expand Down
7 changes: 3 additions & 4 deletions Aspects/Test/Validation/ValidatorXmlStringTests.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using vm.Aspects.Validation;

namespace vm.Aspects.Validation.Tests
{
/// <summary>
/// Summary description for ValidatorXmlStringTests
/// </summary>
[TestClass]
[DeploymentItem("..\\..\\Linq\\Expressions\\Serialization\\Tests\\Microsoft.Serialization.xsd")]
[DeploymentItem("..\\..\\Linq\\Expressions\\Serialization\\Tests\\DataContract.xsd")]
[DeploymentItem("..\\..\\Linq\\Expressions\\Serialization\\Documents\\Expression.xsd")]
[DeploymentItem("..\\..\\..\\Linq\\Expressions\\Serialization\\Tests\\Microsoft.Serialization.xsd")]
[DeploymentItem("..\\..\\..\\Linq\\Expressions\\Serialization\\Tests\\DataContract.xsd")]
[DeploymentItem("..\\..\\..\\Linq\\Expressions\\Serialization\\Documents\\Expression.xsd")]
public class ValidatorXmlStringTests
{
/// <summary>
Expand Down

0 comments on commit f131b9c

Please sign in to comment.