diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 19cd60b3cf..721a1e48c6 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -28,4 +28,9 @@
true
https://github.com/dotnet/orleans
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Orleans.Analyzers/Orleans.Analyzers.csproj b/src/Orleans.Analyzers/Orleans.Analyzers.csproj
index 2f0ad28ff7..82a525bb1e 100644
--- a/src/Orleans.Analyzers/Orleans.Analyzers.csproj
+++ b/src/Orleans.Analyzers/Orleans.Analyzers.csproj
@@ -37,4 +37,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/Orleans.CodeGenerator/Orleans.CodeGenerator.csproj b/src/Orleans.CodeGenerator/Orleans.CodeGenerator.csproj
index dd65e96f3d..8178fa8441 100644
--- a/src/Orleans.CodeGenerator/Orleans.CodeGenerator.csproj
+++ b/src/Orleans.CodeGenerator/Orleans.CodeGenerator.csproj
@@ -62,4 +62,8 @@
+
+
+
+
diff --git a/src/Orleans.Serialization.Abstractions/FrameworkPartAttribute.cs b/src/Orleans.Serialization.Abstractions/FrameworkPartAttribute.cs
new file mode 100644
index 0000000000..d219050973
--- /dev/null
+++ b/src/Orleans.Serialization.Abstractions/FrameworkPartAttribute.cs
@@ -0,0 +1,13 @@
+using System;
+using System.ComponentModel;
+
+namespace Orleans.Metadata;
+
+///
+/// Specifies that an assembly does not contain application code.
+///
+[AttributeUsage(AttributeTargets.Assembly)]
+[EditorBrowsable(EditorBrowsableState.Never)]
+public sealed class FrameworkPartAttribute : Attribute
+{
+}
diff --git a/src/Orleans.Serialization.NewtonsoftJson/NewtonsoftJsonCodec.cs b/src/Orleans.Serialization.NewtonsoftJson/NewtonsoftJsonCodec.cs
index 227ca827b4..97b4e96dca 100644
--- a/src/Orleans.Serialization.NewtonsoftJson/NewtonsoftJsonCodec.cs
+++ b/src/Orleans.Serialization.NewtonsoftJson/NewtonsoftJsonCodec.cs
@@ -1,9 +1,12 @@
using System;
+using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Reflection;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
+using Orleans.Metadata;
using Orleans.Serialization.Buffers;
using Orleans.Serialization.Buffers.Adaptors;
using Orleans.Serialization.Cloning;
@@ -124,6 +127,11 @@ bool IGeneralizedCodec.IsSupportedType(Type type)
return true;
}
+ if (CommonCodecTypeFilter.IsAbstractOrFrameworkType(type))
+ {
+ return false;
+ }
+
foreach (var selector in _serializableTypeSelectors)
{
if (selector.IsSupportedType(type))
@@ -173,6 +181,11 @@ object IDeepCopier.DeepCopy(object input, CopyContext context)
///
bool IGeneralizedCopier.IsSupportedType(Type type)
{
+ if (CommonCodecTypeFilter.IsAbstractOrFrameworkType(type))
+ {
+ return false;
+ }
+
foreach (var selector in _copyableTypeSelectors)
{
if (selector.IsSupportedType(type))
diff --git a/src/Orleans.Serialization.SystemTextJson/JsonCodec.cs b/src/Orleans.Serialization.SystemTextJson/JsonCodec.cs
index 49006c6a9b..1ffed558da 100644
--- a/src/Orleans.Serialization.SystemTextJson/JsonCodec.cs
+++ b/src/Orleans.Serialization.SystemTextJson/JsonCodec.cs
@@ -2,8 +2,10 @@
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
+using System.Reflection;
using System.Text.Json;
using Microsoft.Extensions.Options;
+using Orleans.Metadata;
using Orleans.Serialization.Buffers;
using Orleans.Serialization.Buffers.Adaptors;
using Orleans.Serialization.Cloning;
@@ -158,6 +160,11 @@ bool IGeneralizedCodec.IsSupportedType(Type type)
return true;
}
+ if (CommonCodecTypeFilter.IsAbstractOrFrameworkType(type))
+ {
+ return false;
+ }
+
foreach (var selector in _serializableTypeSelectors)
{
if (selector.IsSupportedType(type))
@@ -204,6 +211,11 @@ object IDeepCopier.DeepCopy(object input, CopyContext context)
///
bool IGeneralizedCopier.IsSupportedType(Type type)
{
+ if (CommonCodecTypeFilter.IsAbstractOrFrameworkType(type))
+ {
+ return false;
+ }
+
foreach (var selector in _copyableTypeSelectors)
{
if (selector.IsSupportedType(type))
diff --git a/src/Orleans.Serialization/Codecs/CommonCodecTypeFilter.cs b/src/Orleans.Serialization/Codecs/CommonCodecTypeFilter.cs
new file mode 100644
index 0000000000..12f0d750c9
--- /dev/null
+++ b/src/Orleans.Serialization/Codecs/CommonCodecTypeFilter.cs
@@ -0,0 +1,31 @@
+using System;
+using System.CodeDom.Compiler;
+using System.Linq;
+using System.Reflection;
+
+using Orleans.Metadata;
+
+namespace Orleans.Serialization.Codecs;
+
+///
+/// Defines common type filtering operations.
+///
+public class CommonCodecTypeFilter
+{
+ ///
+ /// Returns true if the provided type is a framework or abstract type.
+ ///
+ /// The type to check.
+ /// if the type is a framework or abstract type, otherwise .
+ public static bool IsAbstractOrFrameworkType(Type type)
+ {
+ if (type.IsAbstract
+ || type.GetCustomAttributes().Any(a => a.Tool.Equals("OrleansCodeGen"))
+ || type.Assembly.GetCustomAttribute() is not null)
+ {
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/src/Serializers/Orleans.Serialization.Protobuf/ProtobufCodec.cs b/src/Serializers/Orleans.Serialization.Protobuf/ProtobufCodec.cs
index e971914f2f..3a32c68673 100644
--- a/src/Serializers/Orleans.Serialization.Protobuf/ProtobufCodec.cs
+++ b/src/Serializers/Orleans.Serialization.Protobuf/ProtobufCodec.cs
@@ -1,4 +1,5 @@
using Google.Protobuf;
+using Orleans.Metadata;
using Orleans.Serialization.Buffers;
using Orleans.Serialization.Cloning;
using Orleans.Serialization.Codecs;
@@ -8,6 +9,7 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
+using System.Reflection;
namespace Orleans.Serialization;
@@ -68,6 +70,11 @@ bool IGeneralizedCodec.IsSupportedType(Type type)
return true;
}
+ if (CommonCodecTypeFilter.IsAbstractOrFrameworkType(type))
+ {
+ return false;
+ }
+
foreach (var selector in _serializableTypeSelectors)
{
if (selector.IsSupportedType(type))
@@ -82,6 +89,11 @@ bool IGeneralizedCodec.IsSupportedType(Type type)
///
bool IGeneralizedCopier.IsSupportedType(Type type)
{
+ if (CommonCodecTypeFilter.IsAbstractOrFrameworkType(type))
+ {
+ return false;
+ }
+
foreach (var selector in _copyableTypeSelectors)
{
if (selector.IsSupportedType(type))
diff --git a/test/NonSilo.Tests/NonSilo.Tests.csproj b/test/NonSilo.Tests/NonSilo.Tests.csproj
index a799cc2483..1f4a4221a1 100644
--- a/test/NonSilo.Tests/NonSilo.Tests.csproj
+++ b/test/NonSilo.Tests/NonSilo.Tests.csproj
@@ -9,6 +9,8 @@
+
+
diff --git a/test/NonSilo.Tests/Serialization/ExternalCodecTests.cs b/test/NonSilo.Tests/Serialization/ExternalCodecTests.cs
new file mode 100644
index 0000000000..8d6562b7fa
--- /dev/null
+++ b/test/NonSilo.Tests/Serialization/ExternalCodecTests.cs
@@ -0,0 +1,216 @@
+using Microsoft.Extensions.DependencyInjection;
+using Orleans.Configuration;
+using System.Reflection;
+using TestExtensions;
+using Xunit;
+using System.Text;
+using Microsoft.Extensions.Hosting;
+using Newtonsoft.Json;
+
+using Orleans.Serialization;
+using Orleans.Runtime;
+using Orleans.Serialization.Serializers;
+using Orleans.Streaming.EventHubs;
+using Microsoft.Extensions.Options;
+using Orleans.Serialization.Configuration;
+using Orleans.Metadata;
+
+namespace UnitTests.Serialization
+{
+ [TestCategory("Serialization"), TestCategory("BVT")]
+ public class ExternalCodecTests
+ {
+ private readonly SerializationTestEnvironment environment;
+
+ public ExternalCodecTests()
+ {
+ this.environment = SerializationTestEnvironment.InitializeWithDefaults(
+ builder =>
+ builder.ConfigureServices(services =>
+ services.AddSerializer(serializerBuilder =>
+ {
+ serializerBuilder.AddNewtonsoftJsonSerializer(type => type.GetCustomAttribute() is not null);
+ })));
+ }
+
+ [Fact]
+ public void NewtonsoftJsonCodec_ExternalSerializer_Client()
+ {
+ TestSerializationRoundTrip(this.environment.Serializer);
+ }
+
+ [Fact]
+ public void NewtonsoftJsonCodec_ExternalSerializer_Silo()
+ {
+ var silo = new HostBuilder()
+ .UseOrleans((ctx, siloBuilder) =>
+ {
+ siloBuilder
+ .Configure(o => o.ClusterId = o.ServiceId = "s")
+ .UseLocalhostClustering()
+ .ConfigureServices(services =>
+ services.AddSerializer(serializerBuilder =>
+ {
+ serializerBuilder.AddNewtonsoftJsonSerializer(type => type.GetCustomAttribute() is not null);
+ }));
+ })
+ .Build();
+ var serializer = silo.Services.GetRequiredService();
+ TestSerializationRoundTrip(serializer);
+ }
+
+ [Fact]
+ public void NewtonsoftJsonCodec_CanModifySerializerSettings()
+ {
+ var silo = new HostBuilder()
+ .UseOrleans((ctx, siloBuilder) =>
+ {
+ siloBuilder
+ .Configure(o => o.ClusterId = o.ServiceId = "s")
+ .Configure(options => options.JsonSerializerSettings.DefaultValueHandling = DefaultValueHandling.Include)
+ .UseLocalhostClustering();
+ })
+ .Build();
+ var serializer = silo.Services.GetRequiredService();
+ var data = new JsonPoco();
+ var serialized = serializer.Serialize(data, typeof(JsonPoco));
+ Assert.Contains("some_flag", serialized);
+ }
+
+ [Fact]
+ public void NewtonsoftJsonCodec_DoesNotSerializeFrameworkTypes()
+ {
+ var silo = new HostBuilder()
+ .UseOrleans((ctx, siloBuilder) =>
+ {
+ siloBuilder.Services.AddSerializer(serializerBuilder => serializerBuilder.AddNewtonsoftJsonSerializer(type =>
+ {
+ Assert.Fail($"Custom type filter should not be consulted for any type in this test, but it was used for type {type}.");
+ return true;
+ }));
+ siloBuilder.UseLocalhostClustering();
+ })
+ .Build();
+ var services = silo.Services;
+ var serializer = services.GetRequiredService();
+ var generatedGrainReferenceType = services.GetRequiredService>().Value
+ .InterfaceProxies.First(i => ! i.IsGenericType && i.Assembly.GetCustomAttribute() is null);
+ var codecProvider = services.GetRequiredService();
+ foreach (var type in new[] { typeof(SiloAddress), typeof(GrainReference), typeof(EventHubBatchContainer), generatedGrainReferenceType })
+ {
+ var codec = codecProvider.GetCodec(type);
+ Assert.IsNotType(codec);
+
+ var copier = codecProvider.GetDeepCopier(type);
+ Assert.IsNotType(copier);
+ }
+ }
+
+ [Fact]
+ public void SystemTextJsonCodec_DoesNotSerializeFrameworkTypes()
+ {
+ var silo = new HostBuilder()
+ .UseOrleans((ctx, siloBuilder) =>
+ {
+ siloBuilder.Services.AddSerializer(serializerBuilder => serializerBuilder.AddJsonSerializer(type =>
+ {
+ Assert.Fail($"Custom type filter should not be consulted for any type in this test, but it was used for type {type}.");
+ return true;
+ }));
+ siloBuilder.UseLocalhostClustering();
+ })
+ .Build();
+ var services = silo.Services;
+ var serializer = services.GetRequiredService();
+ var generatedGrainReferenceType = services.GetRequiredService>().Value
+ .InterfaceProxies.First(i => ! i.IsGenericType && i.Assembly.GetCustomAttribute() is null);
+ var codecProvider = services.GetRequiredService();
+ foreach (var type in new[] { typeof(SiloAddress), typeof(GrainReference), typeof(EventHubBatchContainer), generatedGrainReferenceType })
+ {
+ var codec = codecProvider.GetCodec(type);
+ Assert.IsNotType(codec);
+
+ var copier = codecProvider.GetDeepCopier(type);
+ Assert.IsNotType(copier);
+ }
+ }
+
+ [Fact]
+ public void ProtocolBuffersCodec_DoesNotSerializeFrameworkTypes()
+ {
+ var silo = new HostBuilder()
+ .UseOrleans((ctx, siloBuilder) =>
+ {
+ siloBuilder.Services.AddSerializer(serializerBuilder => serializerBuilder.AddProtobufSerializer(type =>
+ {
+ Assert.Fail($"Custom type filter should not be consulted for any type in this test, but it was used for type {type}.");
+ return true;
+ },
+ type =>
+ {
+ Assert.Fail($"Custom type filter should not be consulted for any type in this test, but it was used for type {type}.");
+ return true;
+ }));
+ siloBuilder.UseLocalhostClustering();
+ })
+ .Build();
+ var services = silo.Services;
+ var serializer = services.GetRequiredService();
+ var generatedGrainReferenceType = services.GetRequiredService>().Value
+ .InterfaceProxies.First(i => ! i.IsGenericType && i.Assembly.GetCustomAttribute() is null);
+ var codecProvider = services.GetRequiredService();
+ foreach (var type in new[] { typeof(SiloAddress), typeof(GrainReference), typeof(EventHubBatchContainer), generatedGrainReferenceType })
+ {
+ var codec = codecProvider.GetCodec(type);
+ Assert.IsNotType(codec);
+
+ var copier = codecProvider.GetDeepCopier(type);
+ Assert.IsNotType(copier);
+ }
+ }
+
+ private static void TestSerializationRoundTrip(Serializer serializer)
+ {
+ var data = new JsonPoco { Prop = "some data" };
+ var serialized = serializer.SerializeToArray(data);
+ var subSequence = Encoding.UTF8.GetBytes("crazy_name");
+
+ // The serialized data should have our custom [JsonProperty] name, 'crazy_name', in it.
+ Assert.Contains(ToString(subSequence), ToString(serialized));
+
+ var deserialized = serializer.Deserialize(serialized);
+
+ Assert.Equal(data.Prop, deserialized.Prop);
+ }
+
+ private static string ToString(byte[] bytes)
+ {
+ var result = new StringBuilder(bytes.Length * 2);
+ foreach (var b in bytes)
+ {
+ result.Append($"{b:x2}");
+ }
+
+ var str = result.ToString();
+ return str;
+ }
+
+ ///
+ /// Use a custom attribute to distinguish types which should be serialized using the JSON codec.
+ ///
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
+ internal class JsonTypeAttribute : Attribute
+ {
+ }
+
+ [JsonType]
+ public class JsonPoco
+ {
+ [JsonProperty("crazy_name")]
+ public string Prop { get; set; }
+
+ [JsonProperty("some_flag")]
+ public bool Flag { get; set; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/NonSilo.Tests/Serialization/OrleansJsonSerializerTests.cs b/test/NonSilo.Tests/Serialization/OrleansJsonSerializerTests.cs
deleted file mode 100644
index a574208166..0000000000
--- a/test/NonSilo.Tests/Serialization/OrleansJsonSerializerTests.cs
+++ /dev/null
@@ -1,119 +0,0 @@
-using Microsoft.Extensions.DependencyInjection;
-using Orleans.Configuration;
-using System.Reflection;
-using Orleans.Hosting;
-using TestExtensions;
-using Xunit;
-using System.Text;
-using Microsoft.Extensions.Hosting;
-using Newtonsoft.Json;
-
-using Orleans.Serialization;
-
-namespace UnitTests.Serialization
-{
- [TestCategory("Serialization"), TestCategory("BVT")]
- public class OrleansJsonSerializerTests
- {
- private readonly SerializationTestEnvironment environment;
-
- public OrleansJsonSerializerTests()
- {
- this.environment = SerializationTestEnvironment.InitializeWithDefaults(
- builder =>
- builder.ConfigureServices(services =>
- services.AddSerializer(serializerBuilder =>
- {
- serializerBuilder.AddNewtonsoftJsonSerializer(type => type.GetCustomAttribute() is not null);
- })));
- }
-
- [Fact]
- public void OrleansJsonSerializer_ExternalSerializer_Client()
- {
- TestSerializationRoundTrip(this.environment.Serializer);
- }
-
- [Fact]
- public void OrleansJsonSerializer_ExternalSerializer_Silo()
- {
- var silo = new HostBuilder()
- .UseOrleans((ctx, siloBuilder) =>
- {
- siloBuilder
- .Configure(o => o.ClusterId = o.ServiceId = "s")
- .UseLocalhostClustering()
- .ConfigureServices(services =>
- services.AddSerializer(serializerBuilder =>
- {
- serializerBuilder.AddNewtonsoftJsonSerializer(type => type.GetCustomAttribute() is not null);
- }));
- })
- .Build();
- var serializer = silo.Services.GetRequiredService();
- TestSerializationRoundTrip(serializer);
- }
-
- [Fact]
- public void OrleansJsonSerializer_CanModifySerializerSettings()
- {
- var silo = new HostBuilder()
- .UseOrleans((ctx, siloBuilder) =>
- {
- siloBuilder
- .Configure(o => o.ClusterId = o.ServiceId = "s")
- .Configure(options => options.JsonSerializerSettings.DefaultValueHandling = DefaultValueHandling.Include)
- .UseLocalhostClustering();
- })
- .Build();
- var serializer = silo.Services.GetRequiredService();
- var data = new JsonPoco();
- var serialized = serializer.Serialize(data, typeof(JsonPoco));
- Assert.Contains("some_flag", serialized);
- }
-
- private static void TestSerializationRoundTrip(Serializer serializer)
- {
- var data = new JsonPoco {Prop = "some data"};
- var serialized = serializer.SerializeToArray(data);
- var subSequence = Encoding.UTF8.GetBytes("crazy_name");
-
- // The serialized data should have our custom [JsonProperty] name, 'crazy_name', in it.
- Assert.Contains(ToString(subSequence), ToString(serialized));
-
- var deserialized = serializer.Deserialize(serialized);
-
- Assert.Equal(data.Prop, deserialized.Prop);
- }
-
- private static string ToString(byte[] bytes)
- {
- var result = new StringBuilder(bytes.Length * 2);
- foreach (var b in bytes)
- {
- result.Append($"{b:x2}");
- }
-
- var str = result.ToString();
- return str;
- }
-
- ///
- /// Use a custom attribute to distinguish types which should be serialized using the JSON codec.
- ///
- [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
- internal class JsonTypeAttribute : Attribute
- {
- }
-
- [JsonType]
- public class JsonPoco
- {
- [JsonProperty("crazy_name")]
- public string Prop { get; set; }
-
- [JsonProperty("some_flag")]
- public bool Flag { get; set; }
- }
- }
-}
\ No newline at end of file