diff --git a/AbsInjectTypeDll/AbsInjectTypeDll.csproj b/AbsInjectTypeDll/AbsInjectTypeDll.csproj
new file mode 100644
index 0000000..fc6edfa
--- /dev/null
+++ b/AbsInjectTypeDll/AbsInjectTypeDll.csproj
@@ -0,0 +1,12 @@
+
+
+
+ netstandard2.1
+ enable
+
+
+
+
+
+
+
diff --git a/AbsInjectTypeDll/Class1.cs b/AbsInjectTypeDll/Class1.cs
new file mode 100644
index 0000000..4aed4b6
--- /dev/null
+++ b/AbsInjectTypeDll/Class1.cs
@@ -0,0 +1,71 @@
+using ShareAttributes;
+using System;
+
+namespace AbsInjectTypeDll.DllSubAssembly
+{
+
+ [UnionAbsOrInterface]
+ public abstract class CBase1
+ {
+ }
+
+ public abstract class CBase2
+ {
+ public long CTB2 { get; set; }
+ }
+
+ [UnionAbsOrInterface]
+ public abstract class CBase3 : CBase1
+ {
+
+ }
+
+ [UnionAbsOrInterface]
+ public abstract class CBase4
+ {
+
+ }
+
+ public abstract class CBase5 : CBase4
+ {
+
+ }
+ [UnionAbsOrInterface]
+ public abstract class CBase6 : CBase4
+ {
+
+ }
+
+ [UnionAbsOrInterface]
+ public interface IBase1
+ {
+
+ }
+
+ public interface IBase2
+ {
+
+ }
+
+ [UnionAbsOrInterface]
+ public interface IBase3 : IBase1
+ {
+
+ }
+
+ [UnionAbsOrInterface]
+ public interface IBase4
+ {
+
+ }
+
+ public interface IBase5 : IBase4
+ {
+
+ }
+ [UnionAbsOrInterface]
+ public interface IBase6 : IBase4
+ {
+
+ }
+}
diff --git a/Blah/PolymorphicDelegate.cs b/Blah/PolymorphicDelegate.cs
deleted file mode 100644
index 9e851d6..0000000
--- a/Blah/PolymorphicDelegate.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using MessagePack;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace PolymorphicMessagePack
-{
- //I created this, because I have no clue how to create a delegate (MethodInfo.Invoke is too slow), that uses the generic parameter of the class (As opposed to a generic method that has it's own parameter 'T').
- //Is this the only way?
- internal abstract class PolymorphicDelegate
- {
- public abstract void Serialize(ref MessagePackWriter writer, object value, MessagePackSerializerOptions options);
- public abstract object Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options);
-
- }
-
- internal class PolymorphicDelegate : PolymorphicDelegate
- {
- private delegate void SerializeDelegate(ref MessagePackWriter writer, T value, MessagePackSerializerOptions options);
- private delegate T DeserializeDelegate(ref MessagePackReader reader, MessagePackSerializerOptions options);
-
- private SerializeDelegate _serializeDelegate;
- private DeserializeDelegate _deserializeDelegate;
-
- public PolymorphicDelegate(IFormatterResolver resolver)
- {
- var formatter = resolver.GetFormatter();
-
- _serializeDelegate = formatter.Serialize;
- _deserializeDelegate = formatter.Deserialize;
- }
-
- public override void Serialize(ref MessagePackWriter writer, object value, MessagePackSerializerOptions options)
- {
- _serializeDelegate.Invoke(ref writer, (T)value, options);
- }
-
- public override object Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
- {
- return _deserializeDelegate.Invoke(ref reader, options);
- }
- }
-}
diff --git a/Blah/PolymorphicFormatter.cs b/Blah/PolymorphicFormatter.cs
deleted file mode 100644
index f5386f4..0000000
--- a/Blah/PolymorphicFormatter.cs
+++ /dev/null
@@ -1,85 +0,0 @@
-using MessagePack;
-using MessagePack.Formatters;
-using MessagePack.Resolvers;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace PolymorphicMessagePack
-{
-
- public class PolymorphicFormatter : IMessagePackFormatter
- {
- private object lockObj = new object();
-
- public PolymorphicFormatter()
- {
- }
-
- public void Serialize(ref MessagePackWriter writer, T value, MessagePackSerializerOptions options)
- {
-
- if (value == null)
- {
- writer.WriteNil();
- return;
- }
-
- //Could remove this if the settings were part of the regular options
- if (!(options is PolymorphicMessagePackSerializerOptions polyOptions))
- throw new ArgumentException($"You cannot use a { nameof(PolymorphicResolver) } without also using { nameof(PolymorphicMessagePackSerializerOptions) }", nameof(options));
-
- var actualtype = value.GetType();
-
- if (!polyOptions.PolymorphicSettings.TypeToId.TryGetValue(actualtype, out var typeId))
- throw new MessagePackSerializationException($"Type '{ actualtype.FullName }' is not registered in { nameof(PolymorphicMessagePackSerializerOptions) }");
-
- writer.WriteArrayHeader(2);
- writer.WriteInt32(typeId);
-
- //Bottleneck
- polyOptions.PolymorphicResolver.InnerSerialize(actualtype, ref writer, value, options);
- }
-
- public T Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
- {
-
- if (reader.TryReadNil())
- return default;
-
-
- //Could remove this if the settings were part of the regular options
- if (!(options is PolymorphicMessagePackSerializerOptions polyOptions))
- throw new ArgumentException($"You cannot use a { nameof(PolymorphicResolver) } without also using { nameof(PolymorphicMessagePackSerializerOptions) }", nameof(options));
-
- options.Security.DepthStep(ref reader);
-
- try
- {
- var count = reader.ReadArrayHeader();
-
- if (count != 2)
- throw new MessagePackSerializationException("Invalid polymorphic array count");
-
- var typeId = reader.ReadInt32();
-
- if (!polyOptions.PolymorphicSettings.IdToType.TryGetValue(typeId, out var type))
- throw new MessagePackSerializationException($"Cannot find Type Id: { typeId } registered in { nameof(PolymorphicMessagePackSerializerOptions) }");
-
- //Bottleneck
- return (T)polyOptions.PolymorphicResolver.InnerDeserialize(type, ref reader, options);
- }
- finally
- {
- reader.Depth--;
- }
-
- }
-
- }
-
-}
\ No newline at end of file
diff --git a/Blah/PolymorphicMessagePackSerializerOptions.cs b/Blah/PolymorphicMessagePackSerializerOptions.cs
deleted file mode 100644
index 54bb189..0000000
--- a/Blah/PolymorphicMessagePackSerializerOptions.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-using MessagePack;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace PolymorphicMessagePack
-{
-
-
- public class PolymorphicMessagePackSerializerOptions : MessagePackSerializerOptions
- {
- internal readonly PolymorphicMessagePackSettings PolymorphicSettings;
- internal readonly PolymorphicResolver PolymorphicResolver;
-
- public PolymorphicMessagePackSerializerOptions(PolymorphicMessagePackSettings polymorphicSettings) : base(new PolymorphicResolver(polymorphicSettings))
- {
- PolymorphicSettings = polymorphicSettings;
- PolymorphicResolver = Resolver as PolymorphicResolver;
- }
-
- protected PolymorphicMessagePackSerializerOptions(PolymorphicMessagePackSerializerOptions copyFrom) : base(copyFrom)
- {
- PolymorphicSettings = copyFrom.PolymorphicSettings;
- }
-
- protected override MessagePackSerializerOptions Clone()
- {
- return new PolymorphicMessagePackSerializerOptions(this);
- }
-
- }
-}
diff --git a/Blah/PolymorphicMessagePackSettings.cs b/Blah/PolymorphicMessagePackSettings.cs
deleted file mode 100644
index 81985b8..0000000
--- a/Blah/PolymorphicMessagePackSettings.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-using MessagePack;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace PolymorphicMessagePack
-{
- public class PolymorphicMessagePackSettings
- {
- internal readonly Dictionary TypeToId = new();
- internal readonly Dictionary IdToType = new();
- internal readonly HashSet BaseTypes = new();
- internal IFormatterResolver InnerResolver;
- internal Type InnerResolverType;
-
- public PolymorphicMessagePackSettings(IFormatterResolver innerResolver)
- {
- InnerResolver = innerResolver;
- InnerResolverType = InnerResolver.GetType();
- }
-
- public bool SerializeOnlyRegisteredTypes { get; set; } = false;
-
- public void RegisterType(int typeId)
- where B : class
- where T : class, B
- {
-
- if (typeof(T).IsInterface || typeof(T).IsAbstract)
- throw new ArgumentException($"Failed to register derived type '{ typeof(T).FullName }'. It cannot be an interface or an abstract class.", nameof(T));
-
- if (typeof(T).ContainsGenericParameters)
- throw new ArgumentException($"Failed to register derived type '{ typeof(T).FullName }'. It cannot have open generic parameters. You must replace the open generic parameters with specific types.", nameof(T));
-
- if (TypeToId.TryGetValue(typeof(T), out var currentId) && currentId != typeId)
- throw new ArgumentException($"Failed to register derived type '{ typeof(T).FullName }'. Type '{ typeof(T).FullName }' is already registered to Type Id: { currentId }", nameof(T));
-
- if (IdToType.TryGetValue(typeId, out var currentType) && currentType != typeof(T))
- throw new ArgumentException($"Failed to register derived type '{ typeof(T).FullName }'. Type Id: { typeId } is already registered to another type '{ currentType.FullName }'", nameof(typeId));
-
- //Use TryAdd, becasue the type could already exist and the user is simply trying to add another base class
- TypeToId.TryAdd(typeof(T), typeId);
- IdToType.TryAdd(typeId, typeof(T));
- BaseTypes.Add(typeof(B));
- }
-
-
- //TODO: convenience method
- //public void RegisterTypeWithAllInterfacesAndBase(int typeId, bool includeObject = false)
- // where T : class
- //{
-
- //}
-
- //TODO: What is the user needs to register 10,000 types? perhaps a way to do entire namspaaces with auto-numbering, if you aren't storing messages?
- }
-}
diff --git a/Blah/PolymorphicResolver.cs b/Blah/PolymorphicResolver.cs
deleted file mode 100644
index 1eccdac..0000000
--- a/Blah/PolymorphicResolver.cs
+++ /dev/null
@@ -1,86 +0,0 @@
-using MessagePack;
-using MessagePack.Formatters;
-using MessagePack.Resolvers;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace PolymorphicMessagePack
-{
-
- internal sealed class PolymorphicResolver : IFormatterResolver
- {
- private PolymorphicMessagePackSettings _polymorphicSettings;
- private readonly ConcurrentDictionary _innerFormatterCache;
- public PolymorphicResolver(PolymorphicMessagePackSettings polymorphicSettings)
- {
- _polymorphicSettings = polymorphicSettings;
- _innerFormatterCache = new();
- }
-
- public IMessagePackFormatter GetFormatter()
- {
- //Have to check the type here, because of two reasons:
- //1. Deserialize will not work with non-polymorphic types, as it assumes a typeid as the first item in a two part array, the second being the object itself
- //2. We need the polymorphic settings, and they are an instance and are required to be.
-
- //If i had the object to be serialized or its actual type, I could make this a lot more efficient and remove the need for the Polymorphic delegate.
-
- //Can something be optimized here?
- if (_polymorphicSettings.BaseTypes.Contains(typeof(T)) ||
- _polymorphicSettings.TypeToId.ContainsKey(typeof(T)))
- {
- return FormatterCache.Formatter;
- }
- else if (_polymorphicSettings.SerializeOnlyRegisteredTypes)
- {
- throw new MessagePackSerializationException($"Type '{ typeof(T).FullName }' is not registered in the { nameof(PolymorphicMessagePackSettings) } and { nameof(PolymorphicMessagePackSettings.SerializeOnlyRegisteredTypes) } is set to true");
- }
-
- return _polymorphicSettings.InnerResolver.GetFormatter();
- }
-
- //Bottleneck
- public void InnerSerialize(Type type, ref MessagePackWriter writer, object value, MessagePackSerializerOptions options)
- {
- GetDelegate(type).Serialize(ref writer, value, options);
- }
-
- //Bottleneck
- public object InnerDeserialize(Type type, ref MessagePackReader reader, MessagePackSerializerOptions options)
- {
- return GetDelegate(type).Deserialize(ref reader, options);
- }
-
- private PolymorphicDelegate GetDelegate(Type type)
- {
- if (!_innerFormatterCache.TryGetValue(type, out var ploymorphicDeletegate))
- {
- var constructedType = typeof(PolymorphicDelegate<>).MakeGenericType(type);
-
- ploymorphicDeletegate = (PolymorphicDelegate)Activator.CreateInstance(constructedType, _polymorphicSettings.InnerResolver);
-
- _innerFormatterCache.TryAdd(type, ploymorphicDeletegate);
- }
-
- return ploymorphicDeletegate;
- }
-
- private static class FormatterCache
- {
- public static IMessagePackFormatter Formatter;
-
- static FormatterCache()
- {
- Formatter = new PolymorphicFormatter();
- }
-
- }
-
- }
-
-}
\ No newline at end of file
diff --git a/Fody.Test/AutoKeyWeaverTests.cs b/Fody.Test/AutoKeyWeaverTests.cs
new file mode 100644
index 0000000..44fdca9
--- /dev/null
+++ b/Fody.Test/AutoKeyWeaverTests.cs
@@ -0,0 +1,38 @@
+using MessagePack;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+using PolymorphicMessagePack.Fody;
+using ShareAttributes;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml.Linq;
+
+#pragma warning disable CS8604
+
+namespace Fody.Test
+{
+ public class AutoKeyWeaverTests
+ {
+ static TestResult testResult;
+
+ static AutoKeyWeaverTests()
+ {
+ var xElement = XElement.Parse("");
+ var weavingTask = new AutoMsgPackKeyWeaver { Config = xElement };
+ testResult = weavingTask.ExecuteTestRun("MsgPackDefineForInject.dll", runPeVerify: false);
+ }
+
+ [Fact]
+ public void WillNotInjectNonMsgPackObjAttrClass()
+ {
+ var type = testResult.Assembly.GetType("MsgPackDefineForInject.Class2");
+
+ var attribute = type.GetCustomAttribute();
+
+ Assert.NotNull(attribute);
+ }
+ }
+}
diff --git a/Fody.Test/AutoPolyWeaverTests.cs b/Fody.Test/AutoPolyWeaverTests.cs
new file mode 100644
index 0000000..e05736b
--- /dev/null
+++ b/Fody.Test/AutoPolyWeaverTests.cs
@@ -0,0 +1,66 @@
+using PolymorphicMessagePack.Fody;
+using ShareAttributes;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml.Linq;
+
+#pragma warning disable CS8604
+
+namespace Fody.Test
+{
+ public class AutoPolyWeaverTests
+ {
+ static TestResult testResult;
+
+ static AutoPolyWeaverTests()
+ {
+ var xElement = XElement.Parse("");
+ var weavingTask = new AutoPolyMsgPackWeaver { Config= xElement };
+ testResult = weavingTask.ExecuteTestRun("MsgPackDefineForInject.dll",runPeVerify:false);
+ }
+
+ [Fact]
+ public void WillNotInjectNonMsgPackObjAttrClass()
+ {
+ var type = testResult.Assembly.GetType("MsgPackDefineForInject.Class2");
+
+ var attribute = type.GetCustomAttribute();
+
+ Assert.Null(attribute);
+ }
+
+ [Fact]
+ public void InjectMsgPackObjAttrClass()
+ {
+ var type = testResult.Assembly.GetType("MsgPackDefineForInject.Class6");
+
+ var attribute = type.GetCustomAttribute();
+
+ Assert.NotNull(attribute);
+ }
+
+ [Fact]
+ public void InjectGenericUnionType()
+ {
+ var type = testResult.Assembly.GetType("MsgPackDefineForInject.Class5`1");
+
+ var attribute = type.GetCustomAttributes();
+
+ Assert.True(attribute.Count() == 2);
+ }
+
+ [Fact]
+ public void InjectGenericUnionWithSameType()
+ {
+ var type = testResult.Assembly.GetType("MsgPackDefineForInject.Class7`1");
+
+ var attribute = type.GetCustomAttributes();
+
+ Assert.True(attribute.Count() == 2);
+ }
+ }
+}
diff --git a/Fody.Test/Fody.Test.csproj b/Fody.Test/Fody.Test.csproj
new file mode 100644
index 0000000..a330162
--- /dev/null
+++ b/Fody.Test/Fody.Test.csproj
@@ -0,0 +1,31 @@
+
+
+
+ net6.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
+
diff --git a/Fody.Test/Usings.cs b/Fody.Test/Usings.cs
new file mode 100644
index 0000000..8c927eb
--- /dev/null
+++ b/Fody.Test/Usings.cs
@@ -0,0 +1 @@
+global using Xunit;
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..2072e21
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 PatchouliTC
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/MsgPackDefineForInject/AbsDefine.cs b/MsgPackDefineForInject/AbsDefine.cs
new file mode 100644
index 0000000..b564f8f
--- /dev/null
+++ b/MsgPackDefineForInject/AbsDefine.cs
@@ -0,0 +1,37 @@
+using ShareAttributes;
+
+namespace MsgPackDefineForInject
+{
+
+ //[UnionAbsOrInterface]
+ //public abstract class CBase1
+ //{
+ //}
+
+ //public abstract class CBase2
+ //{
+ // public long CTB2 { get; set; }
+ //}
+
+ //[UnionAbsOrInterface]
+ //public abstract class CBase3 : CBase1
+ //{
+
+ //}
+
+ //[UnionAbsOrInterface]
+ //public abstract class CBase4
+ //{
+
+ //}
+
+ //public abstract class CBase5 : CBase4
+ //{
+
+ //}
+ //[UnionAbsOrInterface]
+ //public abstract class CBase6 : CBase4
+ //{
+
+ //}
+}
diff --git a/MsgPackDefineForInject/FodyWeavers.xml b/MsgPackDefineForInject/FodyWeavers.xml
new file mode 100644
index 0000000..c064b07
--- /dev/null
+++ b/MsgPackDefineForInject/FodyWeavers.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/MsgPackDefineForInject/InterfaceDefine.cs b/MsgPackDefineForInject/InterfaceDefine.cs
new file mode 100644
index 0000000..6cf0cf0
--- /dev/null
+++ b/MsgPackDefineForInject/InterfaceDefine.cs
@@ -0,0 +1,37 @@
+using ShareAttributes;
+
+namespace MsgPackDefineForInject
+{
+ //[UnionAbsOrInterface]
+ //public interface IBase1
+ //{
+
+ //}
+
+ //public interface IBase2
+ //{
+
+ //}
+
+ //[UnionAbsOrInterface]
+ //public interface IBase3 : IBase1
+ //{
+
+ //}
+
+ //[UnionAbsOrInterface]
+ //public interface IBase4
+ //{
+
+ //}
+
+ //public interface IBase5 : IBase4
+ //{
+
+ //}
+ //[UnionAbsOrInterface]
+ //public interface IBase6 : IBase4
+ //{
+
+ //}
+}
diff --git a/MsgPackDefineForInject/MsgPackClass.cs b/MsgPackDefineForInject/MsgPackClass.cs
new file mode 100644
index 0000000..9d1327e
--- /dev/null
+++ b/MsgPackDefineForInject/MsgPackClass.cs
@@ -0,0 +1,109 @@
+using AbsInjectTypeDll.DllSubAssembly;
+using MessagePack;
+using ShareAttributes;
+using System.Runtime.Serialization;
+
+namespace MsgPackDefineForInject
+{
+ [MessagePackObject]
+ public class Class1 : CBase1
+ {
+
+ public long CT1 { get; set; }
+ }
+
+
+ [MessagePackObject]
+ public class Class2 : CBase2
+ {
+
+ public long CT2 { get; set; }
+ }
+
+ [MessagePackObject]
+ public class Class2_1 : Class2
+ {
+ public long CT2_1 { get; set; }
+ }
+ [MessagePackObject]
+ public class Class2_2 : Class2
+ {
+ public long CT2_2 { get; set; }
+ }
+
+ [RequireUnion(114514)]
+ [MessagePackObject]
+ public class Class3 : Class1
+ {
+
+ public long CT3 { get; set; }
+ }
+
+
+ [MessagePackObject]
+ public class Class4 : CBase4
+ {
+
+ public long CT4 { get; set; }
+ }
+
+ [GenericUnion(typeof(int))]
+ [GenericUnion(typeof(string))]
+ [MessagePackObject]
+ public class Class5 : CBase6
+ {
+ public long CT5 { get; set; }
+ }
+
+ [MessagePackObject]
+ public class Class6 : IBase3
+ {
+ [Key(0)]
+ public long CT6 { get; set; }
+
+ public long CT6_1 { get; set; } = 12306;
+ private long CT6_2;
+ }
+
+
+ [GenericUnion(typeof(int))]
+ [GenericUnion(typeof(int))]
+ [GenericUnion(typeof(string))]
+ [GenericUnion(typeof(string))]
+ [MessagePackObject]
+ public class Class7 : CBase6
+ {
+ public long CT7 { get; set; }
+ }
+
+
+ [RequireUnionGeneric(1,typeof(Class8))]
+ [RequireUnionGeneric(2,typeof(Class8))]
+ [MessagePackObject]
+ public class Class8 : CBase6
+ {
+ public long CT8 { get; set; }
+ }
+
+ [MessagePackObject]
+ public struct Struct1
+ {
+ private long ST1 { get; set; }
+
+ [Key(1)]
+ public long ST2 { get; set; }
+
+ public long ST3;
+
+ private long ST4;
+
+ internal long ST5;
+
+ public long STT5
+ {
+ get => ST5;
+ set => ST5 = value;
+ }
+
+ }
+}
diff --git a/MsgPackDefineForInject/MsgPackDefineForInject.csproj b/MsgPackDefineForInject/MsgPackDefineForInject.csproj
new file mode 100644
index 0000000..b630ad5
--- /dev/null
+++ b/MsgPackDefineForInject/MsgPackDefineForInject.csproj
@@ -0,0 +1,34 @@
+
+
+
+ netstandard2.1
+ true
+ false
+
+
+
+ 5
+
+
+
+ 5
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PolyClient/PolyClient.csproj b/PolyClient/PolyClient.csproj
new file mode 100644
index 0000000..44af787
--- /dev/null
+++ b/PolyClient/PolyClient.csproj
@@ -0,0 +1,19 @@
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PolyClient/Program.cs b/PolyClient/Program.cs
new file mode 100644
index 0000000..89f52a1
--- /dev/null
+++ b/PolyClient/Program.cs
@@ -0,0 +1,44 @@
+
+
+using AbsInjectTypeDll.DllSubAssembly;
+using Grpc.Net.Client;
+using MagicOnion.Client;
+using MessagePack.Resolvers;
+using MessagePack;
+using MsgPackDefineForInject;
+using Service.Shared;
+using PolymorphicMessagePack;
+
+namespace PolyClient
+{
+ internal class Program
+ {
+ static async Task Main(string[] args)
+ {
+ RegisterResolvers();
+ // Connect to the server using gRPC channel.
+ var channel = GrpcChannel.ForAddress("https://localhost:5001");
+
+ // NOTE: If your project targets non-.NET Standard 2.1, use `Grpc.Core.Channel` class instead.
+ // var channel = new Channel("localhost", 5001, new SslCredentials());
+
+ // Create a proxy to call the server transparently.
+ var client = MagicOnionClient.Create(channel);
+
+ // Call the server-side method using the proxy.
+ var resultClass = await client.GetTestData(3);
+ Console.WriteLine($"Result: {resultClass.GetType().Name}");
+ }
+
+ static void RegisterResolvers()
+ {
+ var polySettings = new PolymorphicMessagePackSettings(StandardResolver.Instance);
+
+ polySettings.InjectUnionRequireFromAssembly(typeof(Class1).Assembly, typeof(CBase1).Assembly);
+
+ var _polyOptions = new PolymorphicMessagePackSerializerOptions(polySettings);
+
+ MessagePackSerializer.DefaultOptions = _polyOptions;
+ }
+ }
+}
\ No newline at end of file
diff --git a/PolyMsgPack.Test/AutoMsgPackTest.cs b/PolyMsgPack.Test/AutoMsgPackTest.cs
new file mode 100644
index 0000000..d76fdbf
--- /dev/null
+++ b/PolyMsgPack.Test/AutoMsgPackTest.cs
@@ -0,0 +1,29 @@
+using MessagePack;
+using MsgPackDefineForInject;
+using PolymorphicMessagePack;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PolyMsgPack.Test
+{
+ [TestClass]
+ public class AutoMsgPackTest
+ {
+ public AutoMsgPackTest()
+ {
+
+ }
+
+ [TestMethod]
+ public void AutoMarkKey()
+ {
+ var s = MessagePackSerializer.Serialize(new Class2_2() { CT2 = 2, CT2_2 = 22, CTB2 = 12 });
+
+ var ds= MessagePackSerializer.Deserialize(s);
+ Assert.IsNotNull(ds);
+ }
+ }
+}
diff --git a/PolyMsgPack.Test/PolyMsgPack.Test.csproj b/PolyMsgPack.Test/PolyMsgPack.Test.csproj
new file mode 100644
index 0000000..5ab241d
--- /dev/null
+++ b/PolyMsgPack.Test/PolyMsgPack.Test.csproj
@@ -0,0 +1,25 @@
+
+
+
+ net6.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PolyMsgPack.Test/PolyMsgPackTest.cs b/PolyMsgPack.Test/PolyMsgPackTest.cs
new file mode 100644
index 0000000..dd6eb35
--- /dev/null
+++ b/PolyMsgPack.Test/PolyMsgPackTest.cs
@@ -0,0 +1,83 @@
+using AbsInjectTypeDll.DllSubAssembly;
+using MessagePack;
+using MessagePack.Resolvers;
+using MsgPackDefineForInject;
+using PolymorphicMessagePack;
+
+namespace PolyMsgPack.Test
+{
+
+ [TestClass]
+ public class PolyMsgPackTest
+ {
+ PolymorphicMessagePackSerializerOptions _polyOptions;
+ public PolyMsgPackTest()
+ {
+ var polySettings = new PolymorphicMessagePackSettings(StandardResolver.Instance);
+
+ polySettings.InjectUnionRequireFromAssembly(typeof(Class1).Assembly,typeof(CBase1).Assembly);
+
+ _polyOptions = new PolymorphicMessagePackSerializerOptions(polySettings);
+
+ MessagePackSerializer.DefaultOptions = _polyOptions;
+ }
+
+ [TestMethod]
+ public void Test_NonGenericToAbs()
+ {
+ var s = MessagePackSerializer.Serialize(new Class3 { CT1 = 3,CT3=4});
+ var ds = MessagePackSerializer.Deserialize(s);
+
+ Assert.IsTrue(ds is Class1 ds1 && ds1.CT1 == 3);
+ }
+
+ [TestMethod]
+ public void Test_NotMarkClassSer()
+ {
+ var s = MessagePackSerializer.Serialize(new Class2 { CT2 = 1 }, _polyOptions);
+ Assert.ThrowsException(() => MessagePackSerializer.Deserialize(s, _polyOptions));
+ }
+
+ [TestMethod]
+ public void Test_S_DSToSubOtherSubClass()
+ {
+ var s = MessagePackSerializer.Serialize(new Class3 { CT3 = 3, CT1 = 1 }, _polyOptions);
+ var ds = MessagePackSerializer.Deserialize(s, _polyOptions);
+ Assert.IsTrue(ds.CT1 == 1);
+ }
+
+ [TestMethod]
+ public void Test_S_DSDriveGenericClass()
+ {
+ var s = MessagePackSerializer.Serialize(new Class4 { CT4 = 4 }, _polyOptions);
+ var ds = MessagePackSerializer.Deserialize(s, _polyOptions);
+ Assert.IsTrue(ds.CT4 == 4);
+ }
+
+ [TestMethod]
+ public void Test_S_DSGenericClass()
+ {
+ var s = MessagePackSerializer.Serialize(new Class5 { CT5 = 5 }, _polyOptions);
+ var ds = MessagePackSerializer.Deserialize>(s, _polyOptions);
+ Assert.IsTrue(ds.CT5 == 5);
+ }
+
+ [TestMethod]
+ public void Test_S_DSWrongGenericClass()
+ {
+ var s = MessagePackSerializer.Serialize(new Class5 { CT5 = 5 }, _polyOptions);
+ var ds = MessagePackSerializer.Deserialize>(s, _polyOptions);
+ Assert.IsNull(ds);
+ }
+
+ [TestMethod]
+ public void Test_S_DS_MultiInterface()
+ {
+ var s = MessagePackSerializer.Serialize(new Class6(), _polyOptions);
+ var ds1 = MessagePackSerializer.Deserialize(s, _polyOptions);
+ var ds2 = MessagePackSerializer.Deserialize(s, _polyOptions);
+ Assert.IsNotNull(ds1);
+ Assert.IsNotNull(ds2);
+ }
+ }
+}
diff --git a/PolyMsgPack.Test/Usings.cs b/PolyMsgPack.Test/Usings.cs
new file mode 100644
index 0000000..ab67c7e
--- /dev/null
+++ b/PolyMsgPack.Test/Usings.cs
@@ -0,0 +1 @@
+global using Microsoft.VisualStudio.TestTools.UnitTesting;
\ No newline at end of file
diff --git a/PolyServer/MyFirstService.cs b/PolyServer/MyFirstService.cs
new file mode 100644
index 0000000..64d4b6d
--- /dev/null
+++ b/PolyServer/MyFirstService.cs
@@ -0,0 +1,32 @@
+using MagicOnion.Server;
+using MagicOnion;
+using Service.Shared;
+using MsgPackDefineForInject;
+using AbsInjectTypeDll.DllSubAssembly;
+
+namespace PolyServer
+{
+ // Implements RPC service in the server project.
+ // The implementation class must inherit `ServiceBase` and `IMyFirstService`
+ public class MyFirstService : ServiceBase, IMyFirstService
+ {
+ // `UnaryResult` allows the method to be treated as `async` method.
+ public async UnaryResult SumAsync(int x, int y)
+ {
+ Console.WriteLine($"Received:{x}, {y}");
+ return x + y;
+ }
+
+ public async UnaryResult DoWorkAsync()
+ {
+ // Something to do ...
+ }
+
+ public async UnaryResult GetTestData(int x)
+ {
+ Console.WriteLine($"Received:{x}");
+ var package=new Class3() { CT1 = x, CT3 = 3 };
+ return package;
+ }
+ }
+}
diff --git a/PolyServer/PolyServer.csproj b/PolyServer/PolyServer.csproj
new file mode 100644
index 0000000..05e01ec
--- /dev/null
+++ b/PolyServer/PolyServer.csproj
@@ -0,0 +1,22 @@
+
+
+
+ net6.0
+ enable
+ enable
+ 1998
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PolyServer/Program.cs b/PolyServer/Program.cs
new file mode 100644
index 0000000..5812a84
--- /dev/null
+++ b/PolyServer/Program.cs
@@ -0,0 +1,35 @@
+using AbsInjectTypeDll.DllSubAssembly;
+using MagicOnion;
+using MagicOnion.Serialization;
+using MagicOnion.Server;
+using MessagePack;
+using MessagePack.Resolvers;
+using Microsoft.AspNetCore;
+using MsgPackDefineForInject;
+using PolymorphicMessagePack;
+using Service.Shared;
+
+namespace PolyServer
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ CreateWebHostBuilder(args).Build().Run();
+ }
+ public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
+ WebHost.CreateDefaultBuilder(args)
+ .ConfigureLogging(
+ (hostingContext, logging) =>
+ {
+ logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
+ logging.AddConsole();
+ logging.AddDebug();
+ logging.AddEventSourceLogger();
+ }
+ )
+ .UseUrls()
+ .UseKestrel()
+ .UseStartup();
+ }
+}
\ No newline at end of file
diff --git a/PolyServer/Properties/launchSettings.json b/PolyServer/Properties/launchSettings.json
new file mode 100644
index 0000000..dd893bc
--- /dev/null
+++ b/PolyServer/Properties/launchSettings.json
@@ -0,0 +1,48 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:59481",
+ "sslPort": 44308
+ }
+ },
+ "profiles": {
+ "Environment-Develop": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": false,
+ "applicationUrl": "https://localhost:7152;http://localhost:5105",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "Environment-Staging": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": false,
+ "applicationUrl": "https://localhost:7152;http://localhost:5105",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Staging",
+ "ASPNETCORE_DETAILEDERRORS": "1",
+ "ASPNETCORE_SHUTDOWNTIMEOUTSECONDS": "3"
+ }
+ },
+ "Environment-Production": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": false,
+ "applicationUrl": "https://localhost:7152;http://localhost:5105",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Production"
+ }
+ },
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/PolyServer/Startup.cs b/PolyServer/Startup.cs
new file mode 100644
index 0000000..f54a21c
--- /dev/null
+++ b/PolyServer/Startup.cs
@@ -0,0 +1,60 @@
+using AbsInjectTypeDll.DllSubAssembly;
+using Grpc.Net.Client;
+using MagicOnion.Server;
+using MessagePack.Resolvers;
+using MsgPackDefineForInject;
+using PolymorphicMessagePack;
+
+namespace PolyServer
+{
+ public class Startup
+ {
+ public Startup(IConfiguration configuration)
+ {
+ Configuration = configuration;
+ }
+
+ public IConfiguration Configuration { get; }
+
+ // This method gets called by the runtime. Use this method to add services to the container.
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services.AddControllersWithViews();
+
+ services.AddGrpc(); // MagicOnion depends on ASP.NET Core gRPC service.
+
+ var polySettings = new PolymorphicMessagePackSettings(StandardResolver.Instance);
+ polySettings.InjectUnionRequireFromAssembly(typeof(Class1).Assembly, typeof(CBase1).Assembly);
+ var _polyOptions = new PolymorphicMessagePackSerializerOptions(polySettings);
+
+ services.AddMagicOnion(options =>
+ {
+ options.MessageSerializer = new MagicOnionPolyMsgPackSerializerProvider(_polyOptions);
+ });
+ }
+
+ // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+ {
+ if (env.IsDevelopment())
+ {
+ app.UseDeveloperExceptionPage();
+ }
+
+ app.UseHttpsRedirection();
+
+ app.UseRouting();
+
+ app.UseEndpoints(endpoints =>
+ {
+ endpoints.MapMagicOnionHttpGateway("_",
+ app.ApplicationServices.GetService()!.MethodHandlers,
+ GrpcChannel.ForAddress($"https://localhost:{Configuration["GrpcChannelPort"]}"));
+ endpoints.MapMagicOnionSwagger("mo/swagger",
+ app.ApplicationServices.GetService()!.MethodHandlers,
+ "/_/");
+ endpoints.MapMagicOnionService();
+ });
+ }
+ }
+}
diff --git a/PolyServer/appsettings.Development.json b/PolyServer/appsettings.Development.json
new file mode 100644
index 0000000..ffed010
--- /dev/null
+++ b/PolyServer/appsettings.Development.json
@@ -0,0 +1,21 @@
+{
+ "Logging": {
+ "Debug": {
+ "LogLevel": {
+ "Default": "Warning",
+ "Microsoft": "Warning"
+ }
+ },
+ "LogLevel": {
+ "Default": "Warning",
+ "Microsoft": "Warning"
+ }
+ },
+ "AllowedHosts": "*",
+ "Kestrel": {
+ "EndpointDefaults": {
+ "Protocols": "Http1AndHttp2"
+ }
+ },
+ "GrpcChannelPort": 5001
+}
diff --git a/PolyServer/appsettings.json b/PolyServer/appsettings.json
new file mode 100644
index 0000000..2bd99c2
--- /dev/null
+++ b/PolyServer/appsettings.json
@@ -0,0 +1,21 @@
+{
+ "Logging": {
+ "Debug": {
+ "LogLevel": {
+ "Default": "Warning",
+ "Microsoft": "Warning"
+ }
+ },
+ "LogLevel": {
+ "Default": "Warning",
+ "Microsoft": "Warning"
+ }
+ },
+ "AllowedHosts": "*",
+ "Kestrel": {
+ "EndpointDefaults": {
+ "Protocols": "Http1AndHttp2"
+ }
+ },
+ "GrpcChannelPort":5001
+}
diff --git a/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs b/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs
new file mode 100644
index 0000000..9fc0484
--- /dev/null
+++ b/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs
@@ -0,0 +1,508 @@
+using Fody;
+using MessagePack;
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+using Mono.Cecil.Rocks;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Runtime.Serialization;
+using System.Text;
+
+namespace PolymorphicMessagePack.Fody
+{
+ public class AutoMsgPackKeyWeaver : BaseModuleWeaver
+ {
+ public override bool ShouldCleanReference => true;
+
+ //require check
+ Type _msgObjAttr = typeof(MessagePackObjectAttribute);
+ Type _msgDataContractAttr = typeof(DataContractAttribute);
+
+ //ignore prop
+ Type _msgIgnoreAttr = typeof(IgnoreMemberAttribute);
+ Type _msgDataIgnoreAttr = typeof(IgnoreDataMemberAttribute);
+
+ //mark key
+ Type _msgKeyAttr = typeof(KeyAttribute);
+ Type _msgDataMemberAttr = typeof(DataMemberAttribute);
+
+ string _msgPackAttrAsName;
+
+ //object for stop while
+ TypeDefinition _objectTypeDef;
+ //Auto property created private field attribute
+ TypeDefinition _autoPropFieldAttrDef;
+ //key mark def
+ TypeDefinition _keyMarkDef;
+
+ TypeDefinition _msgIgnoreDef;
+ TypeDefinition _msgDataIgnoreDef;
+
+ MethodReference _msgIgnoreConstructor;
+
+ MethodReference _msgKeyConstructor;
+
+ MethodDefinition _objctor;
+
+ internal class ReferenceTreeNode
+ {
+ internal TypeDefinition node;
+ internal HashSet ManualMarkedIdKeys = new HashSet();
+ internal HashSet CurrentTypeOwnIdKeys= new HashSet();
+ internal List RequireAddKeyFields=new List();
+ internal List RequireAddKeyProperties=new List();
+ internal ReferenceTreeNode Parent;
+ internal List Childs=new List();
+ }
+
+ public AutoMsgPackKeyWeaver()
+ {
+ _msgPackAttrAsName = _msgObjAttr.Assembly.GetName().Name;
+
+ }
+
+ public override void Execute()
+ {
+ var ns = GetStringFromConfig("NameSpace");
+ if (ns == null)
+ {
+ WriteError("Scan Assembly name not set in config");
+ return;
+ }
+ var markPrivateField = GetBoolFromConfig("AlsoMarkPrivateField");
+ var markBaseTypeIgnoreMem = GetBoolFromConfig("MarkIgnoreToFieldForNonMsgPackBaseType");
+
+ InitBasicRequireRef();
+
+ //get all mark with [MessageObject] class
+ var requireConsiderAbsAndInterfaceTypes = ModuleDefinition.Types.Where(
+ x => x.HasCustomAttributes && //has attr
+ x.Namespace == ns && // in target namespace
+ !x.IsAbstract && //not abstract class
+ !x.IsInterface && //not interface
+ (x.IsClass || (x.IsValueType && !x.IsEnum && x.IsSealed)) && // is class or struct
+ x.CustomAttributes.Any(y =>
+ y.AttributeType.FullName == _msgObjAttr.FullName ||
+ y.AttributeType.FullName== _msgDataContractAttr.FullName)); //has messageobject
+
+ //check can mark
+ foreach(var type in requireConsiderAbsAndInterfaceTypes)
+ {
+ if (!type.IsPublic)
+ {
+ WriteError($"{type.FullName} not public type but marked [MessageObject] or [DataContract]");
+ return;
+ }
+ }
+
+ var refTree = GetDerivedRelationprivate(ModuleDefinition, requireConsiderAbsAndInterfaceTypes);
+
+ //scan each type,analysis each type own require marked fields,property,and check each manual set Key Ids
+ foreach (var typeNode in refTree)
+ {
+ //internal:IsAssembly;private:IsPrivate;public:IsPublic
+ var currentTypeRequireMarkCollection = GetNodeRequireMarkFieldAndProperties(typeNode.node, markPrivateField);
+ //Scan and record manual set keys
+ foreach (var field in currentTypeRequireMarkCollection.Item1)
+ {
+ var keyAttrs = field.CustomAttributes.Where(x => x.AttributeType.FullName == _msgKeyAttr.FullName ||
+ x.AttributeType.FullName == _msgDataMemberAttr.FullName);
+ if (keyAttrs.Count() > 1)
+ {
+ WriteError($"{typeNode.node.FullName}->Field:{field.Name} has both [Key] and [DataMember]");
+ return;
+ }
+ else if (keyAttrs.Count() == 1)
+ {
+ var checkedAttr = keyAttrs.First();
+ if (!CheckAndReigsterManualSetKeys(typeNode, checkedAttr))
+ return;
+ }
+ else
+ {
+ //not set attr
+ typeNode.RequireAddKeyFields.Add(field);
+ }
+ }
+
+ foreach (var property in currentTypeRequireMarkCollection.Item2)
+ {
+ var keyAttrs = property.CustomAttributes.Where(x => x.AttributeType.FullName == _msgKeyAttr.FullName ||
+ x.AttributeType.FullName == _msgDataMemberAttr.FullName);
+ if (keyAttrs.Count() > 1)
+ {
+ WriteError($"{typeNode.node.FullName}->Property:{property.Name} has both [Key] and [DataMember]");
+ return;
+ }
+ else if (keyAttrs.Count() == 1)
+ {
+ var checkedAttr = keyAttrs.First();
+ if (!CheckAndReigsterManualSetKeys(typeNode, checkedAttr))
+ return;
+ }
+ else
+ {
+ //bool HasDefaultValueSet = PropHasDefaultValue(typeNode.node, property);
+ //not set attr
+ typeNode.RequireAddKeyProperties.Add(property);
+ }
+ }
+
+ }
+ //each node get in that type which field and prop need mark,and get each type manual mark key ids
+ //get all end node,check each ancestors used ids
+ HashSet visitedNode= new HashSet();
+ foreach(var typeNode in refTree.Where(x => x.Childs.Count == 0))
+ {
+ if (visitedNode.Contains(typeNode))
+ continue;
+ visitedNode.Add(typeNode);
+ var current = typeNode;
+ var next = current.Parent;
+ while (next != null)
+ {
+ if (next.CurrentTypeOwnIdKeys.Overlaps(current.ManualMarkedIdKeys))
+ {
+ WriteError($"{current.node.FullName} has conflict key id with {next.node.FullName}");
+ return;
+ }
+ //attach current manual keys to parent manual keys
+ //let parent know can't generate key ids which derive type used
+ next.ManualMarkedIdKeys.UnionWith(current.ManualMarkedIdKeys);
+
+ current = next;
+ next=current.Parent;
+ }
+ }
+
+
+ Stack lastBaseTypeUsedId= new Stack();
+ Stack> requireVisitChildNodes = new Stack>();
+ //Mark Key
+ foreach(var typeNode in refTree.Where(x => x.Parent == null))
+ {
+ //each base type start with key id 0
+ MarkTreeNode(typeNode, 0, markBaseTypeIgnoreMem);
+ }
+ }
+ private bool PropHasDefaultValue(TypeDefinition type,PropertyDefinition property)
+ {
+ //must class can property set default value
+ if (!type.IsClass || type.IsValueType ||type.IsInterface) return false;
+
+ var defaultCtor = type.GetConstructors().Where(x => 0 == x.Parameters.Count).FirstOrDefault();
+ if (defaultCtor == null) return false;
+ var autoFieldName = $"<{property.Name}>k__BackingField";
+ var propAutoField = type.Fields.Where(x => x.Name == autoFieldName).FirstOrDefault();
+ if (propAutoField == null) return false;
+
+ var instructions = defaultCtor.Body.Instructions;
+ foreach (var instruction in instructions)
+ {
+ //only check default ctor operators
+ if (instruction.OpCode.Code == OpCodes.Call.Code && instruction.Operand.ToString() == _objctor.FullName)
+ break;
+
+ if (instruction.OpCode.Code == OpCodes.Stfld.Code && instruction.Operand.ToString() == propAutoField.FullName)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void MarkTreeNode(ReferenceTreeNode root,int startId,bool markBaseTypeIgnoreMem)
+ {
+ //if config choose no [MessagePackObject] type set all fields to [ignoreMumber]
+ if (markBaseTypeIgnoreMem && !root.node.CustomAttributes.Any(x=>x.AttributeType.FullName==_msgObjAttr.FullName))
+ {
+ foreach(var field in root.RequireAddKeyFields)
+ {
+ var attribute = new CustomAttribute(_msgIgnoreConstructor);
+ field.CustomAttributes.Add(attribute);
+ }
+ foreach (var property in root.RequireAddKeyProperties)
+ {
+ var attribute = new CustomAttribute(_msgIgnoreConstructor);
+ property.CustomAttributes.Add(attribute);
+ }
+ }
+ else
+ {
+ MarkNodeFields(root, ref startId);
+ }
+ foreach(var deriveType in root.Childs)
+ {
+ MarkTreeNode(deriveType, startId, markBaseTypeIgnoreMem);
+ }
+ }
+ private void MarkNodeFields(ReferenceTreeNode node,ref int startId)
+ {
+ foreach(var field in node.RequireAddKeyFields)
+ {
+ while (node.ManualMarkedIdKeys.Contains(startId))
+ startId++;
+
+ var attribute = new CustomAttribute(_msgKeyConstructor);
+ attribute.ConstructorArguments.Add(
+ new CustomAttributeArgument(
+ _msgKeyConstructor.Parameters[0].ParameterType,
+ startId));
+ field.CustomAttributes.Add(attribute);
+ startId++;
+ }
+
+ foreach(var property in node.RequireAddKeyProperties)
+ {
+ while (node.ManualMarkedIdKeys.Contains(startId))
+ startId++;
+
+ var attribute = new CustomAttribute(_msgKeyConstructor);
+ attribute.ConstructorArguments.Add(
+ new CustomAttributeArgument(
+ _msgKeyConstructor.Parameters[0].ParameterType,
+ startId));
+ property.CustomAttributes.Add(attribute);
+ startId++;
+ }
+ }
+
+ private bool CheckAndReigsterManualSetKeys(ReferenceTreeNode node,CustomAttribute attribute)
+ {
+ int manualSetKey;
+ //if is KeyAttribute and use KeyAttribute(int key)
+ if (attribute.AttributeType.FullName == _msgKeyAttr.FullName &&
+ attribute.ConstructorArguments[0].Type.FullName == TypeSystem.Int32Reference.FullName)
+ {
+ manualSetKey = (int)attribute.ConstructorArguments[0].Value;
+ }
+ //if is DataMemberAttribute and use DataMemberAttribute(Order=int)
+ else if (attribute.AttributeType.FullName == _msgDataMemberAttr.FullName &&
+ attribute.Properties.Any(x => x.Name == "Order"))
+ {
+ manualSetKey = (int)attribute.Properties.Where(x => x.Name == "Order").First().Argument.Value;
+ }
+ //other set with name,not consider,continue
+ else
+ {
+ return true;
+ }
+ //check if same key in one type
+ if (node.ManualMarkedIdKeys.Contains(manualSetKey))
+ {
+ WriteError($"{node.node.FullName} has marked same key {manualSetKey} for more than one field or property");
+ return false;
+ }
+ node.ManualMarkedIdKeys.Add(manualSetKey);
+ node.CurrentTypeOwnIdKeys.Add(manualSetKey);
+ return true;
+ }
+
+ private (List,List) GetNodeRequireMarkFieldAndProperties(TypeDefinition targetType, bool markPrivateField)
+ {
+ List considerFields = null;
+ //ignore mark [IgnoreMember],[IgnoreDataMember]
+ var query = targetType.Fields.Where(x => !x.CustomAttributes.Any(y =>
+ y.AttributeType.FullName == _msgIgnoreDef.FullName ||
+ y.AttributeType.FullName == _msgDataIgnoreDef.FullName));
+
+ //make a copy
+ if (markPrivateField)
+ considerFields = query.ToList();
+ else
+ considerFields = query.Where(x => !(x.IsAssembly || x.IsPrivate) ||
+ x.CustomAttributes.Any(y => y.AttributeType.FullName == _autoPropFieldAttrDef.FullName)).ToList();
+
+ var considerProperties = new List();
+ //Auto Field ignore :{k__BackingField}+{System.Runtime.CompilerServices.CompilerGeneratedAttribute}
+ //Auto Field Match: k__BackingField
+
+ //only consider auto-prop properties,not auto-prop most will be logic and not able to serialize/deserialize
+ foreach (var property in targetType.Properties)
+ {
+ //remove existed auto-prop field
+ var autoFieldName = $"<{property.Name}>k__BackingField";
+ //auto field only has one field or not
+ var relateAutoField = considerFields.Where(x =>
+ x.IsPrivate &&
+ x.Name == autoFieldName &&
+ x.HasCustomAttributes &&
+ x.CustomAttributes.Any(y => y.AttributeType.FullName == _autoPropFieldAttrDef.FullName)
+ ).FirstOrDefault();
+ if (relateAutoField != null)
+ {
+ // remove them from considerFields
+ considerFields.Remove(relateAutoField);
+ }
+
+ //ignore manual mark [IgnoreMember],[IgnoreDataMember]
+ if (property.HasCustomAttributes &&
+ property.CustomAttributes.Any(x =>
+ x.AttributeType.FullName == _msgIgnoreDef.FullName ||
+ x.AttributeType.FullName == _msgDataIgnoreDef.FullName))
+ continue;
+
+ if(relateAutoField == null)
+ {
+ //not auto-prop + no [IgnoreMember]
+ var attribute = new CustomAttribute(_msgIgnoreConstructor);
+ property.CustomAttributes.Add(attribute);
+ continue;
+ }
+
+ // check Accessibility [ignore internal,private]
+ // prop won't have Accessibility mark,must check get/set method Accessibility
+ // for property,get/set must has at last one public
+ var publicGetter = property.GetMethod != null && property.GetMethod.IsPublic ? true : false;
+ var publicSetter = property.SetMethod != null && property.SetMethod.IsPublic ? true : false;
+
+ if (!markPrivateField && !(publicGetter || publicSetter))
+ {
+ //ignore this prop,but this prop not set [IgnoreMember],add it into that property
+ var attribute = new CustomAttribute(_msgIgnoreConstructor);
+ property.CustomAttributes.Add(attribute);
+ continue;
+ }
+
+ considerProperties.Add(property);
+ }
+ return(considerFields,considerProperties);
+ }
+
+ ///
+ /// analysis all [MessagePackObject] class derived relation tree [from base abstract to final class]
+ ///
+ ///
+ ///
+ ///
+ private List GetDerivedRelationprivate(
+ ModuleDefinition module,
+ IEnumerable types)
+ {
+ List referenceTree = new List();
+
+
+ foreach (var typeDef in types)
+ {
+ //target type exist,ignore
+ if (referenceTree.Any(x=>x.node.FullName==typeDef.FullName))
+ continue;
+
+ //struct
+ //Must check first beause struct also IsClass
+ if (typeDef.IsValueType)
+ {
+ var newCurrentType = new ReferenceTreeNode() { node = typeDef };
+ referenceTree.Add(newCurrentType);
+ }
+ else if(typeDef.IsClass)
+ {
+ var newCurrentType = new ReferenceTreeNode() { node = typeDef };
+ referenceTree.Add(newCurrentType);
+
+ var nextcheck = newCurrentType;
+
+ while (nextcheck != null && nextcheck.node.FullName != _objectTypeDef.FullName)
+ {
+ var baseType= nextcheck.node.BaseType?.Resolve();
+ if (baseType == null || baseType.FullName == _objectTypeDef.FullName)
+ break;
+
+ var existType=referenceTree.Where(x=>x.node.FullName==baseType.FullName).FirstOrDefault();
+
+ if(existType == null)
+ {
+ var newBaseType = new ReferenceTreeNode() { node = baseType };
+ referenceTree.Add(newBaseType);
+ newBaseType.Childs.Add(nextcheck);
+ nextcheck.Parent = newBaseType;
+ nextcheck = newBaseType;
+ }
+ else
+ {
+ existType.Childs.Add(nextcheck);
+ nextcheck.Parent = existType;
+ break;
+ }
+ }
+ }
+ }
+ return referenceTree;
+ }
+
+
+
+ public override IEnumerable GetAssembliesForScanning()
+ {
+ yield return "netstandard";
+ yield return "mscorlib";
+ yield return "System";
+ yield return "System.Runtime";
+ }
+
+ void InitBasicRequireRef()
+ {
+ _objectTypeDef = ModuleDefinition.ImportReference(TypeSystem.ObjectDefinition).Resolve();
+ _objctor = _objectTypeDef.GetConstructors().Where(x => 0 == x.Parameters.Count).First();
+
+ _autoPropFieldAttrDef = ModuleDefinition.ImportReference(typeof(CompilerGeneratedAttribute)).Resolve();
+
+ _msgDataIgnoreDef= ModuleDefinition.ImportReference(_msgDataIgnoreAttr).Resolve();
+
+ var msgPackAssembly = new AssemblyNameReference(_msgPackAttrAsName, null);
+ try
+ {
+ _keyMarkDef = ModuleDefinition.AssemblyResolver.Resolve(msgPackAssembly).
+ MainModule.Types.
+ Single(type => type.Name == _msgKeyAttr.Name);
+
+ _msgIgnoreDef = ModuleDefinition.ImportReference(_msgIgnoreAttr).Resolve();
+
+ var msgIgnoreConstructor = _msgIgnoreDef
+ .GetConstructors()
+ .Single(ctor =>
+ 0 == ctor.Parameters.Count);
+ _msgIgnoreConstructor = ModuleDefinition.ImportReference(msgIgnoreConstructor);
+
+ var msgKeyConstructor = _keyMarkDef
+ .GetConstructors()
+ .Single(ctor =>
+ 1 == ctor.Parameters.Count &&
+ TypeSystem.Int32Reference.FullName == ctor.Parameters[0].ParameterType.FullName);
+ _msgKeyConstructor = ModuleDefinition.ImportReference(msgKeyConstructor);
+
+ }
+ catch (Exception ex)
+ {
+ WriteError($"Init Basic TypeDefine Failed ({ex.Message})");
+ }
+ }
+
+ #region GetAssemblyNameSpaceAttributes
+ string GetStringFromConfig(string name)
+ {
+ var attribute = Config?.Attribute(name);
+ if (attribute == null)
+ {
+ return null;
+ }
+ var value = attribute.Value;
+ return value;
+ }
+
+ bool GetBoolFromConfig(string name)
+ {
+ var attribute = Config?.Attribute(name);
+ if (attribute == null)
+ return false;
+ var value = (bool?)attribute;
+ if (value == null)
+ return false;
+ return value.Value;
+ }
+ #endregion
+ }
+}
diff --git a/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.xcf b/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.xcf
new file mode 100644
index 0000000..cf0ef52
--- /dev/null
+++ b/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.xcf
@@ -0,0 +1,23 @@
+
+
+
+
+ Namespace to use for the injected type
+
+
+
+
+ Also scan and mark private field
+
+
+
+
+
+ For class Base Types,if they don't have [MessagePackObject],set their field with [IgnoreMember] if not manual mark
+
+
+
+
\ No newline at end of file
diff --git a/PolymorphicMessagePack.Fody/AutoPolyMsgPackWeaver.cs b/PolymorphicMessagePack.Fody/AutoPolyMsgPackWeaver.cs
new file mode 100644
index 0000000..1444daa
--- /dev/null
+++ b/PolymorphicMessagePack.Fody/AutoPolyMsgPackWeaver.cs
@@ -0,0 +1,419 @@
+using Fody;
+using MessagePack;
+using Mono.Cecil;
+using Mono.Cecil.Rocks;
+using ShareAttributes;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+
+namespace PolymorphicMessagePack.Fody
+{
+ public class AutoPolyMsgPackWeaver : BaseModuleWeaver
+ {
+ public override bool ShouldCleanReference => true;
+
+ Type _absAttr = typeof(UnionAbsOrInterfaceAttribute);
+ Type _genericUnion = typeof(GenericUnionAttribute);
+ Type _reqUnionAttr = typeof(RequireUnionAttribute);
+ Type _reqGenericUnionAttr = typeof(RequireUnionGenericAttribute);
+
+ Type _msgObj = typeof(MessagePackObjectAttribute);
+
+ string _polyAttrAsName;
+ string _msgPackAttrAsName;
+
+ TypeDefinition _objectTypeRef;
+
+ TypeDefinition _requireUnionAttrRef;
+ MethodReference _requireUnionAttrConstructor;
+
+ TypeDefinition _requireUnionGenericAttrRef;
+ MethodReference _requireUnionGenericAttrConstructor;
+
+
+ public AutoPolyMsgPackWeaver()
+ {
+ _polyAttrAsName = _absAttr.Assembly.GetName().Name;
+ _msgPackAttrAsName=_msgObj.Assembly.GetName().Name;
+ }
+
+ public override void Execute()
+ {
+ var ns = GetValueFromConfig("NameSpace");
+ var absns = GetValueFromConfig("AbsNameSpace");
+
+ if(ns == null)
+ {
+ WriteError("Scan Assembly name not set in config");
+ return;
+ }
+ InitBasicRequireRef();
+
+ IEnumerable requireConsiderAbsAndInterfaceTypes;
+
+ if (absns == null)
+ {
+ //find abs from target namespace
+ requireConsiderAbsAndInterfaceTypes = ModuleDefinition.Types.Where(
+ x => x.HasCustomAttributes &&
+ x.Module.Assembly.Name.Name == ns &&
+ x.CustomAttributes.Any(y => y.AttributeType.FullName == _absAttr.FullName));
+ }
+ else
+ {
+ var absLocationAssembly = ModuleDefinition.AssemblyReferences.Where(x => x.Name == absns).FirstOrDefault();
+ if (absLocationAssembly == null)
+ {
+ WriteError($"Not Find abstract type assembly {absns} in {ns} reference assemblies");
+ return;
+ }
+ var resolveTypes = ModuleDefinition.AssemblyResolver.Resolve(absLocationAssembly).
+ MainModule.Types;
+ requireConsiderAbsAndInterfaceTypes = resolveTypes.Where(
+ x => x.HasCustomAttributes &&
+ x.Module.Assembly.Name.Name == absns &&
+ x.CustomAttributes.Any(y => y.AttributeType.FullName == _absAttr.FullName));
+ }
+
+ var resultsForNonGenericTypes = GetNonGenericDerivedTypes(ModuleDefinition, requireConsiderAbsAndInterfaceTypes).OrderBy(x => x.FullName);
+
+ var resultsForGenericTypes = GetGenericDerivedTypes(ModuleDefinition, requireConsiderAbsAndInterfaceTypes).OrderBy(x => x.Item1.FullName);
+
+ //pass nongeneric types
+ HashSet ignoreNonGenericTypes=new HashSet();
+ //record all manual marked used static ids
+ Dictionary manualMarkUsedIdForNonGenericTypes = new Dictionary();
+
+ Dictionary manualMarkUsedIdForGenericTypes = new Dictionary();
+
+ //check non generic first
+ foreach(var derivedType in resultsForNonGenericTypes)
+ {
+ if(derivedType.HasCustomAttributes &&
+ derivedType.CustomAttributes.Any(x => x.AttributeType.FullName == _reqGenericUnionAttr.FullName || x.AttributeType.FullName==_genericUnion.FullName))
+ {
+ WriteError($"Error: {derivedType.FullName} set generic types(this is non generic type)");
+ return;
+ }
+ //If Marked [RequireUnionAttribute] by manual
+ if (derivedType.HasCustomAttributes &&
+ derivedType.CustomAttributes.Any(x=>x.AttributeType.FullName== _reqUnionAttr.FullName))
+ {
+ var markedReqUnionAttr=derivedType.CustomAttributes.Single(x=>x.AttributeType.FullName==_reqUnionAttr.FullName);
+
+ //public RequireUnionAttribute(uint unionUniqueId)
+ var manualSetId = (uint)markedReqUnionAttr.ConstructorArguments[0].Value;
+
+ if (manualMarkUsedIdForNonGenericTypes.TryGetValue(manualSetId,out var existUsedIdType))
+ {
+ WriteError($"Error: {manualSetId} set for diff types:{derivedType.FullName} and {existUsedIdType.FullName}");
+ return;
+ }
+ manualMarkUsedIdForNonGenericTypes.Add(manualSetId, derivedType);
+ ignoreNonGenericTypes.Add(derivedType);
+ }
+ }
+
+ foreach(var derivedGenericType in resultsForGenericTypes)
+ {
+ var classtyperef = derivedGenericType.Item1;
+
+ var relateAttributes = derivedGenericType.Item2;
+
+ if (classtyperef.HasCustomAttributes &&
+ classtyperef.CustomAttributes.Any(x => x.AttributeType.FullName == _reqUnionAttr.FullName))
+ {
+ WriteError($"Error: {classtyperef.FullName} set non generic types(this is generic type)");
+ return;
+ }
+
+ //generic type really diff to compare
+ //maybe mark with same attr
+ //e.g:
+ //[RequireUnionGenericAttribute(1,typeof(string))]
+ //[RequireUnionGenericAttribute(2,typeof(string))]
+ //[RequireUnionGenericAttribute(3,typeof(int))]
+ //[RequireUnionGenericAttribute(4,typeof(int))]
+ //forget it
+ //visit classtyperef attributes
+
+ //public RequireUnionGenericAttribute(uint unionUniqueId, Type supportGeneric)
+ if (classtyperef.HasCustomAttributes &&
+ classtyperef.CustomAttributes.Any(x => x.AttributeType.FullName == _requireUnionGenericAttrRef.FullName))
+ {
+ //using type to group
+ var groupsTypeAttributes = classtyperef.CustomAttributes
+ .Where(x => x.AttributeType.FullName == _requireUnionGenericAttrRef.FullName)
+ .GroupBy(y => y.ConstructorArguments[1].Value.ToString());
+ foreach(var groupAttr in groupsTypeAttributes)
+ {
+ //only one mark
+ if (groupAttr.Count()==1)
+ {
+ var targetAttr = groupAttr.First();
+
+ var manualSetId = (uint)targetAttr.ConstructorArguments[0].Value;
+ var typeName = targetAttr.ConstructorArguments[1].Value.ToString();
+
+ var setType = (TypeReference)targetAttr.ConstructorArguments[1].Value;
+ var setTypeDef = setType.Resolve();
+
+ if (!setType.IsGenericInstance || setTypeDef.FullName!=classtyperef.FullName)
+ {
+ WriteError($"{classtyperef.FullName} fixed id manaully,but target union type not {classtyperef.FullName} generic type (is {setTypeDef.FullName})");
+ return;
+ }
+
+ if (manualMarkUsedIdForNonGenericTypes.TryGetValue(manualSetId, out var _)
+ || manualMarkUsedIdForGenericTypes.TryGetValue(manualSetId, out var _))
+ {
+ string outputString;
+ var typeForNonGeneric = manualMarkUsedIdForNonGenericTypes.TryGetValue(manualSetId, out var existNonType);
+ var typeForGeneric = manualMarkUsedIdForGenericTypes.TryGetValue(manualSetId, out var existTypeWithGeneric);
+ if (typeForNonGeneric)
+ outputString = existNonType.FullName;
+ else
+ outputString = $"{existTypeWithGeneric.Item1.FullName}-{existTypeWithGeneric.Item2.ConstructorArguments[1].Value}";
+ WriteError($"Error: {manualSetId} set for diff types:{classtyperef.FullName}-{typeName} and {outputString}");
+ return;
+ }
+
+ manualMarkUsedIdForGenericTypes.Add(manualSetId, (classtyperef, targetAttr));
+ //remove same type mark attr in attributes
+ //remove from class
+ var existOldAttributes = classtyperef.CustomAttributes.Where(x =>
+ x.AttributeType.FullName==_genericUnion.FullName &&
+ x.ConstructorArguments[0].Value.ToString() == typeName).ToList();
+ foreach (var oldAttribute in existOldAttributes)
+ classtyperef.CustomAttributes.Remove(oldAttribute);
+
+ //remove from auto generate attr
+ existOldAttributes = relateAttributes.Where(x =>
+ x.AttributeType.FullName == _genericUnion.FullName &&
+ x.ConstructorArguments[0].Value.ToString() == typeName).ToList();
+ foreach (var oldAttribute in existOldAttributes)
+ relateAttributes.Remove(oldAttribute);
+ }
+ //multi manual set
+ else
+ {
+ WriteError($"Error: {classtyperef.FullName}-{groupAttr.Key} has more than one mark");
+ return;
+ }
+ }
+ }
+
+ }
+
+ uint autoIncreaseIdForPoly = 1;
+ while (manualMarkUsedIdForNonGenericTypes.ContainsKey(autoIncreaseIdForPoly) || manualMarkUsedIdForGenericTypes.ContainsKey(autoIncreaseIdForPoly))
+ autoIncreaseIdForPoly++;
+
+ //Add [RequireUnion(uint x)] into target class which from marked abs/interface and marked [MessageObject]
+ foreach (var derivedType in resultsForNonGenericTypes)
+ {
+ if (ignoreNonGenericTypes.Contains(derivedType))
+ continue;
+
+ var attribute = new CustomAttribute(_requireUnionAttrConstructor);
+ attribute.ConstructorArguments.Add(
+ new CustomAttributeArgument(
+ _requireUnionAttrConstructor.Parameters[0].ParameterType,
+ autoIncreaseIdForPoly));
+ derivedType.CustomAttributes.Add(attribute);
+ autoIncreaseIdForPoly++;
+ while (manualMarkUsedIdForNonGenericTypes.ContainsKey(autoIncreaseIdForPoly) || manualMarkUsedIdForGenericTypes.ContainsKey(autoIncreaseIdForPoly))
+ autoIncreaseIdForPoly++;
+ }
+
+ //Add [RequireUnionGeneric(uint x,Type y)] into target class which from marked abs/interface and marked [MessageObject]
+ foreach (var derivedType in resultsForGenericTypes)
+ {
+ var classtyperef = derivedType.Item1;
+ var relateAttributes = derivedType.Item2.OrderBy(x => x.ConstructorArguments[0].Value.ToString());
+ foreach (var defineoldAttribute in relateAttributes)
+ {
+ //using marked class type to package generic
+ var newClassdefWithGeneric = new GenericInstanceType(classtyperef);
+ newClassdefWithGeneric.GenericArguments.Add(defineoldAttribute.ConstructorArguments[0].Value as TypeReference);
+
+ var attribute = new CustomAttribute(_requireUnionGenericAttrConstructor);
+
+ attribute.ConstructorArguments.Add(new CustomAttributeArgument(_requireUnionGenericAttrConstructor.Parameters[0].ParameterType, autoIncreaseIdForPoly));
+ attribute.ConstructorArguments.Add(new CustomAttributeArgument(_requireUnionGenericAttrConstructor.Parameters[1].ParameterType, newClassdefWithGeneric));
+
+ var existOldAttributes = classtyperef.CustomAttributes.Where(x => x.ConstructorArguments[0].Value.ToString() == defineoldAttribute.ConstructorArguments[0].Value.ToString()).ToList();
+ foreach (var oldAttribute in existOldAttributes)
+ classtyperef.CustomAttributes.Remove(oldAttribute);
+
+ classtyperef.CustomAttributes.Add(attribute);
+
+ autoIncreaseIdForPoly++;
+ while (manualMarkUsedIdForNonGenericTypes.ContainsKey(autoIncreaseIdForPoly) || manualMarkUsedIdForGenericTypes.ContainsKey(autoIncreaseIdForPoly))
+ autoIncreaseIdForPoly++;
+ }
+ }
+ WriteInfo($"Generate Auto PolyMark Run Finished.(Used for {ns})");
+ }
+
+ private List GetNonGenericDerivedTypes(
+ ModuleDefinition module,
+ IEnumerable baseTypes)
+ {
+ //get all non generic class types
+ //also must has [MessagePackObject]
+ var classdefs = module.GetTypes().
+ Where(x =>
+ !x.IsAbstract &&
+ x.IsClass &&
+ !x.HasGenericParameters &&
+ x.HasCustomAttributes &&
+ x.CustomAttributes.Any(y => y.AttributeType.FullName == _msgObj.FullName));
+
+ List derivedtypes = new List();
+
+ foreach (var classdef in classdefs)
+ {
+ if (derivedtypes.Contains(classdef))
+ continue;
+
+ var nextcheck = classdef;
+
+ while (nextcheck != null && nextcheck.FullName != _objectTypeRef.FullName)
+ {
+ var baseType = baseTypes.Where(x => x.IsAbstract && x.FullName == nextcheck.FullName).FirstOrDefault();
+ if (baseType != null)
+ {
+ derivedtypes.Add(classdef);
+ break;
+ }
+
+ nextcheck = nextcheck.BaseType?.Resolve();
+ }
+
+ if (!derivedtypes.Contains(classdef))
+ {
+ var relateInterfaces = baseTypes.Where(x => x.IsInterface && classdef.Interfaces.Any(y => y.InterfaceType.FullName == x.FullName));
+ if (relateInterfaces.Count() > 0)
+ {
+ derivedtypes.Add(classdef);
+ }
+ }
+ }
+ return derivedtypes;
+ }
+
+ private List<(TypeDefinition, List)> GetGenericDerivedTypes(
+ ModuleDefinition module,
+ IEnumerable baseTypes)
+ {
+ var classdefs = module.GetTypes().
+ Where(x =>
+ !x.IsAbstract &&
+ x.IsClass &&
+ x.HasGenericParameters &&
+ x.HasCustomAttributes &&
+ x.CustomAttributes.Any(y => y.AttributeType.FullName == _msgObj.FullName));
+
+ List<(TypeDefinition, List)> derivedtypes = new List<(TypeDefinition, List)>();
+
+ foreach (var classdef in classdefs)
+ {
+ //avoid same generic type repeat attr
+ var genericUnionTypes = classdef.CustomAttributes
+ .Where(x => x.AttributeType.FullName == _genericUnion.FullName)
+ .ToLookup(y => y.ConstructorArguments[0].Value.ToString())
+ .Select(z => z.First()).ToList();
+
+ //package
+ var package = (classdef, genericUnionTypes);
+
+ var nextcheck = classdef;
+ bool isInAnyAbs = false;
+ while (nextcheck != null && nextcheck.FullName != _objectTypeRef.FullName)
+ {
+ var baseType = baseTypes.Where(x => x.IsAbstract && x.FullName == nextcheck.FullName).FirstOrDefault();
+ if (baseType != null)
+ {
+ derivedtypes.Add(package);
+ isInAnyAbs = true;
+ break;
+ }
+ nextcheck = nextcheck.BaseType?.Resolve();
+ }
+ if (!isInAnyAbs)
+ {
+ //check all interface base type
+ var relateInterfaces = baseTypes.Where(x => x.IsInterface && classdef.Interfaces.Any(y => (y.InterfaceType.IsGenericInstance && y.InterfaceType.GetElementType().FullName == x.FullName) ||
+ (!y.InterfaceType.IsGenericInstance && y.InterfaceType.FullName == x.FullName)));
+ if (relateInterfaces.Count() > 0)
+ {
+ derivedtypes.Add(package);
+ }
+ }
+
+ }
+ return derivedtypes;
+ }
+
+ public override IEnumerable GetAssembliesForScanning()
+ {
+ yield return "netstandard";
+ yield return "mscorlib";
+ yield return "System";
+ yield return "System.Runtime";
+ }
+
+ void InitBasicRequireRef()
+ {
+ _objectTypeRef = ModuleDefinition.ImportReference(TypeSystem.ObjectDefinition).Resolve();
+ var msgPackAssembly = new AssemblyNameReference(_msgPackAttrAsName, null);
+ var polyAttrAssembly = new AssemblyNameReference(_polyAttrAsName, null);
+ try
+ {
+ _requireUnionAttrRef = ModuleDefinition.AssemblyResolver.Resolve(polyAttrAssembly).
+ MainModule.Types.
+ Single(type => type.Name == _reqUnionAttr.Name);
+ _requireUnionGenericAttrRef = ModuleDefinition.AssemblyResolver.Resolve(polyAttrAssembly).
+ MainModule.Types.
+ Single(type => type.Name == _reqGenericUnionAttr.Name);
+
+ var requireUnionAttrConstructor= _requireUnionAttrRef
+ .GetConstructors()
+ .Single(ctor =>
+ 1 == ctor.Parameters.Count &&
+ TypeSystem.UInt32Reference.FullName == ctor.Parameters[0].ParameterType.FullName);
+ _requireUnionAttrConstructor = ModuleDefinition.ImportReference(requireUnionAttrConstructor);
+
+ var requireUnionGenericAttrConstructor=_requireUnionGenericAttrRef
+ .GetConstructors()
+ .Single(ctor =>
+ 2 == ctor.Parameters.Count &&
+ TypeSystem.UInt32Reference.FullName == ctor.Parameters[0].ParameterType.FullName &&
+ "System.Type" == ctor.Parameters[1].ParameterType.FullName);
+ _requireUnionGenericAttrConstructor=ModuleDefinition.ImportReference(requireUnionGenericAttrConstructor);
+
+ }
+ catch (Exception ex)
+ {
+ WriteError($"Init Basic TypeDefine Failed ({ex.Message})");
+ }
+ }
+
+ #region GetAssemblyNameSpaceAttributes
+ string GetValueFromConfig(string name)
+ {
+ var attribute = Config?.Attribute(name);
+ if (attribute == null)
+ {
+ return null;
+ }
+
+ var value = attribute.Value;
+ return value;
+ }
+ #endregion
+ }
+}
diff --git a/PolymorphicMessagePack.Fody/AutoPolyMsgPackWeaver.xcf b/PolymorphicMessagePack.Fody/AutoPolyMsgPackWeaver.xcf
new file mode 100644
index 0000000..feee850
--- /dev/null
+++ b/PolymorphicMessagePack.Fody/AutoPolyMsgPackWeaver.xcf
@@ -0,0 +1,15 @@
+
+
+
+
+ Namespace to use for the injected type
+
+
+
+
+ Abstruct Type in space
+
+
+
\ No newline at end of file
diff --git a/PolymorphicMessagePack.Fody/PolymorphicMessagePack.Fody.csproj b/PolymorphicMessagePack.Fody/PolymorphicMessagePack.Fody.csproj
new file mode 100644
index 0000000..1ab5aec
--- /dev/null
+++ b/PolymorphicMessagePack.Fody/PolymorphicMessagePack.Fody.csproj
@@ -0,0 +1,17 @@
+
+
+
+ netstandard2.0
+ true
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PolymorphicMessagePack.Fody/Properties/AssemblyInfo.cs b/PolymorphicMessagePack.Fody/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..5f28270
--- /dev/null
+++ b/PolymorphicMessagePack.Fody/Properties/AssemblyInfo.cs
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/PolymorphicMessagePack.csproj b/PolymorphicMessagePack.csproj
index bdaba76..dc02cad 100644
--- a/PolymorphicMessagePack.csproj
+++ b/PolymorphicMessagePack.csproj
@@ -1,11 +1,53 @@
- net5.0
+ netstandard2.1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PolymorphicMessagePack.sln b/PolymorphicMessagePack.sln
index aa8274d..f56c37e 100644
--- a/PolymorphicMessagePack.sln
+++ b/PolymorphicMessagePack.sln
@@ -1,9 +1,30 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
-VisualStudioVersion = 17.0.32112.339
+VisualStudioVersion = 17.5.33516.290
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PolymorphicMessagePack", "PolymorphicMessagePack\PolymorphicMessagePack.csproj", "{F7E1E83F-3593-4946-8DFB-9E8E1EAEC6E7}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PolymorphicMessagePack", "PolymorphicMessagePack.csproj", "{8DF39770-8DBD-4647-A73D-7B362CD07CBA}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PolyMsgPack.Test", "PolyMsgPack.Test\PolyMsgPack.Test.csproj", "{A1C98719-901D-43FA-9FE8-F80441835D4A}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ShareAttributes", "ShareAttributes\ShareAttributes.csproj", "{D0E6414F-4F35-41E9-A10B-AF0042E9D0EB}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MsgPackDefineForInject", "MsgPackDefineForInject\MsgPackDefineForInject.csproj", "{8CF34B2D-452A-4C0D-BFDC-10789FA3E145}"
+ ProjectSection(ProjectDependencies) = postProject
+ {B9E1906C-3F9D-49AB-8B9A-A7413B513BFC} = {B9E1906C-3F9D-49AB-8B9A-A7413B513BFC}
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PolymorphicMessagePack.Fody", "PolymorphicMessagePack.Fody\PolymorphicMessagePack.Fody.csproj", "{B9E1906C-3F9D-49AB-8B9A-A7413B513BFC}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fody.Test", "Fody.Test\Fody.Test.csproj", "{863134AE-368E-46E9-8D60-72AC9EDBA107}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AbsInjectTypeDll", "AbsInjectTypeDll\AbsInjectTypeDll.csproj", "{FFC7FCC9-C9A6-4896-8807-670D52C725AD}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PolyServer", "PolyServer\PolyServer.csproj", "{1F2010FA-8685-4FB4-85FA-48A29D9EF74B}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Service.Shared", "Service.Shared\Service.Shared.csproj", "{EF0D7F0D-4BB8-41D7-95BC-E3066FEF6341}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PolyClient", "PolyClient\PolyClient.csproj", "{43F43EB2-326E-40F5-9678-EC1402949286}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -11,15 +32,51 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {F7E1E83F-3593-4946-8DFB-9E8E1EAEC6E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F7E1E83F-3593-4946-8DFB-9E8E1EAEC6E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F7E1E83F-3593-4946-8DFB-9E8E1EAEC6E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F7E1E83F-3593-4946-8DFB-9E8E1EAEC6E7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8DF39770-8DBD-4647-A73D-7B362CD07CBA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8DF39770-8DBD-4647-A73D-7B362CD07CBA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8DF39770-8DBD-4647-A73D-7B362CD07CBA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8DF39770-8DBD-4647-A73D-7B362CD07CBA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A1C98719-901D-43FA-9FE8-F80441835D4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A1C98719-901D-43FA-9FE8-F80441835D4A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A1C98719-901D-43FA-9FE8-F80441835D4A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A1C98719-901D-43FA-9FE8-F80441835D4A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D0E6414F-4F35-41E9-A10B-AF0042E9D0EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D0E6414F-4F35-41E9-A10B-AF0042E9D0EB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D0E6414F-4F35-41E9-A10B-AF0042E9D0EB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D0E6414F-4F35-41E9-A10B-AF0042E9D0EB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8CF34B2D-452A-4C0D-BFDC-10789FA3E145}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8CF34B2D-452A-4C0D-BFDC-10789FA3E145}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8CF34B2D-452A-4C0D-BFDC-10789FA3E145}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8CF34B2D-452A-4C0D-BFDC-10789FA3E145}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B9E1906C-3F9D-49AB-8B9A-A7413B513BFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B9E1906C-3F9D-49AB-8B9A-A7413B513BFC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B9E1906C-3F9D-49AB-8B9A-A7413B513BFC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B9E1906C-3F9D-49AB-8B9A-A7413B513BFC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {863134AE-368E-46E9-8D60-72AC9EDBA107}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {863134AE-368E-46E9-8D60-72AC9EDBA107}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {863134AE-368E-46E9-8D60-72AC9EDBA107}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {863134AE-368E-46E9-8D60-72AC9EDBA107}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FFC7FCC9-C9A6-4896-8807-670D52C725AD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FFC7FCC9-C9A6-4896-8807-670D52C725AD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FFC7FCC9-C9A6-4896-8807-670D52C725AD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FFC7FCC9-C9A6-4896-8807-670D52C725AD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1F2010FA-8685-4FB4-85FA-48A29D9EF74B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1F2010FA-8685-4FB4-85FA-48A29D9EF74B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1F2010FA-8685-4FB4-85FA-48A29D9EF74B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1F2010FA-8685-4FB4-85FA-48A29D9EF74B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EF0D7F0D-4BB8-41D7-95BC-E3066FEF6341}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EF0D7F0D-4BB8-41D7-95BC-E3066FEF6341}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EF0D7F0D-4BB8-41D7-95BC-E3066FEF6341}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EF0D7F0D-4BB8-41D7-95BC-E3066FEF6341}.Release|Any CPU.Build.0 = Release|Any CPU
+ {43F43EB2-326E-40F5-9678-EC1402949286}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {43F43EB2-326E-40F5-9678-EC1402949286}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {43F43EB2-326E-40F5-9678-EC1402949286}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {43F43EB2-326E-40F5-9678-EC1402949286}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {1641AF2D-0E2D-439C-9A01-0E20BAB57CA9}
+ SolutionGuid = {01350A72-C58D-4348-80AB-F78490469B60}
EndGlobalSection
EndGlobal
diff --git a/PolymorphicMessagePack/MagicOnionPolyMsgPackFormatter.cs b/PolymorphicMessagePack/MagicOnionPolyMsgPackFormatter.cs
new file mode 100644
index 0000000..f53c5ce
--- /dev/null
+++ b/PolymorphicMessagePack/MagicOnionPolyMsgPackFormatter.cs
@@ -0,0 +1,66 @@
+using Grpc.Core;
+using MagicOnion.Serialization;
+using MessagePack;
+using System;
+using System.Buffers;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Text;
+
+namespace PolymorphicMessagePack
+{
+ public class MagicOnionPolyMsgPackSerializerProvider : IMagicOnionSerializerProvider
+ {
+ private class MessagePackMagicOnionSerializer : IMagicOnionSerializer
+ {
+ private readonly PolymorphicMessagePackSerializerOptions serializerOptions;
+
+ public MessagePackMagicOnionSerializer(PolymorphicMessagePackSerializerOptions serializerOptions)
+ {
+ this.serializerOptions = serializerOptions;
+ }
+
+ public T Deserialize(in ReadOnlySequence bytes)
+ {
+ return MessagePackSerializer.Deserialize(in bytes, serializerOptions);
+ }
+
+ public void Serialize(IBufferWriter writer, in T value)
+ {
+ //we use origin value type instand of fact require type[maybe abstract or interface]
+ //ignore valueType
+ if (value != null && (typeof(T).IsClass||typeof(T).IsInterface))
+ {
+ MessagePackSerializer.Serialize(value.GetType(), writer, value, serializerOptions);
+ }
+ else
+ {
+ MessagePackSerializer.Serialize(writer, value);
+ }
+
+ }
+
+ void IMagicOnionSerializer.Serialize(IBufferWriter writer, in T value)
+ {
+ Serialize(writer, in value);
+ }
+
+ T IMagicOnionSerializer.Deserialize(in ReadOnlySequence bytes)
+ {
+ return Deserialize(in bytes);
+ }
+ }
+
+ protected PolymorphicMessagePackSerializerOptions SerializerOptions { get; }
+
+ public MagicOnionPolyMsgPackSerializerProvider(PolymorphicMessagePackSerializerOptions serializerOptions)
+ {
+ SerializerOptions = serializerOptions;
+ }
+
+ public IMagicOnionSerializer Create(MethodType methodType, MethodInfo methodInfo)
+ {
+ return new MessagePackMagicOnionSerializer(SerializerOptions);
+ }
+ }
+}
diff --git a/PolymorphicMessagePack/PolymorphicDelegate.cs b/PolymorphicMessagePack/PolymorphicDelegate.cs
deleted file mode 100644
index 9e851d6..0000000
--- a/PolymorphicMessagePack/PolymorphicDelegate.cs
+++ /dev/null
@@ -1,45 +0,0 @@
-using MessagePack;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace PolymorphicMessagePack
-{
- //I created this, because I have no clue how to create a delegate (MethodInfo.Invoke is too slow), that uses the generic parameter of the class (As opposed to a generic method that has it's own parameter 'T').
- //Is this the only way?
- internal abstract class PolymorphicDelegate
- {
- public abstract void Serialize(ref MessagePackWriter writer, object value, MessagePackSerializerOptions options);
- public abstract object Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options);
-
- }
-
- internal class PolymorphicDelegate : PolymorphicDelegate
- {
- private delegate void SerializeDelegate(ref MessagePackWriter writer, T value, MessagePackSerializerOptions options);
- private delegate T DeserializeDelegate(ref MessagePackReader reader, MessagePackSerializerOptions options);
-
- private SerializeDelegate _serializeDelegate;
- private DeserializeDelegate _deserializeDelegate;
-
- public PolymorphicDelegate(IFormatterResolver resolver)
- {
- var formatter = resolver.GetFormatter();
-
- _serializeDelegate = formatter.Serialize;
- _deserializeDelegate = formatter.Deserialize;
- }
-
- public override void Serialize(ref MessagePackWriter writer, object value, MessagePackSerializerOptions options)
- {
- _serializeDelegate.Invoke(ref writer, (T)value, options);
- }
-
- public override object Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
- {
- return _deserializeDelegate.Invoke(ref reader, options);
- }
- }
-}
diff --git a/PolymorphicMessagePack/PolymorphicFormatter.cs b/PolymorphicMessagePack/PolymorphicFormatter.cs
index f5386f4..323bb30 100644
--- a/PolymorphicMessagePack/PolymorphicFormatter.cs
+++ b/PolymorphicMessagePack/PolymorphicFormatter.cs
@@ -1,23 +1,85 @@
using MessagePack;
using MessagePack.Formatters;
-using MessagePack.Resolvers;
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
namespace PolymorphicMessagePack
{
+ internal interface IMessagePackDeserializeToObject
+ {
+ object Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options);
+ }
- public class PolymorphicFormatter : IMessagePackFormatter
+ public class PolymorphicFormatter : IMessagePackFormatter, IMessagePackDeserializeToObject
{
- private object lockObj = new object();
- public PolymorphicFormatter()
+ private readonly IMessagePackFormatter _formater;
+
+ public PolymorphicFormatter() { }
+
+ public PolymorphicFormatter(IFormatterResolver resolver)
{
+ _formater = resolver.GetFormatter();
+ //target method fact belong to instance
+ //var instance = Expression.Constant(formatter);
+
+ ////get method info+it's params
+ //var methodInfo = resolver.GetType().GetMethod("Serialize");
+ //var methodTypeParams = methodInfo.GetParameters().Select(m => m.ParameterType);
+ //var delegateInfo = typeof(SerializeDelegate).GetMethod("Invoke");
+ //var delegateTypeParams = delegateInfo.GetParameters().Select(m => m.ParameterType);
+ //var delegateArguments = delegateTypeParams.Select(Expression.Parameter).ToArray();
+
+ ////make a transform for delegate to Expr.parameter wrong types
+ //var convertedArguments = methodTypeParams.Zip(
+ // delegateTypeParams, delegateArguments,
+ // (methodType, delegateType, delegateArgument) =>
+ // methodType != delegateType
+ // ? (Expression)Expression.Convert(delegateArgument, methodType)
+ // : delegateArgument);
+ ////make call
+ //MethodCallExpression methodCall = Expression.Call(
+ // instance,
+ // methodInfo,
+ // convertedArguments
+ // );
+ ////transform delegate call to instance call-with return type convert
+ //Expression convertedMethodCall = delegateInfo.ReturnType == methodInfo.ReturnType
+ // ? (Expression)methodCall
+ //: Expression.Convert(methodCall, delegateInfo.ReturnType);
+
+
+ ////End
+ //_serializeDelegate = Expression.Lambda(
+ // convertedMethodCall,
+ // delegateArguments
+ // ).Compile();
+
+ ////For Deserialize,Do samething
+ //methodInfo = resolver.GetType().GetMethod("Deserialize");
+ //methodTypeParams = methodInfo.GetParameters().Select(m => m.ParameterType);
+ //delegateInfo = typeof(DeserializeDelegate).GetMethod("Invoke");
+ //delegateTypeParams = delegateInfo.GetParameters().Select(m => m.ParameterType);
+ //delegateArguments = delegateTypeParams.Select(Expression.Parameter).ToArray();
+
+ //convertedArguments = methodTypeParams.Zip(
+ // delegateTypeParams, delegateArguments,
+ // (methodType, delegateType, delegateArgument) =>
+ // methodType != delegateType
+ // ? (Expression)Expression.Convert(delegateArgument, methodType)
+ // : delegateArgument);
+ //methodCall = Expression.Call(
+ // instance,
+ // methodInfo,
+ // convertedArguments
+ // );
+ //convertedMethodCall = delegateInfo.ReturnType == methodInfo.ReturnType
+ // ? (Expression)methodCall
+ //: Expression.Convert(methodCall, delegateInfo.ReturnType);
+
+ //_deserializeDelegate = Expression.Lambda(
+ // convertedMethodCall,
+ // delegateArguments
+ // ).Compile();
}
public void Serialize(ref MessagePackWriter writer, T value, MessagePackSerializerOptions options)
@@ -31,18 +93,17 @@ public void Serialize(ref MessagePackWriter writer, T value, MessagePackSerializ
//Could remove this if the settings were part of the regular options
if (!(options is PolymorphicMessagePackSerializerOptions polyOptions))
- throw new ArgumentException($"You cannot use a { nameof(PolymorphicResolver) } without also using { nameof(PolymorphicMessagePackSerializerOptions) }", nameof(options));
+ throw new ArgumentException($"You cannot use a {nameof(PolymorphicResolver)} without also using {nameof(PolymorphicMessagePackSerializerOptions)}", nameof(options));
var actualtype = value.GetType();
if (!polyOptions.PolymorphicSettings.TypeToId.TryGetValue(actualtype, out var typeId))
- throw new MessagePackSerializationException($"Type '{ actualtype.FullName }' is not registered in { nameof(PolymorphicMessagePackSerializerOptions) }");
+ throw new MessagePackSerializationException($"Type '{actualtype.FullName}' is not registered in {nameof(PolymorphicMessagePackSerializerOptions)}");
writer.WriteArrayHeader(2);
- writer.WriteInt32(typeId);
+ writer.WriteUInt32(typeId);
- //Bottleneck
- polyOptions.PolymorphicResolver.InnerSerialize(actualtype, ref writer, value, options);
+ _formater.Serialize(ref writer, value, options);
}
public T Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
@@ -54,7 +115,7 @@ public T Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions
//Could remove this if the settings were part of the regular options
if (!(options is PolymorphicMessagePackSerializerOptions polyOptions))
- throw new ArgumentException($"You cannot use a { nameof(PolymorphicResolver) } without also using { nameof(PolymorphicMessagePackSerializerOptions) }", nameof(options));
+ throw new ArgumentException($"You cannot use a {nameof(PolymorphicResolver)} without also using {nameof(PolymorphicMessagePackSerializerOptions)}", nameof(options));
options.Security.DepthStep(ref reader);
@@ -65,13 +126,13 @@ public T Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions
if (count != 2)
throw new MessagePackSerializationException("Invalid polymorphic array count");
- var typeId = reader.ReadInt32();
+ var typeId = reader.ReadUInt32();
if (!polyOptions.PolymorphicSettings.IdToType.TryGetValue(typeId, out var type))
- throw new MessagePackSerializationException($"Cannot find Type Id: { typeId } registered in { nameof(PolymorphicMessagePackSerializerOptions) }");
+ throw new MessagePackSerializationException($"Cannot find Type Id: {typeId} registered in {nameof(PolymorphicMessagePackSerializerOptions)}");
//Bottleneck
- return (T)polyOptions.PolymorphicResolver.InnerDeserialize(type, ref reader, options);
+ return polyOptions.PolymorphicResolver.InnerDeserialize(type, ref reader, options);
}
finally
{
@@ -80,6 +141,13 @@ public T Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions
}
+ object IMessagePackDeserializeToObject.Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
+ {
+ var result = _formater.Deserialize(ref reader, options);
+ if (result is K fact)
+ return fact;
+ return default;
+ }
}
}
\ No newline at end of file
diff --git a/PolymorphicMessagePack/PolymorphicMessagePackSerializerOptions.cs b/PolymorphicMessagePack/PolymorphicMessagePackSerializerOptions.cs
index 54bb189..cb09f0f 100644
--- a/PolymorphicMessagePack/PolymorphicMessagePackSerializerOptions.cs
+++ b/PolymorphicMessagePack/PolymorphicMessagePackSerializerOptions.cs
@@ -1,9 +1,4 @@
using MessagePack;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
namespace PolymorphicMessagePack
{
@@ -11,16 +6,19 @@ namespace PolymorphicMessagePack
public class PolymorphicMessagePackSerializerOptions : MessagePackSerializerOptions
{
+
internal readonly PolymorphicMessagePackSettings PolymorphicSettings;
internal readonly PolymorphicResolver PolymorphicResolver;
- public PolymorphicMessagePackSerializerOptions(PolymorphicMessagePackSettings polymorphicSettings) : base(new PolymorphicResolver(polymorphicSettings))
+ public PolymorphicMessagePackSerializerOptions(PolymorphicMessagePackSettings polymorphicSettings)
+ : base(new PolymorphicResolver(polymorphicSettings))
{
PolymorphicSettings = polymorphicSettings;
PolymorphicResolver = Resolver as PolymorphicResolver;
}
- protected PolymorphicMessagePackSerializerOptions(PolymorphicMessagePackSerializerOptions copyFrom) : base(copyFrom)
+ protected PolymorphicMessagePackSerializerOptions(PolymorphicMessagePackSerializerOptions copyFrom)
+ : base(copyFrom)
{
PolymorphicSettings = copyFrom.PolymorphicSettings;
}
@@ -29,6 +27,5 @@ protected override MessagePackSerializerOptions Clone()
{
return new PolymorphicMessagePackSerializerOptions(this);
}
-
}
}
diff --git a/PolymorphicMessagePack/PolymorphicMessagePackSettings.cs b/PolymorphicMessagePack/PolymorphicMessagePackSettings.cs
index 81985b8..83a3c4f 100644
--- a/PolymorphicMessagePack/PolymorphicMessagePackSettings.cs
+++ b/PolymorphicMessagePack/PolymorphicMessagePackSettings.cs
@@ -1,20 +1,123 @@
using MessagePack;
+using MessagePack.Resolvers;
+using ShareAttributes;
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System.Reflection;
namespace PolymorphicMessagePack
{
+
+ internal static class GetMarkAttributeClassListExtension
+ {
+ private static readonly Type _objType = typeof(object);
+ public static (Assembly, HashSet) GetMarkUnionAbsAttributeClasses(this Assembly assembly)
+ {
+ var markdata = new HashSet(assembly.GetTypes().Where(x => (x.IsAbstract || x.IsInterface) && x.GetCustomAttribute(false) != null));
+ return (assembly, markdata);
+ }
+
+ public static Dictionary> GetAbsDriveClassTypes(this HashSet types,Assembly target)
+ {
+ var require_search_types = target.GetTypes().Where(x =>
+ //is abstract,is interface,not class,is object,in marked types,is generic are ignore
+ !x.IsAbstract && !x.IsInterface && x.IsClass && x != _objType && !types.Contains(x) && !x.IsGenericType
+ );
+
+ return AnalysisTypes(require_search_types, types);
+ }
+
+ public static Dictionary> GetAbsDriveGenericClassTypes(this HashSet types, Assembly target)
+ {
+ var require_search_types = target.GetTypes().Where(x =>
+ //is abstract,is interface,not class,is object,in marked types,not generic are ignore
+ !x.IsAbstract && !x.IsInterface && x.IsClass && x != _objType && !types.Contains(x) && x.IsGenericType
+ );
+
+ return AnalysisTypes(require_search_types, types);
+ }
+
+ private static Dictionary> AnalysisTypes(IEnumerable types, HashSet exceptTypes)
+ {
+ Dictionary> cache = new Dictionary>();
+ HashSet visitedScanInterfaceTypes = new HashSet();
+
+ foreach (var type in types)
+ {
+ Type temp = type;
+ while (temp != null && temp != _objType)
+ {
+ var factCheckType = temp.BaseType;
+ //if base type is marked abs
+ //is generic base?
+ if (temp.BaseType.IsGenericType)
+ factCheckType = temp.BaseType.GetGenericTypeDefinition();
+ if (exceptTypes.Contains(factCheckType))
+ {
+ if (!cache.TryGetValue(factCheckType, out var list))
+ {
+ list = new List() { type };
+ cache.Add(factCheckType, list);
+ }
+ else
+ list.Add(type);
+ }
+
+ if (!temp.IsAbstract && !visitedScanInterfaceTypes.Contains(temp))
+ {
+ visitedScanInterfaceTypes.Add(temp);
+ foreach (var @interface in temp.GetInterfaces())
+ {
+ //if any interface in marked interface
+ //is generic base?
+ factCheckType = @interface;
+ if (@interface.IsGenericType)
+ factCheckType = @interface.GetGenericTypeDefinition();
+ if (exceptTypes.Contains(factCheckType))
+ {
+ if (!cache.TryGetValue(factCheckType, out var list))
+ {
+ list = new List() { type };
+ cache.Add(factCheckType, list);
+ }
+ else
+ {
+ list.Add(type);
+ }
+ }
+ }
+ }
+ temp = temp.BaseType;
+ }
+ }
+
+ return cache;
+ }
+ }
+
public class PolymorphicMessagePackSettings
{
- internal readonly Dictionary TypeToId = new();
- internal readonly Dictionary IdToType = new();
- internal readonly HashSet BaseTypes = new();
+ internal readonly Dictionary TypeToId = new Dictionary();
+ internal readonly Dictionary IdToType = new Dictionary();
+ internal readonly HashSet BaseTypes = new HashSet();
+ internal readonly HashSet GenericTypes = new HashSet();
+ internal readonly HashSet Assemblies = new HashSet();
internal IFormatterResolver InnerResolver;
internal Type InnerResolverType;
+ ///
+ /// Use MsgPack StandardResolver
+ ///
+ public PolymorphicMessagePackSettings()
+ {
+ InnerResolver = StandardResolver.Instance;
+ InnerResolverType = InnerResolver.GetType();
+ }
+ ///
+ /// Use your own resolver
+ ///
+ ///
public PolymorphicMessagePackSettings(IFormatterResolver innerResolver)
{
InnerResolver = innerResolver;
@@ -23,37 +126,95 @@ public PolymorphicMessagePackSettings(IFormatterResolver innerResolver)
public bool SerializeOnlyRegisteredTypes { get; set; } = false;
- public void RegisterType(int typeId)
+ ///
+ ///
+ ///
+ /// abstract class or interface
+ /// target class
+ ///
+ ///
+ public void RegisterType(uint typeId)
where B : class
where T : class, B
{
if (typeof(T).IsInterface || typeof(T).IsAbstract)
- throw new ArgumentException($"Failed to register derived type '{ typeof(T).FullName }'. It cannot be an interface or an abstract class.", nameof(T));
+ throw new ArgumentException($"Failed to register derived type '{typeof(T).FullName}'. It cannot be an interface or an abstract class.", nameof(T));
if (typeof(T).ContainsGenericParameters)
- throw new ArgumentException($"Failed to register derived type '{ typeof(T).FullName }'. It cannot have open generic parameters. You must replace the open generic parameters with specific types.", nameof(T));
+ throw new ArgumentException($"Failed to register derived type '{typeof(T).FullName}'. It cannot have open generic parameters. You must replace the open generic parameters with specific types.", nameof(T));
if (TypeToId.TryGetValue(typeof(T), out var currentId) && currentId != typeId)
- throw new ArgumentException($"Failed to register derived type '{ typeof(T).FullName }'. Type '{ typeof(T).FullName }' is already registered to Type Id: { currentId }", nameof(T));
+ throw new ArgumentException($"Failed to register derived type '{typeof(T).FullName}'. Type '{typeof(T).FullName}' is already registered to Type Id: {currentId}", nameof(T));
if (IdToType.TryGetValue(typeId, out var currentType) && currentType != typeof(T))
- throw new ArgumentException($"Failed to register derived type '{ typeof(T).FullName }'. Type Id: { typeId } is already registered to another type '{ currentType.FullName }'", nameof(typeId));
+ throw new ArgumentException($"Failed to register derived type '{typeof(T).FullName}'. Type Id: {typeId} is already registered to another type '{currentType.FullName}'", nameof(typeId));
//Use TryAdd, becasue the type could already exist and the user is simply trying to add another base class
- TypeToId.TryAdd(typeof(T), typeId);
- IdToType.TryAdd(typeId, typeof(T));
+ TypeToId.Add(typeof(T), typeId);
+ IdToType.Add(typeId, typeof(T));
BaseTypes.Add(typeof(B));
}
+ public void InjectUnionRequireFromAssembly(Assembly assembly,Assembly absClassAssembly)
+ {
+ if (Assemblies.Contains(assembly) && Assemblies.Contains(absClassAssembly))
+ return;
+ Assemblies.Add(assembly);
+ Assemblies.Add(absClassAssembly);
+ //get all mark require union abs/interface
+ var markedUnionRequireAbsOrInterfaces = absClassAssembly.GetMarkUnionAbsAttributeClasses();
+
+ //get all drive abs/interface non generic classes
+ var allNeedMapNonGenericClasses = markedUnionRequireAbsOrInterfaces.Item2.GetAbsDriveClassTypes(assembly);
+
+ //register them,record relate abs and interface
+ foreach (var pair in allNeedMapNonGenericClasses)
+ {
+ BaseTypes.Add(pair.Key);
+ foreach (var pair2 in pair.Value)
+ {
+ if (TypeToId.ContainsKey(pair2))
+ continue;
+ var unionIdAttribute = pair2.GetCustomAttribute(false);
+ var pairAttr = pair2.CustomAttributes.First();
+ if (unionIdAttribute == null)
+ throw new ArgumentException(message: $"Shouldn't Happened---{pair2.FullName} not set RequireUnionAttribute but has been scaned");
+ if (IdToType.TryGetValue(unionIdAttribute.UnionUniqueId, out var existMarkType))
+ throw new ArgumentException(message: $"{pair2.FullName} Set union unique Id {unionIdAttribute.UnionUniqueId},but it already been used for {existMarkType.FullName}");
- //TODO: convenience method
- //public void RegisterTypeWithAllInterfacesAndBase(int typeId, bool includeObject = false)
- // where T : class
- //{
-
- //}
+ TypeToId.Add(pair2, unionIdAttribute.UnionUniqueId);
+ IdToType.Add(unionIdAttribute.UnionUniqueId, pair2);
+ }
+ }
- //TODO: What is the user needs to register 10,000 types? perhaps a way to do entire namspaaces with auto-numbering, if you aren't storing messages?
+ //get all drive abs/interface generic classes
+ var allNeedRecordGenericClasses = markedUnionRequireAbsOrInterfaces.Item2.GetAbsDriveGenericClassTypes(assembly);
+ //record all need prepare generic type class type
+ foreach (var pair in allNeedRecordGenericClasses)
+ {
+ BaseTypes.Add(pair.Key);
+ foreach (var pair2 in pair.Value)
+ {
+ var unionGenericAttributes = pair2.GetCustomAttributes(false);
+ //truth require register type
+ foreach (var factUsedRuntimeGenericVersion in unionGenericAttributes)
+ {
+ var fType = factUsedRuntimeGenericVersion.SupportGenericType;
+ if (fType.IsGenericType && fType.GetGenericTypeDefinition() == pair2)
+ {
+ if (TypeToId.ContainsKey(fType))
+ continue;
+ if (IdToType.TryGetValue(factUsedRuntimeGenericVersion.UnionUniqueId, out var existMarkType))
+ throw new ArgumentException(message: $"{fType.FullName} Set union unique Id {factUsedRuntimeGenericVersion.UnionUniqueId},but it already been used for {existMarkType.FullName}");
+ TypeToId.Add(fType, factUsedRuntimeGenericVersion.UnionUniqueId);
+ IdToType.Add(factUsedRuntimeGenericVersion.UnionUniqueId, fType);
+ }
+ else
+ throw new ArgumentException(message: $"{fType.FullName} is not genericType or not generic by {pair2.FullName}");
+ }
+ }
+ }
+ }
}
}
diff --git a/PolymorphicMessagePack/PolymorphicResolver.cs b/PolymorphicMessagePack/PolymorphicResolver.cs
index 1eccdac..4be8fff 100644
--- a/PolymorphicMessagePack/PolymorphicResolver.cs
+++ b/PolymorphicMessagePack/PolymorphicResolver.cs
@@ -1,25 +1,20 @@
using MessagePack;
using MessagePack.Formatters;
-using MessagePack.Resolvers;
using System;
using System.Collections.Concurrent;
-using System.Collections.Generic;
using System.Linq;
-using System.Reflection;
-using System.Text;
-using System.Threading.Tasks;
namespace PolymorphicMessagePack
{
internal sealed class PolymorphicResolver : IFormatterResolver
{
- private PolymorphicMessagePackSettings _polymorphicSettings;
- private readonly ConcurrentDictionary _innerFormatterCache;
+
+ private readonly PolymorphicMessagePackSettings _polymorphicSettings;
+ private readonly ConcurrentDictionary _innerDeserializeFormatterCache = new ConcurrentDictionary();
public PolymorphicResolver(PolymorphicMessagePackSettings polymorphicSettings)
{
_polymorphicSettings = polymorphicSettings;
- _innerFormatterCache = new();
}
public IMessagePackFormatter GetFormatter()
@@ -31,56 +26,49 @@ public IMessagePackFormatter GetFormatter()
//If i had the object to be serialized or its actual type, I could make this a lot more efficient and remove the need for the Polymorphic delegate.
//Can something be optimized here?
- if (_polymorphicSettings.BaseTypes.Contains(typeof(T)) ||
- _polymorphicSettings.TypeToId.ContainsKey(typeof(T)))
+ var inType = typeof(T);
+ if (_polymorphicSettings.BaseTypes.Contains(inType) || _polymorphicSettings.TypeToId.ContainsKey(inType))
{
- return FormatterCache.Formatter;
+ if (_innerDeserializeFormatterCache.TryGetValue(inType, out var formatter))
+ return (IMessagePackFormatter)formatter;
+ else
+ {
+ var targetTypeFormatter = new PolymorphicFormatter(_polymorphicSettings.InnerResolver);
+ _innerDeserializeFormatterCache.TryAdd(inType, targetTypeFormatter);
+ return targetTypeFormatter;
+ }
}
- else if (_polymorphicSettings.SerializeOnlyRegisteredTypes)
+ //generic type won't contain in settings,scan generic types
+ else if (inType.IsGenericType && _polymorphicSettings.GenericTypes.Contains(inType.GetGenericTypeDefinition()))
{
- throw new MessagePackSerializationException($"Type '{ typeof(T).FullName }' is not registered in the { nameof(PolymorphicMessagePackSettings) } and { nameof(PolymorphicMessagePackSettings.SerializeOnlyRegisteredTypes) } is set to true");
+ //Nice,this generic with generic param is marked that has union require abs/interface and not registered,register it and create formatter
+ var targetTypeFormatter = new PolymorphicFormatter(_polymorphicSettings.InnerResolver);
+ _innerDeserializeFormatterCache.TryAdd(inType, targetTypeFormatter);
+ //get max id which current used
+ var avilableId = _polymorphicSettings.IdToType.Keys.Max() + 1;
+ _polymorphicSettings.TypeToId.Add(inType, avilableId);
+ _polymorphicSettings.IdToType.Add(avilableId, inType);
+ return targetTypeFormatter;
}
+ else if (_polymorphicSettings.SerializeOnlyRegisteredTypes)
+ throw new MessagePackSerializationException($"Type '{inType.FullName}' is not registered in the {nameof(PolymorphicMessagePackSettings)} and {nameof(PolymorphicMessagePackSettings.SerializeOnlyRegisteredTypes)} is set to true");
+ //Use oher formatter
return _polymorphicSettings.InnerResolver.GetFormatter();
}
- //Bottleneck
- public void InnerSerialize(Type type, ref MessagePackWriter writer, object value, MessagePackSerializerOptions options)
- {
- GetDelegate(type).Serialize(ref writer, value, options);
- }
-
- //Bottleneck
- public object InnerDeserialize(Type type, ref MessagePackReader reader, MessagePackSerializerOptions options)
+ internal T InnerDeserialize(Type truthType, ref MessagePackReader reader, MessagePackSerializerOptions options)
{
- return GetDelegate(type).Deserialize(ref reader, options);
- }
-
- private PolymorphicDelegate GetDelegate(Type type)
- {
- if (!_innerFormatterCache.TryGetValue(type, out var ploymorphicDeletegate))
+ if (_innerDeserializeFormatterCache.TryGetValue(truthType, out var ploymorphicFormatter))
+ return (T)ploymorphicFormatter.Deserialize(ref reader, options) ?? default;
+ else
{
- var constructedType = typeof(PolymorphicDelegate<>).MakeGenericType(type);
-
- ploymorphicDeletegate = (PolymorphicDelegate)Activator.CreateInstance(constructedType, _polymorphicSettings.InnerResolver);
-
- _innerFormatterCache.TryAdd(type, ploymorphicDeletegate);
+ var constructedType = typeof(PolymorphicFormatter<>).MakeGenericType(truthType);
+ var instance = (IMessagePackDeserializeToObject)Activator.CreateInstance(constructedType, _polymorphicSettings.InnerResolver);
+ _innerDeserializeFormatterCache.TryAdd(truthType, instance);
+ return (T)instance.Deserialize(ref reader, options) ?? default;
}
-
- return ploymorphicDeletegate;
}
-
- private static class FormatterCache
- {
- public static IMessagePackFormatter Formatter;
-
- static FormatterCache()
- {
- Formatter = new PolymorphicFormatter();
- }
-
- }
-
}
}
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..99fb713
--- /dev/null
+++ b/README.md
@@ -0,0 +1,177 @@
+# AutoPolymorphicMessagePack
+Auto Union Scanner & Auto Key Generator For [MessagePack-CSharp](https://github.com/neuecc/MessagePack-CSharp)
+
+Let you use more easy way to
+
+ 1. union messagepack object
+
+ 2. auto mark [Key(x)] to public or private
+
+## How To Use
+Mark which abstract class or interface you want to union for (e.g.these class are in `Project1` project range)
+
+```C#
+ //mark abstract class
+ [UnionAbsOrInterface]
+ public abstract class CBase1
+ {
+
+ }
+ //mark interface
+ [UnionAbsOrInterface]
+ public interface IBase1
+ {
+
+ }
+ // generic interface or abstract also support
+ [UnionAbsOrInterface]
+ public interface IBase4
+ {
+
+ }
+```
+
+AutoPolymorphicMessagePack use Fody Plugin to weave in automatically at compile time,I haven't publish nuget package yet,
+so you need add [PolymorphicMessagePack.Fody](https://github.com/PatchouliTC/PolymorphicMessagePack/tree/master/PolymorphicMessagePack.Fody) into your project
+
+_For more details you can see [in-solution-weaving](https://github.com/Fody/Home/blob/master/pages/in-solution-weaving.md)_
+
+Then set your `Project1` follow these steps:
+
+ 1. import [Fody Nuget Package](https://www.nuget.org/packages/Fody) into `Project1`
+
+ **Don't worry about this package,it won't be your project reference when you compile `Project1`**
+
+ 2. add an entry to the `Project1.csproj` file:
+
+```xml
+
+
+
+```
+
+ 3. Change the [solution build order](https://docs.microsoft.com/en-au/visualstudio/ide/how-to-create-and-remove-project-dependencies) so the `Project1` project is built after the fody projects consuming it.
+ 4. Compile `Project1`,Fody will generate `FodyWeavers.xml` into project when not found that file
+
+ then write config into `FodyWeavers.xml`:
+
+```xml
+
+
+
+```
+ 5. prepare your msgpack marked object into `Project1`:
+
+```C#
+ //if non generic type,you need do nothing,Fody will add mark and unique id attr into it
+ [MessagePackObject]
+ public class Class1 : CBase1
+ {
+ [Key(0)]
+ public long CT1 { get; set; }
+ }
+
+ // if generic object,all actual generic types to be used must be declared
+ [GenericUnion(typeof(int))]
+ [GenericUnion(typeof(string))]
+ [MessagePackObject]
+ public class Class2 : IBase4
+ {
+ [Key(0)]
+ public long CT2 { get; set; }
+ }
+
+ //if you want to fixed union id manually,mark it,fody will ignore this type and avoid use fixed id to mark other types
+ [RequireUnion(1)]
+ [MessagePackObject]
+ public class Class3 : CBase1
+ {
+ [Key(0)]
+ public long CT1 { get; set; }
+ }
+
+ //also work for generic type,but you must make sure type in [RequireUnionGeneric] is current generic type or fody will give complie error
+ [RequireUnionGeneric(10,typeof(Class4))]
+ [GenericUnion(typeof(string))]
+ [MessagePackObject]
+ public class Class4 : IBase4
+ {
+ [Key(0)]
+ public long CT2 { get; set; }
+ }
+```
+ 6. using and inject `Project1` assembly into `PolymorphicMessagePackSettings` and use it
+
+```C#
+ var polySettings = new PolymorphicMessagePackSettings();
+ polySettings.InjectUnionRequireFromAssembly(typeof(Project1NameSpaceWhichContainMsgPackMarkedClass).Assembly);
+
+ //serialize fact instance
+ var s1 = MessagePackSerializer.Serialize(new Class1 { CT1 = 1 }, _options);
+
+ //deserialize it to abstract
+ var ds1 = MessagePackSerializer.Deserialize(s1, _options);
+
+ //serialize marked generic also allowed
+ var s2 = MessagePackSerializer.Serialize(new Class2 { CT2 = 2 }, _options);
+
+ //deserialize it to generic interface
+ var ds2 = MessagePackSerializer.Deserialize>(s2, _options);
+```
+
+_You can see more in [PolyMsgPack.Test](https://github.com/PatchouliTC/PolymorphicMessagePack/tree/master/PolymorphicMessagePack.Fody),[MsgPackDefineForInject](https://github.com/PatchouliTC/PolymorphicMessagePack/tree/master/MsgPackDefineForInject) and use `ILSpy` to see how it works_
+
+If you want to enable auto Key generate ,set `FodyWeavers.xml` with `AutoMsgPackKeyWeaver`
+
+```xml
+
+
+
+```
+
+Auto Key generate will scan target assembly,find all mark [MessagePackObject] type,and get their base type relate tree,then follow config to add unique Key id ([Key(int x)]) as much as possible to every not manual marked fields/Props
+
+
+`NameSpace` : which assembly to scan
+
+`AlsoMarkPrivateField` : if set with `true`,then all private/internal field/prop will be select and try to add key
+
+`MarkIgnoreToFieldForNonMsgPackBaseType` :if target type base type is not mark [MessagePackObject],then all of it's public(private/internal if AlsoMarkPrivateField enabled) will be mark [IgnoreMember]
+
+`AutoMsgPackKeyWeaver` will do these step:
+
+ 1. only scan mark with [MessagePackObject] or [DataContract] classes
+
+ 2. will ignore all manual mark with [IgnoreMember] or [IgnoreDataMember] or [Key(string name)] or [DataContract] fields/props
+
+ 3. will follow config, add key to target fields/props which not manual mark with [Key(int x)] or [DataContract(Order=int)]
+
+ 4. will check every field/prop,if it has both [Key(int)] and [DataContract[Order=int]],will cause complie error
+
+ 5. will check target class Accessibility,only public class can mark [MessageObject]
+
+ 6. will check type and base type any fields/props key id conflict [e.g. B->A,both use [key(1)],will cause complie error]
+
+also you can enable both
+
+```xml
+
+
+
+
+```
+
+## Note
+
+ 1. This tool will automatic **give each derivedType unique id** to distinguish which type is when deserialize
+
+ Also you can use `[RequireUnion(1)]`(For non generic) or `[RequireUnionGeneric(2,typeof(Class2))]`(For generic) to manual fixed id
+
+ **it can Fixed target type match id and won't change id in diff version**
+ 2. Fody plugin also will Check each fixed id,If the same Id is pointed to a different type, **it will prevent compilation and indicate the specific conflict type**
+
+ 3. If you want to use _Fody.Test_ to see _PolymorphicMessagePack.Fody_ works,make sure change `MsgPackDefineForInject` project property:
+```xml
+ true
+```
diff --git a/Service.Shared/IMyFirstService.cs b/Service.Shared/IMyFirstService.cs
new file mode 100644
index 0000000..3051dfc
--- /dev/null
+++ b/Service.Shared/IMyFirstService.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Threading.Tasks;
+using AbsInjectTypeDll.DllSubAssembly;
+using MagicOnion;
+using MsgPackDefineForInject;
+
+namespace Service.Shared
+{
+ // Defines .NET interface as a Server/Client IDL.
+ // The interface is shared between server and client.
+ public interface IMyFirstService : IService
+ {
+ // The return type must be `UnaryResult` or `UnaryResult`.
+ UnaryResult SumAsync(int x, int y);
+ // `UnaryResult` does not have a return value like `Task`, `ValueTask`, or `void`.
+ UnaryResult DoWorkAsync();
+
+ UnaryResult GetTestData(int x);
+ }
+}
diff --git a/Service.Shared/Service.Shared.csproj b/Service.Shared/Service.Shared.csproj
new file mode 100644
index 0000000..30f1733
--- /dev/null
+++ b/Service.Shared/Service.Shared.csproj
@@ -0,0 +1,17 @@
+
+
+
+ netstandard2.1
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ShareAttributes/Attributes.cs b/ShareAttributes/Attributes.cs
new file mode 100644
index 0000000..9270c4c
--- /dev/null
+++ b/ShareAttributes/Attributes.cs
@@ -0,0 +1,63 @@
+using System;
+
+namespace ShareAttributes
+{
+ ///
+ /// Abstract/Interface Mark
+ ///
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false, Inherited = false)]
+ public class UnionAbsOrInterfaceAttribute : Attribute
+ {
+
+ }
+
+ ///
+ /// GenericClassMark
+ /// No need current generic type in param
+ ///
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
+ public class GenericUnionAttribute : Attribute
+ {
+ public Type GenericUsageType { get; set; }
+
+ public GenericUnionAttribute(Type genericUsageType)
+ {
+ GenericUsageType = genericUsageType;
+ }
+ }
+
+
+ ///
+ /// For non generic class,Mark it's union unique id
+ ///
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
+ public class RequireUnionAttribute : Attribute
+ {
+ public uint UnionUniqueId { get; private set; }
+
+ public RequireUnionAttribute(uint unionUniqueId)
+ {
+ UnionUniqueId = unionUniqueId;
+ }
+ }
+
+ ///
+ /// For generic class,Mark which generic types it will be used
+ ///
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
+ public class RequireUnionGenericAttribute : RequireUnionAttribute
+ {
+ public Type SupportGenericType { get; private set; }
+
+ ///
+ ///
+ ///
+ /// unique Id
+ /// which runtime generic will be used
+ public RequireUnionGenericAttribute(uint unionUniqueId, Type supportGeneric)
+ : base(unionUniqueId)
+ {
+ SupportGenericType = supportGeneric;
+ }
+ }
+}
diff --git a/ShareAttributes/ShareAttributes.csproj b/ShareAttributes/ShareAttributes.csproj
new file mode 100644
index 0000000..9f5c4f4
--- /dev/null
+++ b/ShareAttributes/ShareAttributes.csproj
@@ -0,0 +1,7 @@
+
+
+
+ netstandard2.0
+
+
+