diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/InitializationConstructor.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/InitializationConstructorAttribute.cs similarity index 80% rename from sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/InitializationConstructor.cs rename to sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/InitializationConstructorAttribute.cs index 44ddb331ff80..b5f3b625ab00 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/InitializationConstructor.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/InitializationConstructorAttribute.cs @@ -9,12 +9,12 @@ namespace Azure.ResourceManager.Core /// An attribute class indicating the constructor to use for initialization. /// [AttributeUsage(AttributeTargets.Constructor)] - public class InitializationConstructor : Attribute + public class InitializationConstructorAttribute : Attribute { /// /// Instatiate a new InitializationConstructor attribute. /// - public InitializationConstructor() + public InitializationConstructorAttribute() { } } diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/Resource.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/Resource.cs index 29ebb2d3740d..befebf7f9a56 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/Resource.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/Resource.cs @@ -25,7 +25,7 @@ protected Resource() { } /// The name of the resource. /// The of the resource. [SerializationConstructor] - protected Resource(TIdentifier id, string name, ResourceType type) + protected internal Resource(TIdentifier id, string name, ResourceType type) { Id = id; Name = name; diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/SerializationConstructor.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/SerializationConstructorAttribute.cs similarity index 80% rename from sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/SerializationConstructor.cs rename to sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/SerializationConstructorAttribute.cs index e124b806da6c..4d62b8a047ad 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/SerializationConstructor.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/SerializationConstructorAttribute.cs @@ -9,12 +9,12 @@ namespace Azure.ResourceManager.Core /// An attribute class indicating the constructor to use for serialization. /// [AttributeUsage(AttributeTargets.Constructor)] - public class SerializationConstructor : Attribute + public class SerializationConstructorAttribute : Attribute { /// /// Instatiate a new SerializationConstructor attribute. /// - public SerializationConstructor() + public SerializationConstructorAttribute() { } } diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/SubResource.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/SubResource.cs index d3161537833f..49a2b46b604b 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/SubResource.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/SubResource.cs @@ -13,20 +13,20 @@ public partial class SubResource /// Initializes an empty instance of for mocking. /// [InitializationConstructor] - protected SubResource() + public SubResource() { } /// Initializes a new instance of SubResource. /// ARM resource Id. [SerializationConstructor] - internal SubResource(string id) + protected internal SubResource(string id) { Id = id; } /// - /// ARM resource identifier. + /// Gets the ARM resource identifier. /// /// public virtual ResourceIdentifier Id { get; } diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/SubResourceWritable.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/SubResourceWritable.cs index ef810d7120f0..4061074adb48 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/SubResourceWritable.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/SubResourceWritable.cs @@ -18,13 +18,13 @@ public SubResourceWritable() /// Initializes a new instance of SubResourceReadOnly. /// ARM resource Id. [SerializationConstructor] - internal SubResourceWritable(string id) + protected internal SubResourceWritable(string id) { Id = id; } /// - /// ARM resource identifier (read-only). + /// Gets or sets the ARM resource identifier. /// /// public virtual ResourceIdentifier Id { get; set; } diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/TrackedResource.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/TrackedResource.cs index b17703ffad8c..24fe9a1c8149 100644 --- a/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/TrackedResource.cs +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/src/Resources/TrackedResource.cs @@ -40,7 +40,7 @@ protected TrackedResource(LocationData location) /// The tags for the resource. /// The location of the resource. [SerializationConstructor] - protected TrackedResource(TIdentifier id, string name, ResourceType type, LocationData location, IDictionary tags) + protected internal TrackedResource(TIdentifier id, string name, ResourceType type, LocationData location, IDictionary tags) : base(id, name, type) { Tags = tags ?? new Dictionary(StringComparer.InvariantCultureIgnoreCase); diff --git a/sdk/resourcemanager/Azure.ResourceManager.Core/tests/Unit/ReferenceTypeTests.cs b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/Unit/ReferenceTypeTests.cs new file mode 100644 index 000000000000..7f35a405e80a --- /dev/null +++ b/sdk/resourcemanager/Azure.ResourceManager.Core/tests/Unit/ReferenceTypeTests.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using NUnit.Framework; + +namespace Azure.ResourceManager.Core.Tests +{ + public class ReferenceTypeTests + { + private static readonly Type ReferenceAttribute = typeof(ReferenceTypeAttribute); + private static readonly Type SerializationConstructor = typeof(SerializationConstructorAttribute); + private static readonly Type InitializationConstructor = typeof(InitializationConstructorAttribute); + private static readonly IEnumerable AssemblyTypes = typeof(ArmClient).Assembly.GetTypes(); + + [Test] + public void ValidateSerializationConstructor() + { + foreach (var refType in AssemblyTypes.Where(t => HasAttribute(t.GetCustomAttributes(false), ReferenceAttribute))) + { + var serializationCtor = refType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) + .Where(c => HasAttribute(c.GetCustomAttributes(false), SerializationConstructor)).FirstOrDefault(); + Assert.IsNotNull(serializationCtor); + Assert.IsTrue(serializationCtor.IsFamilyOrAssembly, $"Serialization ctor for {refType.Name} should be protected internal"); + Assert.IsFalse(serializationCtor.IsPublic, $"Serialization ctor for {refType.Name} should not be public"); + } + } + + [Test] + public void ValidateInitializationConstructor() + { + foreach (var refType in AssemblyTypes.Where(t => HasAttribute(t.GetCustomAttributes(false), ReferenceAttribute))) + { + var initializationCtor = refType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) + .Where(c => HasAttribute(c.GetCustomAttributes(false), InitializationConstructor)).FirstOrDefault(); + Assert.IsNotNull(initializationCtor); + Assert.IsTrue(refType.IsAbstract == initializationCtor.IsFamily, $"If {refType.Name} is abstract then its initialization ctor should be protected"); + Assert.IsTrue(refType.IsAbstract != initializationCtor.IsPublic, $"If {refType.Name} is abstract then its initialization ctor should be public"); + Assert.IsFalse(initializationCtor.IsAssembly, $"Initialization ctor for {refType.Name} should not be internal"); + } + } + + public bool HasAttribute(IEnumerable list, Type attributeType) + { + return list.FirstOrDefault(a => a.GetType() == attributeType) is not null; + } + } +}