From 12aa3761758727bfa1a69ee86c6aba43f444c878 Mon Sep 17 00:00:00 2001 From: PatchouliTC <1009609373@qq.com> Date: Fri, 21 Apr 2023 05:10:40 +0800 Subject: [PATCH 01/28] - Fix multi abs/interface not recongnise full base type - Rewrite formatter delegate function - Add auto scan and register in assembly all mark [UnionAbsOrInterface] abs/interface - Add Unit Test --- Blah/PolymorphicDelegate.cs | 45 --- Blah/PolymorphicFormatter.cs | 85 ----- ...PolymorphicMessagePackSerializerOptions.cs | 34 -- Blah/PolymorphicMessagePackSettings.cs | 59 ---- Blah/PolymorphicResolver.cs | 86 ----- PolymorphicMessagePack.sln | 20 +- PolymorphicMessagePack/PolymorphicDelegate.cs | 45 --- .../PolymorphicFormatter.cs | 106 ++++-- ...PolymorphicMessagePackSerializerOptions.cs | 12 +- .../PolymorphicMessagePackSettings.cs | 303 +++++++++++++++++- PolymorphicMessagePack/PolymorphicResolver.cs | 80 ++--- 11 files changed, 426 insertions(+), 449 deletions(-) delete mode 100644 Blah/PolymorphicDelegate.cs delete mode 100644 Blah/PolymorphicFormatter.cs delete mode 100644 Blah/PolymorphicMessagePackSerializerOptions.cs delete mode 100644 Blah/PolymorphicMessagePackSettings.cs delete mode 100644 Blah/PolymorphicResolver.cs delete mode 100644 PolymorphicMessagePack/PolymorphicDelegate.cs 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/PolymorphicMessagePack.sln b/PolymorphicMessagePack.sln index aa8274d..41b75be 100644 --- a/PolymorphicMessagePack.sln +++ b/PolymorphicMessagePack.sln @@ -1,9 +1,11 @@  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("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PolyMsgPack.Test", "..\PolyMsgPack.Test\PolyMsgPack.Test.csproj", "{F6F8E9A2-5B34-48F6-AF68-C6E5D59AA7C9}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -11,15 +13,19 @@ 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 + {F6F8E9A2-5B34-48F6-AF68-C6E5D59AA7C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F6F8E9A2-5B34-48F6-AF68-C6E5D59AA7C9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F6F8E9A2-5B34-48F6-AF68-C6E5D59AA7C9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F6F8E9A2-5B34-48F6-AF68-C6E5D59AA7C9}.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/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..6152253 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; } diff --git a/PolymorphicMessagePack/PolymorphicMessagePackSettings.cs b/PolymorphicMessagePack/PolymorphicMessagePackSettings.cs index 81985b8..883f11a 100644 --- a/PolymorphicMessagePack/PolymorphicMessagePackSettings.cs +++ b/PolymorphicMessagePack/PolymorphicMessagePackSettings.cs @@ -1,20 +1,243 @@ using MessagePack; +using MessagePack.Resolvers; using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Reflection; namespace PolymorphicMessagePack { + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false, Inherited = false)] + public class UnionAbsOrInterfaceAttribute : Attribute + { + + } + + public static class GetMarkAttributeClassListExtension + { + private static readonly Type _objType = typeof(object); + public static (Assembly, HashSet) GetMarkUnionAbsAttributeClasses(this Assembly assembly) + { + return (assembly, assembly.GetTypes().Where(x => (x.IsAbstract || x.IsInterface) && x.GetCustomAttribute(false) != null).ToHashSet()); + } + + public static Dictionary> GetAbsDriveClassTypes(this (Assembly assembly, HashSet types) data) + { + var require_search_types = data.assembly.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 && !data.types.Contains(x) && !x.IsGenericType + ); + + Dictionary> cache = new(); + HashSet visitedScanInterfaceTypes = new(); + + foreach (var type in require_search_types) + { + Type temp = type; + while (temp != null && temp != _objType) + { + //if base type is marked abs + //is generic base? + if (temp.BaseType.IsGenericType) + { + var clothType = temp.BaseType.GetGenericTypeDefinition(); + if (data.types.Contains(clothType)) + { + if (!cache.TryGetValue(clothType, out var list)) + { + list = new List() { type }; + cache.Add(clothType, list); + } + else + { + list.Add(type); + } + } + } + else + { + if (data.types.Contains(temp.BaseType)) + { + if (!cache.TryGetValue(temp.BaseType, out var list)) + { + list = new List() { type }; + cache.Add(temp.BaseType, 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? + if (@interface.IsGenericType) + { + var clothType = @interface.GetGenericTypeDefinition(); + if (data.types.Contains(clothType)) + { + if (!cache.TryGetValue(clothType, out var list)) + { + list = new List() { type }; + cache.Add(clothType, list); + } + else + { + list.Add(type); + } + } + } + else + { + + if (data.types.Contains(@interface)) + { + if (!cache.TryGetValue(@interface, out var list)) + { + list = new List() { type }; + cache.Add(@interface, list); + } + else + { + list.Add(type); + } + } + } + } + } + temp = temp.BaseType; + } + } + return cache; + } + + public static Dictionary> GetAbsDriveGenericClassTypes(this (Assembly assembly, HashSet types) data) + { + var require_search_types = data.assembly.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 && !data.types.Contains(x) && x.IsGenericType + ); + + Dictionary> cache = new(); + HashSet visitedScanInterfaceTypes = new(); + + foreach (var type in require_search_types) + { + Type temp = type; + while (temp != null && temp != _objType) + { + //if base type is marked abs + //is generic base? + if (temp.BaseType.IsGenericType) + { + var clothType = temp.BaseType.GetGenericTypeDefinition(); + if (data.types.Contains(clothType)) + { + if (!cache.TryGetValue(temp.BaseType, out var list)) + { + list = new List() { type }; + cache.Add(temp.BaseType, list); + } + else + { + list.Add(type); + } + } + } + else + { + if (data.types.Contains(temp.BaseType)) + { + if (!cache.TryGetValue(temp.BaseType, out var list)) + { + list = new List() { type }; + cache.Add(temp.BaseType, 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? + if (@interface.IsGenericType) + { + var clothType = @interface.GetGenericTypeDefinition(); + if (data.types.Contains(clothType)) + { + if (!cache.TryGetValue(@interface, out var list)) + { + list = new List() { type }; + cache.Add(@interface, list); + } + else + { + list.Add(type); + } + } + } + else + { + + if (data.types.Contains(@interface)) + { + if (!cache.TryGetValue(@interface, out var list)) + { + list = new List() { type }; + cache.Add(@interface, 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 Dictionary TypeToId = new(); + internal readonly Dictionary IdToType = new(); internal readonly HashSet BaseTypes = new(); + internal readonly HashSet GenericTypes = new(); + internal readonly HashSet Assemblies = new(); 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,22 +246,29 @@ 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); @@ -46,14 +276,55 @@ public void RegisterType(int typeId) BaseTypes.Add(typeof(B)); } + public void InjectUnionRequireFromAssembly(Assembly assembly) + { + if (Assemblies.Contains(assembly)) + return; + Assemblies.Add(assembly); + //prepare auto increase key id + uint startId = 0; + if (IdToType.Count > 0) + startId = IdToType.Keys.Max() + 1; + + //get all mark require union abs/interface + var markedUnionRequireAbsOrInterfaces = assembly.GetMarkUnionAbsAttributeClasses(); - //TODO: convenience method - //public void RegisterTypeWithAllInterfacesAndBase(int typeId, bool includeObject = false) - // where T : class - //{ - - //} + //get all drive abs/interface non generic classes + var allNeedMapNonGenericClasses = markedUnionRequireAbsOrInterfaces.GetAbsDriveClassTypes(); - //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? + //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; + TypeToId.TryAdd(pair2, startId); + IdToType.TryAdd(startId, pair2); + startId++; + } + } + + //get all drive abs/interface generic classes + var allNeedRecordGenericClasses = markedUnionRequireAbsOrInterfaces.GetAbsDriveGenericClassTypes(); + //record all need prepare generic type class type + //can't scan from static code,only can know what actually type used for them + //e.g: abstract class A {} class B:A{} + //this can be scan and find define for B->B<> + //but can't know what really T will be used which type + foreach (var pair in allNeedRecordGenericClasses) + { + BaseTypes.Add(pair.Key); + foreach (var pair2 in pair.Value) + { + if (GenericTypes.Contains(pair2)) + continue; + GenericTypes.Add(pair2); + } + } + + + } } } diff --git a/PolymorphicMessagePack/PolymorphicResolver.cs b/PolymorphicMessagePack/PolymorphicResolver.cs index 1eccdac..b1f9983 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(); 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 From 05ce5a8df057e8f482deb260c18b88e932c07433 Mon Sep 17 00:00:00 2001 From: PatchouliTC <1009609373@qq.com> Date: Fri, 21 Apr 2023 05:18:17 +0800 Subject: [PATCH 02/28] - Add Test - Set Lib to .net6.0 --- PolyMsgPack.Test.zip | Bin 0 -> 58475 bytes PolyMsgPack.Test/ClassDriveAbs.cs | 36 +++++++++++ PolyMsgPack.Test/ClassDriveInterface.cs | 37 +++++++++++ PolyMsgPack.Test/ClassForTest.cs | 42 ++++++++++++ PolyMsgPack.Test/PolyMsgPack.Test.csproj | 23 +++++++ PolyMsgPack.Test/PolyMsgPackTest.cs | 78 +++++++++++++++++++++++ PolyMsgPack.Test/Usings.cs | 1 + PolymorphicMessagePack.csproj | 9 ++- PolymorphicMessagePack.sln | 10 +-- 9 files changed, 230 insertions(+), 6 deletions(-) create mode 100644 PolyMsgPack.Test.zip create mode 100644 PolyMsgPack.Test/ClassDriveAbs.cs create mode 100644 PolyMsgPack.Test/ClassDriveInterface.cs create mode 100644 PolyMsgPack.Test/ClassForTest.cs create mode 100644 PolyMsgPack.Test/PolyMsgPack.Test.csproj create mode 100644 PolyMsgPack.Test/PolyMsgPackTest.cs create mode 100644 PolyMsgPack.Test/Usings.cs diff --git a/PolyMsgPack.Test.zip b/PolyMsgPack.Test.zip new file mode 100644 index 0000000000000000000000000000000000000000..6766aca98881e90bd52810e75e8435866d1b4c4f GIT binary patch literal 58475 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

Uy4taww0Y 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 vU6DT+W}#nChk*u + { + + } + + public abstract class CBase5 : CBase4 + { + + } + [UnionAbsOrInterface] + public abstract class CBase6 : CBase4 + { + + } +} diff --git a/PolyMsgPack.Test/ClassDriveInterface.cs b/PolyMsgPack.Test/ClassDriveInterface.cs new file mode 100644 index 0000000..baedcb8 --- /dev/null +++ b/PolyMsgPack.Test/ClassDriveInterface.cs @@ -0,0 +1,37 @@ +using PolymorphicMessagePack; + +namespace PolyMsgPack.Test +{ + [UnionAbsOrInterface] + public interface Base1 + { + + } + + public interface Base2 + { + + } + + [UnionAbsOrInterface] + public interface Base3 : Base1 + { + + } + + [UnionAbsOrInterface] + public interface Base4 + { + + } + + public interface Base5 : Base4 + { + + } + [UnionAbsOrInterface] + public interface Base6 : Base4 + { + + } +} diff --git a/PolyMsgPack.Test/ClassForTest.cs b/PolyMsgPack.Test/ClassForTest.cs new file mode 100644 index 0000000..9fc9fe8 --- /dev/null +++ b/PolyMsgPack.Test/ClassForTest.cs @@ -0,0 +1,42 @@ +using MessagePack; + +namespace PolyMsgPack.Test +{ + [MessagePackObject] + public class Class1 : CBase1 + { + [Key(0)] + public long CT1 { get; set; } + } + [MessagePackObject] + public class Class2 : CBase2 + { + [Key(0)] + public long CT2 { get; set; } + } + [MessagePackObject] + public class Class3 : Class1 + { + [Key(1)] + public long CT3 { get; set; } + } + [MessagePackObject] + public class Class4 : CBase4 + { + [Key(0)] + public long CT4 { get; set; } + } + [MessagePackObject] + public class Class5 : CBase6 + { + [Key(0)] + public long CT5 { get; set; } + } + + [MessagePackObject] + public class Class6 : Base3 + { + + } + +} diff --git a/PolyMsgPack.Test/PolyMsgPack.Test.csproj b/PolyMsgPack.Test/PolyMsgPack.Test.csproj new file mode 100644 index 0000000..b8b6afc --- /dev/null +++ b/PolyMsgPack.Test/PolyMsgPack.Test.csproj @@ -0,0 +1,23 @@ + + + + net6.0 + enable + enable + + false + true + + + + + + + + + + + + + + diff --git a/PolyMsgPack.Test/PolyMsgPackTest.cs b/PolyMsgPack.Test/PolyMsgPackTest.cs new file mode 100644 index 0000000..590e220 --- /dev/null +++ b/PolyMsgPack.Test/PolyMsgPackTest.cs @@ -0,0 +1,78 @@ +using MessagePack; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using PolymorphicMessagePack; + +namespace PolyMsgPack.Test +{ + [TestClass] + public class PolyMsgPackTest + { + PolymorphicMessagePackSerializerOptions _options; + public PolyMsgPackTest() + { + var polySettings = new PolymorphicMessagePackSettings(); + + polySettings.InjectUnionRequireFromAssembly(typeof(PolyMsgPackTest).Assembly); + + _options = new PolymorphicMessagePackSerializerOptions(polySettings); + } + + [TestMethod] + public void Test_NonGenericToAbs() + { + var s = MessagePackSerializer.Serialize(new Class1 { CT1 = 1 }, _options); + var ds = MessagePackSerializer.Deserialize(s, _options); + + Assert.IsTrue(ds is Class1 ds1 && ds1.CT1 == 1); + } + + [TestMethod] + public void Test_NotMarkClassSer() + { + var s = MessagePackSerializer.Serialize(new Class2 { CT2 = 1 }, _options); + Assert.ThrowsException(() => MessagePackSerializer.Deserialize(s, _options)); + } + + [TestMethod] + public void Test_S_DSToSubOtherSubClass() + { + var s = MessagePackSerializer.Serialize(new Class3 { CT3 = 3, CT1 = 1 }, _options); + var ds = MessagePackSerializer.Deserialize(s, _options); + Assert.IsTrue(ds.CT1 == 1); + } + + [TestMethod] + public void Test_S_DSDriveGenericClass() + { + var s = MessagePackSerializer.Serialize(new Class4 { CT4 = 4 }, _options); + var ds = MessagePackSerializer.Deserialize(s, _options); + Assert.IsTrue(ds.CT4 == 4); + } + + [TestMethod] + public void Test_S_DSGenericClass() + { + var s = MessagePackSerializer.Serialize(new Class5 { CT5 = 5 }, _options); + var ds = MessagePackSerializer.Deserialize>(s, _options); + Assert.IsTrue(ds.CT5 == 5); + } + + [TestMethod] + public void Test_S_DSWrongGenericClass() + { + var s = MessagePackSerializer.Serialize(new Class5 { CT5 = 5 }, _options); + var ds = MessagePackSerializer.Deserialize>(s, _options); + Assert.IsNull(ds); + } + + [TestMethod] + public void Test_S_DS_MultiInterface() + { + var s = MessagePackSerializer.Serialize(new Class6(), _options); + var ds1 = MessagePackSerializer.Deserialize(s, _options); + var ds2 = MessagePackSerializer.Deserialize(s, _options); + 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/PolymorphicMessagePack.csproj b/PolymorphicMessagePack.csproj index bdaba76..8216e9f 100644 --- a/PolymorphicMessagePack.csproj +++ b/PolymorphicMessagePack.csproj @@ -1,11 +1,18 @@ - net5.0 + net6.0 + + + + + + + diff --git a/PolymorphicMessagePack.sln b/PolymorphicMessagePack.sln index 41b75be..a6bfe89 100644 --- a/PolymorphicMessagePack.sln +++ b/PolymorphicMessagePack.sln @@ -5,7 +5,7 @@ VisualStudioVersion = 17.5.33516.290 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PolymorphicMessagePack", "PolymorphicMessagePack.csproj", "{8DF39770-8DBD-4647-A73D-7B362CD07CBA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PolyMsgPack.Test", "..\PolyMsgPack.Test\PolyMsgPack.Test.csproj", "{F6F8E9A2-5B34-48F6-AF68-C6E5D59AA7C9}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PolyMsgPack.Test", "PolyMsgPack.Test\PolyMsgPack.Test.csproj", "{A1C98719-901D-43FA-9FE8-F80441835D4A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -17,10 +17,10 @@ Global {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 - {F6F8E9A2-5B34-48F6-AF68-C6E5D59AA7C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F6F8E9A2-5B34-48F6-AF68-C6E5D59AA7C9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F6F8E9A2-5B34-48F6-AF68-C6E5D59AA7C9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F6F8E9A2-5B34-48F6-AF68-C6E5D59AA7C9}.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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 638846384f2cdf4c2ee86c8ad18eb33c20e1f96e Mon Sep 17 00:00:00 2001 From: PatchouliTC <1009609373@qq.com> Date: Thu, 27 Apr 2023 01:07:15 +0800 Subject: [PATCH 03/28] - Add Auto Poly Code Inject --- Fody.Test/Fody.Test.csproj | 31 ++ Fody.Test/Usings.cs | 1 + Fody.Test/WeaverTests.cs | 66 +++ .../AbsDefine.cs | 4 +- MsgPackDefineForInject/FodyWeavers.xml | 3 + .../InterfaceDefine.cs | 4 +- .../MsgPackClass.cs | 43 +- .../MsgPackDefineForInject.csproj | 31 ++ PolyMsgPack.Test.zip | Bin 58475 -> 0 bytes PolyMsgPack.Test/PolyMsgPack.Test.csproj | 4 +- PolyMsgPack.Test/PolyMsgPackTest.cs | 2 +- PolymorphicMessagePack.Fody/ModuleWeaver.cs | 400 ++++++++++++++++++ .../PolymorphicMessagePack.Fody.csproj | 17 + .../PolymorphicMessagePack.Fody.xcf | 9 + .../Properties/AssemblyInfo.cs | 1 + PolymorphicMessagePack.csproj | 22 + PolymorphicMessagePack.sln | 29 +- .../PolymorphicMessagePackSettings.cs | 234 +++------- ShareAttributes/Attributes.cs | 63 +++ ShareAttributes/ShareAttributes.csproj | 7 + 20 files changed, 785 insertions(+), 186 deletions(-) create mode 100644 Fody.Test/Fody.Test.csproj create mode 100644 Fody.Test/Usings.cs create mode 100644 Fody.Test/WeaverTests.cs rename PolyMsgPack.Test/ClassDriveAbs.cs => MsgPackDefineForInject/AbsDefine.cs (87%) create mode 100644 MsgPackDefineForInject/FodyWeavers.xml rename PolyMsgPack.Test/ClassDriveInterface.cs => MsgPackDefineForInject/InterfaceDefine.cs (86%) rename PolyMsgPack.Test/ClassForTest.cs => MsgPackDefineForInject/MsgPackClass.cs (51%) create mode 100644 MsgPackDefineForInject/MsgPackDefineForInject.csproj delete mode 100644 PolyMsgPack.Test.zip create mode 100644 PolymorphicMessagePack.Fody/ModuleWeaver.cs create mode 100644 PolymorphicMessagePack.Fody/PolymorphicMessagePack.Fody.csproj create mode 100644 PolymorphicMessagePack.Fody/PolymorphicMessagePack.Fody.xcf create mode 100644 PolymorphicMessagePack.Fody/Properties/AssemblyInfo.cs create mode 100644 ShareAttributes/Attributes.cs create mode 100644 ShareAttributes/ShareAttributes.csproj 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/Fody.Test/WeaverTests.cs b/Fody.Test/WeaverTests.cs new file mode 100644 index 0000000..5ee0d10 --- /dev/null +++ b/Fody.Test/WeaverTests.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 WeaverTests + { + static TestResult testResult; + + static WeaverTests() + { + var xElement = XElement.Parse(""); + var weavingTask = new ModuleWeaver { 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/PolyMsgPack.Test/ClassDriveAbs.cs b/MsgPackDefineForInject/AbsDefine.cs similarity index 87% rename from PolyMsgPack.Test/ClassDriveAbs.cs rename to MsgPackDefineForInject/AbsDefine.cs index 4b27113..5fec017 100644 --- a/PolyMsgPack.Test/ClassDriveAbs.cs +++ b/MsgPackDefineForInject/AbsDefine.cs @@ -1,6 +1,6 @@ -using PolymorphicMessagePack; +using ShareAttributes; -namespace PolyMsgPack.Test +namespace MsgPackDefineForInject { [UnionAbsOrInterface] public abstract class CBase1 diff --git a/MsgPackDefineForInject/FodyWeavers.xml b/MsgPackDefineForInject/FodyWeavers.xml new file mode 100644 index 0000000..25cae70 --- /dev/null +++ b/MsgPackDefineForInject/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/PolyMsgPack.Test/ClassDriveInterface.cs b/MsgPackDefineForInject/InterfaceDefine.cs similarity index 86% rename from PolyMsgPack.Test/ClassDriveInterface.cs rename to MsgPackDefineForInject/InterfaceDefine.cs index baedcb8..e430042 100644 --- a/PolyMsgPack.Test/ClassDriveInterface.cs +++ b/MsgPackDefineForInject/InterfaceDefine.cs @@ -1,6 +1,6 @@ -using PolymorphicMessagePack; +using ShareAttributes; -namespace PolyMsgPack.Test +namespace MsgPackDefineForInject { [UnionAbsOrInterface] public interface Base1 diff --git a/PolyMsgPack.Test/ClassForTest.cs b/MsgPackDefineForInject/MsgPackClass.cs similarity index 51% rename from PolyMsgPack.Test/ClassForTest.cs rename to MsgPackDefineForInject/MsgPackClass.cs index 9fc9fe8..55f7003 100644 --- a/PolyMsgPack.Test/ClassForTest.cs +++ b/MsgPackDefineForInject/MsgPackClass.cs @@ -1,42 +1,71 @@ using MessagePack; +using ShareAttributes; -namespace PolyMsgPack.Test +namespace MsgPackDefineForInject { - [MessagePackObject] + public class Class1 : CBase1 { - [Key(0)] + public long CT1 { get; set; } } + + [MessagePackObject] public class Class2 : CBase2 { - [Key(0)] + public long CT2 { get; set; } } + + [RequireUnion(114514)] [MessagePackObject] public class Class3 : Class1 { - [Key(1)] + public long CT3 { get; set; } } + + [MessagePackObject] public class Class4 : CBase4 { - [Key(0)] + public long CT4 { get; set; } } + + [GenericUnion(typeof(int))] + [GenericUnion(typeof(string))] [MessagePackObject] public class Class5 : CBase6 { - [Key(0)] public long CT5 { get; set; } } [MessagePackObject] public class Class6 : Base3 { + [Key(0)] + public long CT6 { get; set; } + } + + [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(int))] + [RequireUnionGeneric(2,typeof(string))] + [MessagePackObject] + public class Class8 : CBase6 + { + public long CT8 { get; set; } + } } diff --git a/MsgPackDefineForInject/MsgPackDefineForInject.csproj b/MsgPackDefineForInject/MsgPackDefineForInject.csproj new file mode 100644 index 0000000..faa00d6 --- /dev/null +++ b/MsgPackDefineForInject/MsgPackDefineForInject.csproj @@ -0,0 +1,31 @@ + + + + netstandard2.0 + + + + 5 + + + + 5 + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + diff --git a/PolyMsgPack.Test.zip b/PolyMsgPack.Test.zip deleted file mode 100644 index 6766aca98881e90bd52810e75e8435866d1b4c4f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 58475 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

Uy4taww0Y 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 vU6DT+W}#nChk*u + net6.0 @@ -17,7 +17,9 @@ + + diff --git a/PolyMsgPack.Test/PolyMsgPackTest.cs b/PolyMsgPack.Test/PolyMsgPackTest.cs index 590e220..ccb3f63 100644 --- a/PolyMsgPack.Test/PolyMsgPackTest.cs +++ b/PolyMsgPack.Test/PolyMsgPackTest.cs @@ -1,5 +1,5 @@ using MessagePack; -using Microsoft.VisualStudio.TestTools.UnitTesting; +using MsgPackDefineForInject; using PolymorphicMessagePack; namespace PolyMsgPack.Test diff --git a/PolymorphicMessagePack.Fody/ModuleWeaver.cs b/PolymorphicMessagePack.Fody/ModuleWeaver.cs new file mode 100644 index 0000000..11157ef --- /dev/null +++ b/PolymorphicMessagePack.Fody/ModuleWeaver.cs @@ -0,0 +1,400 @@ +using Fody; +using MessagePack; +using Mono.Cecil; +using Mono.Cecil.Rocks; +using ShareAttributes; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace PolymorphicMessagePack.Fody +{ + public class MsgPackPolyWeaver : 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); + Type _msgKey = typeof(KeyAttribute); + + string _polyAttrAsName; + string _msgPackAttrAsName; + + TypeDefinition _objectTypeRef; + + TypeDefinition _requireUnionAttrRef; + MethodReference _requireUnionAttrConstructor; + + TypeDefinition _requireUnionGenericAttrRef; + MethodReference _requireUnionGenericAttrConstructor; + + TypeDefinition _keyMarkRef; + + public MsgPackPolyWeaver() + { + _polyAttrAsName = _absAttr.Assembly.GetName().Name; + _msgPackAttrAsName=_msgObj.Assembly.GetName().Name; + } + + public override void Execute() + { + var ns = GetValueFromConfig("NameSpace"); + if(ns == null) + { + WriteError("Scan Assembly name not set in config"); + return; + } + InitBasicRequireRef(); + + var requireConsiderAbsAndInterfaceTypes = ModuleDefinition.Types.Where( + x => x.HasCustomAttributes && + x.Namespace == ns && + 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(); + + 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 bool HasGenericTypeMarkAttribute(TypeDefinition type) => + type.HasCustomAttributes && + type.CustomAttributes.Any(attr => attr.AttributeType.FullName == _genericUnion.FullName) && + type.CustomAttributes.Any(attr => attr.AttributeType.FullName == _msgObj.FullName); + + private List<(TypeDefinition, List)> GetGenericDerivedTypes( + ModuleDefinition module, + IEnumerable baseTypes) + { + var classdefs = module.GetTypes(). + Where(x => + !x.IsAbstract && + x.IsClass && + x.HasGenericParameters && + HasGenericTypeMarkAttribute(x)); + + 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(); + + if (genericUnionTypes.Count > 0) + { + //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"; + yield return "System.Core"; + } + + 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); + _keyMarkRef = ModuleDefinition.AssemblyResolver.Resolve(msgPackAssembly). + MainModule.Types. + Single(type => type.Name == _msgKey.Name); + + var requireUnionAttrConstructor= _requireUnionAttrRef + .GetConstructors() + .Single(ctor => + 1 == ctor.Parameters.Count && + "System.UInt32" == ctor.Parameters[0].ParameterType.FullName); + _requireUnionAttrConstructor = ModuleDefinition.ImportReference(requireUnionAttrConstructor); + + var requireUnionGenericAttrConstructor=_requireUnionGenericAttrRef + .GetConstructors() + .Single(ctor => + 2 == ctor.Parameters.Count && + "System.UInt32" == 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/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/PolymorphicMessagePack.Fody.xcf b/PolymorphicMessagePack.Fody/PolymorphicMessagePack.Fody.xcf new file mode 100644 index 0000000..1839755 --- /dev/null +++ b/PolymorphicMessagePack.Fody/PolymorphicMessagePack.Fody.xcf @@ -0,0 +1,9 @@ + + + + + Namespace to use for the injected type + + + \ No newline at end of file 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 8216e9f..2733159 100644 --- a/PolymorphicMessagePack.csproj +++ b/PolymorphicMessagePack.csproj @@ -5,9 +5,27 @@ + + + + + + + + + + + + + + + + + + @@ -15,4 +33,8 @@ + + + + diff --git a/PolymorphicMessagePack.sln b/PolymorphicMessagePack.sln index a6bfe89..0632001 100644 --- a/PolymorphicMessagePack.sln +++ b/PolymorphicMessagePack.sln @@ -5,7 +5,18 @@ VisualStudioVersion = 17.5.33516.290 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PolymorphicMessagePack", "PolymorphicMessagePack.csproj", "{8DF39770-8DBD-4647-A73D-7B362CD07CBA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PolyMsgPack.Test", "PolyMsgPack.Test\PolyMsgPack.Test.csproj", "{A1C98719-901D-43FA-9FE8-F80441835D4A}" +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 Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -21,6 +32,22 @@ Global {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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/PolymorphicMessagePack/PolymorphicMessagePackSettings.cs b/PolymorphicMessagePack/PolymorphicMessagePackSettings.cs index 883f11a..19f3378 100644 --- a/PolymorphicMessagePack/PolymorphicMessagePackSettings.cs +++ b/PolymorphicMessagePack/PolymorphicMessagePackSettings.cs @@ -1,5 +1,6 @@ using MessagePack; using MessagePack.Resolvers; +using ShareAttributes; using System; using System.Collections.Generic; using System.Linq; @@ -7,18 +8,14 @@ namespace PolymorphicMessagePack { - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false, Inherited = false)] - public class UnionAbsOrInterfaceAttribute : Attribute - { - - } - public static class GetMarkAttributeClassListExtension + internal static class GetMarkAttributeClassListExtension { private static readonly Type _objType = typeof(object); public static (Assembly, HashSet) GetMarkUnionAbsAttributeClasses(this Assembly assembly) { - return (assembly, assembly.GetTypes().Where(x => (x.IsAbstract || x.IsInterface) && x.GetCustomAttribute(false) != null).ToHashSet()); + var markdata = new HashSet(assembly.GetTypes().Where(x => (x.IsAbstract || x.IsInterface) && x.GetCustomAttribute(false) != null)); + return (assembly, markdata); } public static Dictionary> GetAbsDriveClassTypes(this (Assembly assembly, HashSet types) data) @@ -28,92 +25,7 @@ public static Dictionary> GetAbsDriveClassTypes(this (Assembly !x.IsAbstract && !x.IsInterface && x.IsClass && x != _objType && !data.types.Contains(x) && !x.IsGenericType ); - Dictionary> cache = new(); - HashSet visitedScanInterfaceTypes = new(); - - foreach (var type in require_search_types) - { - Type temp = type; - while (temp != null && temp != _objType) - { - //if base type is marked abs - //is generic base? - if (temp.BaseType.IsGenericType) - { - var clothType = temp.BaseType.GetGenericTypeDefinition(); - if (data.types.Contains(clothType)) - { - if (!cache.TryGetValue(clothType, out var list)) - { - list = new List() { type }; - cache.Add(clothType, list); - } - else - { - list.Add(type); - } - } - } - else - { - if (data.types.Contains(temp.BaseType)) - { - if (!cache.TryGetValue(temp.BaseType, out var list)) - { - list = new List() { type }; - cache.Add(temp.BaseType, 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? - if (@interface.IsGenericType) - { - var clothType = @interface.GetGenericTypeDefinition(); - if (data.types.Contains(clothType)) - { - if (!cache.TryGetValue(clothType, out var list)) - { - list = new List() { type }; - cache.Add(clothType, list); - } - else - { - list.Add(type); - } - } - } - else - { - - if (data.types.Contains(@interface)) - { - if (!cache.TryGetValue(@interface, out var list)) - { - list = new List() { type }; - cache.Add(@interface, list); - } - else - { - list.Add(type); - } - } - } - } - } - temp = temp.BaseType; - } - } - return cache; + return AnalysisTypes(require_search_types, data.types); } public static Dictionary> GetAbsDriveGenericClassTypes(this (Assembly assembly, HashSet types) data) @@ -123,50 +35,35 @@ public static Dictionary> GetAbsDriveGenericClassTypes(this (As !x.IsAbstract && !x.IsInterface && x.IsClass && x != _objType && !data.types.Contains(x) && x.IsGenericType ); - Dictionary> cache = new(); - HashSet visitedScanInterfaceTypes = new(); + return AnalysisTypes(require_search_types, data.types); + } - foreach (var type in require_search_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)) { - var clothType = temp.BaseType.GetGenericTypeDefinition(); - if (data.types.Contains(clothType)) - { - if (!cache.TryGetValue(temp.BaseType, out var list)) - { - list = new List() { type }; - cache.Add(temp.BaseType, list); - } - else - { - list.Add(type); - } - } - } - else - { - if (data.types.Contains(temp.BaseType)) + if (!cache.TryGetValue(factCheckType, out var list)) { - if (!cache.TryGetValue(temp.BaseType, out var list)) - { - list = new List() { type }; - cache.Add(temp.BaseType, list); - } - else - { - list.Add(type); - } + list = new List() { type }; + cache.Add(factCheckType, list); } + else + list.Add(type); } - - if (!temp.IsAbstract && !visitedScanInterfaceTypes.Contains(temp)) { visitedScanInterfaceTypes.Add(temp); @@ -174,36 +71,19 @@ public static Dictionary> GetAbsDriveGenericClassTypes(this (As { //if any interface in marked interface //is generic base? + factCheckType = @interface; if (@interface.IsGenericType) + factCheckType = @interface.GetGenericTypeDefinition(); + if (exceptTypes.Contains(factCheckType)) { - var clothType = @interface.GetGenericTypeDefinition(); - if (data.types.Contains(clothType)) + if (!cache.TryGetValue(factCheckType, out var list)) { - if (!cache.TryGetValue(@interface, out var list)) - { - list = new List() { type }; - cache.Add(@interface, list); - } - else - { - list.Add(type); - } + list = new List() { type }; + cache.Add(factCheckType, list); } - } - else - { - - if (data.types.Contains(@interface)) + else { - if (!cache.TryGetValue(@interface, out var list)) - { - list = new List() { type }; - cache.Add(@interface, list); - } - else - { - list.Add(type); - } + list.Add(type); } } } @@ -218,11 +98,11 @@ public static Dictionary> GetAbsDriveGenericClassTypes(this (As public class PolymorphicMessagePackSettings { - internal readonly Dictionary TypeToId = new(); - internal readonly Dictionary IdToType = new(); - internal readonly HashSet BaseTypes = new(); - internal readonly HashSet GenericTypes = new(); - internal readonly HashSet Assemblies = 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; @@ -271,8 +151,8 @@ public void RegisterType(uint 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)); } @@ -281,10 +161,6 @@ public void InjectUnionRequireFromAssembly(Assembly assembly) if (Assemblies.Contains(assembly)) return; Assemblies.Add(assembly); - //prepare auto increase key id - uint startId = 0; - if (IdToType.Count > 0) - startId = IdToType.Keys.Max() + 1; //get all mark require union abs/interface var markedUnionRequireAbsOrInterfaces = assembly.GetMarkUnionAbsAttributeClasses(); @@ -300,31 +176,45 @@ public void InjectUnionRequireFromAssembly(Assembly assembly) { if (TypeToId.ContainsKey(pair2)) continue; - TypeToId.TryAdd(pair2, startId); - IdToType.TryAdd(startId, pair2); - startId++; + 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}"); + + TypeToId.Add(pair2, unionIdAttribute.UnionUniqueId); + IdToType.Add(unionIdAttribute.UnionUniqueId, pair2); } } //get all drive abs/interface generic classes var allNeedRecordGenericClasses = markedUnionRequireAbsOrInterfaces.GetAbsDriveGenericClassTypes(); //record all need prepare generic type class type - //can't scan from static code,only can know what actually type used for them - //e.g: abstract class A {} class B:A{} - //this can be scan and find define for B->B<> - //but can't know what really T will be used which type foreach (var pair in allNeedRecordGenericClasses) { BaseTypes.Add(pair.Key); foreach (var pair2 in pair.Value) { - if (GenericTypes.Contains(pair2)) - continue; - GenericTypes.Add(pair2); + 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/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 + + + From 68107c56239bfb9aacc1db85147e955f02049b49 Mon Sep 17 00:00:00 2001 From: PatchouliTC <1009609373@qq.com> Date: Thu, 27 Apr 2023 01:15:48 +0800 Subject: [PATCH 04/28] Create LICENSE --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE 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. From 95639fcf8e56b6d927cc7962c31550f3f7098502 Mon Sep 17 00:00:00 2001 From: PatchouliTC <1009609373@qq.com> Date: Thu, 27 Apr 2023 01:57:43 +0800 Subject: [PATCH 05/28] Create README.md --- README.md | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..c104ce6 --- /dev/null +++ b/README.md @@ -0,0 +1,107 @@ +# AutoPolymorphicMessagePack +Auto Union Scanner For MessagePack-CSharp + +Let you use more easy way to union messagepack object + +## 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 + +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 before the 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; } + } +``` + 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_ + +## 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** From 23f5557cc7dc072c0549f32e31904f040d40c481 Mon Sep 17 00:00:00 2001 From: PatchouliTC <1009609373@qq.com> Date: Thu, 27 Apr 2023 01:58:49 +0800 Subject: [PATCH 06/28] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c104ce6..f3c89b8 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # AutoPolymorphicMessagePack -Auto Union Scanner For MessagePack-CSharp +Auto Union Scanner For [MessagePack-CSharp](https://github.com/neuecc/MessagePack-CSharp) Let you use more easy way to union messagepack object From c1b2e5db38e9f8c1ce59cfdc76a3b118bf850307 Mon Sep 17 00:00:00 2001 From: PatchouliTC <1009609373@qq.com> Date: Thu, 27 Apr 2023 01:59:38 +0800 Subject: [PATCH 07/28] - fix --- MsgPackDefineForInject/AbsDefine.cs | 1 + MsgPackDefineForInject/InterfaceDefine.cs | 12 ++++++------ MsgPackDefineForInject/MsgPackClass.cs | 2 +- PolyMsgPack.Test/PolyMsgPackTest.cs | 4 ++-- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/MsgPackDefineForInject/AbsDefine.cs b/MsgPackDefineForInject/AbsDefine.cs index 5fec017..1c0eaf2 100644 --- a/MsgPackDefineForInject/AbsDefine.cs +++ b/MsgPackDefineForInject/AbsDefine.cs @@ -2,6 +2,7 @@ namespace MsgPackDefineForInject { + [UnionAbsOrInterface] public abstract class CBase1 { diff --git a/MsgPackDefineForInject/InterfaceDefine.cs b/MsgPackDefineForInject/InterfaceDefine.cs index e430042..cfb9eb3 100644 --- a/MsgPackDefineForInject/InterfaceDefine.cs +++ b/MsgPackDefineForInject/InterfaceDefine.cs @@ -3,34 +3,34 @@ namespace MsgPackDefineForInject { [UnionAbsOrInterface] - public interface Base1 + public interface IBase1 { } - public interface Base2 + public interface IBase2 { } [UnionAbsOrInterface] - public interface Base3 : Base1 + public interface IBase3 : IBase1 { } [UnionAbsOrInterface] - public interface Base4 + public interface IBase4 { } - public interface Base5 : Base4 + public interface IBase5 : IBase4 { } [UnionAbsOrInterface] - public interface Base6 : Base4 + public interface IBase6 : IBase4 { } diff --git a/MsgPackDefineForInject/MsgPackClass.cs b/MsgPackDefineForInject/MsgPackClass.cs index 55f7003..27e417a 100644 --- a/MsgPackDefineForInject/MsgPackClass.cs +++ b/MsgPackDefineForInject/MsgPackClass.cs @@ -43,7 +43,7 @@ public class Class5 : CBase6 } [MessagePackObject] - public class Class6 : Base3 + public class Class6 : IBase3 { [Key(0)] public long CT6 { get; set; } diff --git a/PolyMsgPack.Test/PolyMsgPackTest.cs b/PolyMsgPack.Test/PolyMsgPackTest.cs index ccb3f63..d5e488e 100644 --- a/PolyMsgPack.Test/PolyMsgPackTest.cs +++ b/PolyMsgPack.Test/PolyMsgPackTest.cs @@ -69,8 +69,8 @@ public void Test_S_DSWrongGenericClass() public void Test_S_DS_MultiInterface() { var s = MessagePackSerializer.Serialize(new Class6(), _options); - var ds1 = MessagePackSerializer.Deserialize(s, _options); - var ds2 = MessagePackSerializer.Deserialize(s, _options); + var ds1 = MessagePackSerializer.Deserialize(s, _options); + var ds2 = MessagePackSerializer.Deserialize(s, _options); Assert.IsNotNull(ds1); Assert.IsNotNull(ds2); } From 649cb1b0005921c3f02b33d76238d2e14886b4ff Mon Sep 17 00:00:00 2001 From: PatchouliTC <1009609373@qq.com> Date: Thu, 27 Apr 2023 02:24:08 +0800 Subject: [PATCH 08/28] - fix --- Fody.Test/WeaverTests.cs | 4 +- MsgPackDefineForInject/MsgPackClass.cs | 4 +- .../MsgPackDefineForInject.csproj | 1 + PolymorphicMessagePack.Fody/ModuleWeaver.cs | 61 ++++++++++--------- 4 files changed, 37 insertions(+), 33 deletions(-) diff --git a/Fody.Test/WeaverTests.cs b/Fody.Test/WeaverTests.cs index 5ee0d10..0469402 100644 --- a/Fody.Test/WeaverTests.cs +++ b/Fody.Test/WeaverTests.cs @@ -18,8 +18,8 @@ public class WeaverTests static WeaverTests() { - var xElement = XElement.Parse(""); - var weavingTask = new ModuleWeaver { Config= xElement }; + var xElement = XElement.Parse(""); + var weavingTask = new MsgPackPolyWeaver { Config= xElement }; testResult = weavingTask.ExecuteTestRun("MsgPackDefineForInject.dll",runPeVerify:false); } diff --git a/MsgPackDefineForInject/MsgPackClass.cs b/MsgPackDefineForInject/MsgPackClass.cs index 27e417a..519c085 100644 --- a/MsgPackDefineForInject/MsgPackClass.cs +++ b/MsgPackDefineForInject/MsgPackClass.cs @@ -61,8 +61,8 @@ public class Class7 : CBase6 } - [RequireUnionGeneric(1,typeof(int))] - [RequireUnionGeneric(2,typeof(string))] + [RequireUnionGeneric(1,typeof(Class8))] + [RequireUnionGeneric(2,typeof(Class8))] [MessagePackObject] public class Class8 : CBase6 { diff --git a/MsgPackDefineForInject/MsgPackDefineForInject.csproj b/MsgPackDefineForInject/MsgPackDefineForInject.csproj index faa00d6..209a14f 100644 --- a/MsgPackDefineForInject/MsgPackDefineForInject.csproj +++ b/MsgPackDefineForInject/MsgPackDefineForInject.csproj @@ -2,6 +2,7 @@ netstandard2.0 + false diff --git a/PolymorphicMessagePack.Fody/ModuleWeaver.cs b/PolymorphicMessagePack.Fody/ModuleWeaver.cs index 11157ef..69b9913 100644 --- a/PolymorphicMessagePack.Fody/ModuleWeaver.cs +++ b/PolymorphicMessagePack.Fody/ModuleWeaver.cs @@ -135,6 +135,15 @@ public override void Execute() 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 _)) { @@ -274,11 +283,6 @@ private List GetNonGenericDerivedTypes( return derivedtypes; } - private bool HasGenericTypeMarkAttribute(TypeDefinition type) => - type.HasCustomAttributes && - type.CustomAttributes.Any(attr => attr.AttributeType.FullName == _genericUnion.FullName) && - type.CustomAttributes.Any(attr => attr.AttributeType.FullName == _msgObj.FullName); - private List<(TypeDefinition, List)> GetGenericDerivedTypes( ModuleDefinition module, IEnumerable baseTypes) @@ -288,7 +292,8 @@ private bool HasGenericTypeMarkAttribute(TypeDefinition type) => !x.IsAbstract && x.IsClass && x.HasGenericParameters && - HasGenericTypeMarkAttribute(x)); + x.HasCustomAttributes && + x.CustomAttributes.Any(y => y.AttributeType.FullName == _msgObj.FullName)); List<(TypeDefinition, List)> derivedtypes = new List<(TypeDefinition, List)>(); @@ -300,35 +305,33 @@ private bool HasGenericTypeMarkAttribute(TypeDefinition type) => .ToLookup(y => y.ConstructorArguments[0].Value.ToString()) .Select(z => z.First()).ToList(); - if (genericUnionTypes.Count > 0) - { - //package - var package = (classdef, genericUnionTypes); + //package + var package = (classdef, genericUnionTypes); - var nextcheck = classdef; - bool isInAnyAbs = false; - while (nextcheck != null && nextcheck.FullName != _objectTypeRef.FullName) + 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) { - 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(); + derivedtypes.Add(package); + isInAnyAbs = true; + break; } - if (!isInAnyAbs) + 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) { - //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); - } + derivedtypes.Add(package); } } + } return derivedtypes; } From 46491f1abb532d86e866ccf69ee435e921f7ec28 Mon Sep 17 00:00:00 2001 From: PatchouliTC <1009609373@qq.com> Date: Thu, 27 Apr 2023 02:27:26 +0800 Subject: [PATCH 09/28] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index f3c89b8..2b90e93 100644 --- a/README.md +++ b/README.md @@ -105,3 +105,8 @@ _You can see more in [PolyMsgPack.Test](https://github.com/PatchouliTC/Polymorph **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 +``` From d5889381158a4ccc2c54a02b4519da1771af62a2 Mon Sep 17 00:00:00 2001 From: PatchouliTC <1009609373@qq.com> Date: Thu, 27 Apr 2023 02:31:27 +0800 Subject: [PATCH 10/28] Update README.md --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index 2b90e93..069ac65 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,25 @@ Then set your `Project1` follow these steps: [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 mast make sure type in [RequireUnionGeneric] must current generic type or fody will give complie error + [RequireUnionGeneric(10,typeof(Class2))] + [GenericUnion(typeof(string))] + [MessagePackObject] + public class Class2 : IBase4 + { + [Key(0)] + public long CT2 { get; set; } + } ``` 6. using and inject `Project1` assembly into `PolymorphicMessagePackSettings` and use it From fd948cf08609b88ad59945b5f1ad74a21ba90792 Mon Sep 17 00:00:00 2001 From: PatchouliTC <1009609373@qq.com> Date: Thu, 27 Apr 2023 02:32:02 +0800 Subject: [PATCH 11/28] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 069ac65..c9715db 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ Then set your `Project1` follow these steps: public long CT1 { get; set; } } - //also work for generic type,but you mast make sure type in [RequireUnionGeneric] must current generic type or fody will give complie error + //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(Class2))] [GenericUnion(typeof(string))] [MessagePackObject] From e4713c074e7fdbaa2c0d0a014db18d44290cb881 Mon Sep 17 00:00:00 2001 From: PatchouliTC <1009609373@qq.com> Date: Thu, 27 Apr 2023 02:32:41 +0800 Subject: [PATCH 12/28] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c9715db..e3fdcb2 100644 --- a/README.md +++ b/README.md @@ -86,10 +86,10 @@ Then set your `Project1` follow these steps: } //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(Class2))] + [RequireUnionGeneric(10,typeof(Class4))] [GenericUnion(typeof(string))] [MessagePackObject] - public class Class2 : IBase4 + public class Class4 : IBase4 { [Key(0)] public long CT2 { get; set; } From adf2af1df7da4e1f696fcef1ef6cc0986d4f1bd0 Mon Sep 17 00:00:00 2001 From: PatchouliTC <1009609373@qq.com> Date: Thu, 27 Apr 2023 15:51:44 +0800 Subject: [PATCH 13/28] - add auto wire msgpack key --- Fody.Test/AutoKeyWeaverTests.cs | 38 ++ ...{WeaverTests.cs => AutoPolyWeaverTests.cs} | 8 +- MsgPackDefineForInject/FodyWeavers.xml | 3 +- MsgPackDefineForInject/MsgPackClass.cs | 36 ++ .../MsgPackDefineForInject.csproj | 4 +- PolyMsgPack.Test/PolyMsgPackTest.cs | 1 + .../AutoMsgPackKeyWeaver.cs | 456 ++++++++++++++++++ .../AutoMsgPackKeyWeaver.xcf | 23 + ...duleWeaver.cs => AutoPolyMsgPackWeaver.cs} | 14 +- ...ack.Fody.xcf => AutoPolyMsgPackWeaver.xcf} | 0 10 files changed, 566 insertions(+), 17 deletions(-) create mode 100644 Fody.Test/AutoKeyWeaverTests.cs rename Fody.Test/{WeaverTests.cs => AutoPolyWeaverTests.cs} (86%) create mode 100644 PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs create mode 100644 PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.xcf rename PolymorphicMessagePack.Fody/{ModuleWeaver.cs => AutoPolyMsgPackWeaver.cs} (96%) rename PolymorphicMessagePack.Fody/{PolymorphicMessagePack.Fody.xcf => AutoPolyMsgPackWeaver.xcf} (100%) diff --git a/Fody.Test/AutoKeyWeaverTests.cs b/Fody.Test/AutoKeyWeaverTests.cs new file mode 100644 index 0000000..f523857 --- /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/WeaverTests.cs b/Fody.Test/AutoPolyWeaverTests.cs similarity index 86% rename from Fody.Test/WeaverTests.cs rename to Fody.Test/AutoPolyWeaverTests.cs index 0469402..e6fdf7a 100644 --- a/Fody.Test/WeaverTests.cs +++ b/Fody.Test/AutoPolyWeaverTests.cs @@ -12,14 +12,14 @@ namespace Fody.Test { - public class WeaverTests + public class AutoPolyWeaverTests { static TestResult testResult; - static WeaverTests() + static AutoPolyWeaverTests() { - var xElement = XElement.Parse(""); - var weavingTask = new MsgPackPolyWeaver { Config= xElement }; + var xElement = XElement.Parse(""); + var weavingTask = new AutoPolyMsgPackWeaver { Config= xElement }; testResult = weavingTask.ExecuteTestRun("MsgPackDefineForInject.dll",runPeVerify:false); } diff --git a/MsgPackDefineForInject/FodyWeavers.xml b/MsgPackDefineForInject/FodyWeavers.xml index 25cae70..f4cb040 100644 --- a/MsgPackDefineForInject/FodyWeavers.xml +++ b/MsgPackDefineForInject/FodyWeavers.xml @@ -1,3 +1,4 @@  - + + \ No newline at end of file diff --git a/MsgPackDefineForInject/MsgPackClass.cs b/MsgPackDefineForInject/MsgPackClass.cs index 519c085..5608392 100644 --- a/MsgPackDefineForInject/MsgPackClass.cs +++ b/MsgPackDefineForInject/MsgPackClass.cs @@ -1,5 +1,6 @@ using MessagePack; using ShareAttributes; +using System.Runtime.Serialization; namespace MsgPackDefineForInject { @@ -18,6 +19,17 @@ 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 @@ -68,4 +80,28 @@ public class Class8 : CBase6 { public long CT8 { get; set; } } + + [MessagePackObject] + public struct Struct1 + { + private long ST1 { get; } + + [Key(1)] + public long ST2 { get; set; } + + [Key(2)] + public long ST3; + + [DataMember(Order =3)] + private long ST4; + + internal long ST5; + + public long STT5 + { + get => ST5; + set => ST5 = value; + } + + } } diff --git a/MsgPackDefineForInject/MsgPackDefineForInject.csproj b/MsgPackDefineForInject/MsgPackDefineForInject.csproj index 209a14f..dd04bde 100644 --- a/MsgPackDefineForInject/MsgPackDefineForInject.csproj +++ b/MsgPackDefineForInject/MsgPackDefineForInject.csproj @@ -2,7 +2,7 @@ netstandard2.0 - false + true @@ -26,7 +26,7 @@ - + diff --git a/PolyMsgPack.Test/PolyMsgPackTest.cs b/PolyMsgPack.Test/PolyMsgPackTest.cs index d5e488e..6e5c895 100644 --- a/PolyMsgPack.Test/PolyMsgPackTest.cs +++ b/PolyMsgPack.Test/PolyMsgPackTest.cs @@ -4,6 +4,7 @@ namespace PolyMsgPack.Test { + [TestClass] public class PolyMsgPackTest { diff --git a/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs b/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs new file mode 100644 index 0000000..9fb18b4 --- /dev/null +++ b/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs @@ -0,0 +1,456 @@ +using Fody; +using MessagePack; +using Mono.Cecil; +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; + 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("BaseNonMarkTypeFieldMarkAsIgnore"); + + 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 + { + //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); + } + } + + private void MarkTreeNode(ReferenceTreeNode root,int startId) + { + MarkNodeFields(root, ref startId); + foreach(var deriveType in root.Childs) + { + MarkTreeNode(deriveType, startId); + } + } + 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 :{System.Int64 MsgPackDefineForInject.Struct1::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(); + _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..7e2a76b --- /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/ModuleWeaver.cs b/PolymorphicMessagePack.Fody/AutoPolyMsgPackWeaver.cs similarity index 96% rename from PolymorphicMessagePack.Fody/ModuleWeaver.cs rename to PolymorphicMessagePack.Fody/AutoPolyMsgPackWeaver.cs index 69b9913..607fae7 100644 --- a/PolymorphicMessagePack.Fody/ModuleWeaver.cs +++ b/PolymorphicMessagePack.Fody/AutoPolyMsgPackWeaver.cs @@ -9,7 +9,7 @@ namespace PolymorphicMessagePack.Fody { - public class MsgPackPolyWeaver : BaseModuleWeaver + public class AutoPolyMsgPackWeaver : BaseModuleWeaver { public override bool ShouldCleanReference => true; @@ -19,7 +19,6 @@ public class MsgPackPolyWeaver : BaseModuleWeaver Type _reqGenericUnionAttr = typeof(RequireUnionGenericAttribute); Type _msgObj = typeof(MessagePackObjectAttribute); - Type _msgKey = typeof(KeyAttribute); string _polyAttrAsName; string _msgPackAttrAsName; @@ -32,9 +31,8 @@ public class MsgPackPolyWeaver : BaseModuleWeaver TypeDefinition _requireUnionGenericAttrRef; MethodReference _requireUnionGenericAttrConstructor; - TypeDefinition _keyMarkRef; - public MsgPackPolyWeaver() + public AutoPolyMsgPackWeaver() { _polyAttrAsName = _absAttr.Assembly.GetName().Name; _msgPackAttrAsName=_msgObj.Assembly.GetName().Name; @@ -342,7 +340,6 @@ public override IEnumerable GetAssembliesForScanning() yield return "mscorlib"; yield return "System"; yield return "System.Runtime"; - yield return "System.Core"; } void InitBasicRequireRef() @@ -358,22 +355,19 @@ void InitBasicRequireRef() _requireUnionGenericAttrRef = ModuleDefinition.AssemblyResolver.Resolve(polyAttrAssembly). MainModule.Types. Single(type => type.Name == _reqGenericUnionAttr.Name); - _keyMarkRef = ModuleDefinition.AssemblyResolver.Resolve(msgPackAssembly). - MainModule.Types. - Single(type => type.Name == _msgKey.Name); var requireUnionAttrConstructor= _requireUnionAttrRef .GetConstructors() .Single(ctor => 1 == ctor.Parameters.Count && - "System.UInt32" == ctor.Parameters[0].ParameterType.FullName); + TypeSystem.UInt32Reference.FullName == ctor.Parameters[0].ParameterType.FullName); _requireUnionAttrConstructor = ModuleDefinition.ImportReference(requireUnionAttrConstructor); var requireUnionGenericAttrConstructor=_requireUnionGenericAttrRef .GetConstructors() .Single(ctor => 2 == ctor.Parameters.Count && - "System.UInt32" == ctor.Parameters[0].ParameterType.FullName && + TypeSystem.UInt32Reference.FullName == ctor.Parameters[0].ParameterType.FullName && "System.Type" == ctor.Parameters[1].ParameterType.FullName); _requireUnionGenericAttrConstructor=ModuleDefinition.ImportReference(requireUnionGenericAttrConstructor); diff --git a/PolymorphicMessagePack.Fody/PolymorphicMessagePack.Fody.xcf b/PolymorphicMessagePack.Fody/AutoPolyMsgPackWeaver.xcf similarity index 100% rename from PolymorphicMessagePack.Fody/PolymorphicMessagePack.Fody.xcf rename to PolymorphicMessagePack.Fody/AutoPolyMsgPackWeaver.xcf From 10b6a58268308e7d5705d859fb100ed5824be79f Mon Sep 17 00:00:00 2001 From: PatchouliTC <1009609373@qq.com> Date: Thu, 27 Apr 2023 15:55:55 +0800 Subject: [PATCH 14/28] - add simple --- MsgPackDefineForInject/AbsDefine.cs | 2 +- MsgPackDefineForInject/MsgPackClass.cs | 5 +++-- MsgPackDefineForInject/MsgPackDefineForInject.csproj | 2 +- PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs | 3 ++- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/MsgPackDefineForInject/AbsDefine.cs b/MsgPackDefineForInject/AbsDefine.cs index 1c0eaf2..bc4ab7a 100644 --- a/MsgPackDefineForInject/AbsDefine.cs +++ b/MsgPackDefineForInject/AbsDefine.cs @@ -10,7 +10,7 @@ public abstract class CBase1 public abstract class CBase2 { - + public long CTB2 { get; set; } } [UnionAbsOrInterface] diff --git a/MsgPackDefineForInject/MsgPackClass.cs b/MsgPackDefineForInject/MsgPackClass.cs index 5608392..0944401 100644 --- a/MsgPackDefineForInject/MsgPackClass.cs +++ b/MsgPackDefineForInject/MsgPackClass.cs @@ -59,6 +59,9 @@ public class Class6 : IBase3 { [Key(0)] public long CT6 { get; set; } + + public long CT6_1 { get; set; } + private long CT6_2; } @@ -89,10 +92,8 @@ public struct Struct1 [Key(1)] public long ST2 { get; set; } - [Key(2)] public long ST3; - [DataMember(Order =3)] private long ST4; internal long ST5; diff --git a/MsgPackDefineForInject/MsgPackDefineForInject.csproj b/MsgPackDefineForInject/MsgPackDefineForInject.csproj index dd04bde..8fe1ad1 100644 --- a/MsgPackDefineForInject/MsgPackDefineForInject.csproj +++ b/MsgPackDefineForInject/MsgPackDefineForInject.csproj @@ -2,7 +2,7 @@ netstandard2.0 - true + false diff --git a/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs b/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs index 9fb18b4..17957ff 100644 --- a/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs +++ b/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs @@ -264,7 +264,8 @@ private bool CheckAndReigsterManualSetKeys(ReferenceTreeNode node,CustomAttribut if (markPrivateField) considerFields = query.ToList(); else - considerFields = query.Where(x => !(x.IsAssembly || x.IsPrivate) || x.CustomAttributes.Any(y => y.AttributeType.FullName == _autoPropFieldAttrDef.FullName)).ToList(); + 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 :{System.Int64 MsgPackDefineForInject.Struct1::k__BackingField}+{System.Runtime.CompilerServices.CompilerGeneratedAttribute} From 24c92dc8af72e5838e6f82fe946c55762191f9ea Mon Sep 17 00:00:00 2001 From: PatchouliTC <1009609373@qq.com> Date: Thu, 27 Apr 2023 16:15:44 +0800 Subject: [PATCH 15/28] - add support ignore member --- PolyMsgPack.Test/AutoMsgPackTest.cs | 29 +++++++++++++++++++ .../AutoMsgPackKeyWeaver.cs | 25 +++++++++++++--- 2 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 PolyMsgPack.Test/AutoMsgPackTest.cs 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/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs b/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs index 17957ff..b7292a9 100644 --- a/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs +++ b/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs @@ -178,16 +178,33 @@ public override void Execute() foreach(var typeNode in refTree.Where(x => x.Parent == null)) { //each base type start with key id 0 - MarkTreeNode(typeNode, 0); + MarkTreeNode(typeNode, 0, markBaseTypeIgnoreMem); } } - private void MarkTreeNode(ReferenceTreeNode root,int startId) + private void MarkTreeNode(ReferenceTreeNode root,int startId,bool markBaseTypeIgnoreMem) { - MarkNodeFields(root, ref startId); + //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); + MarkTreeNode(deriveType, startId, markBaseTypeIgnoreMem); } } private void MarkNodeFields(ReferenceTreeNode node,ref int startId) From 6375ef649a65fadba4f9025363c93b2a7db92344 Mon Sep 17 00:00:00 2001 From: PatchouliTC <1009609373@qq.com> Date: Thu, 27 Apr 2023 16:22:06 +0800 Subject: [PATCH 16/28] - fix --- Fody.Test/AutoKeyWeaverTests.cs | 2 +- MsgPackDefineForInject/FodyWeavers.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Fody.Test/AutoKeyWeaverTests.cs b/Fody.Test/AutoKeyWeaverTests.cs index f523857..a81b5e0 100644 --- a/Fody.Test/AutoKeyWeaverTests.cs +++ b/Fody.Test/AutoKeyWeaverTests.cs @@ -20,7 +20,7 @@ public class AutoKeyWeaverTests static AutoKeyWeaverTests() { - var xElement = XElement.Parse(""); + var xElement = XElement.Parse(""); var weavingTask = new AutoMsgPackKeyWeaver { Config = xElement }; testResult = weavingTask.ExecuteTestRun("MsgPackDefineForInject.dll", runPeVerify: false); } diff --git a/MsgPackDefineForInject/FodyWeavers.xml b/MsgPackDefineForInject/FodyWeavers.xml index f4cb040..e7ed5ba 100644 --- a/MsgPackDefineForInject/FodyWeavers.xml +++ b/MsgPackDefineForInject/FodyWeavers.xml @@ -1,4 +1,4 @@  - - + + \ No newline at end of file From 5bf760aa2b4234e066b97c0410fc88239e94adbe Mon Sep 17 00:00:00 2001 From: PatchouliTC <1009609373@qq.com> Date: Thu, 27 Apr 2023 17:00:20 +0800 Subject: [PATCH 17/28] - add fix --- Fody.Test/AutoKeyWeaverTests.cs | 2 +- MsgPackDefineForInject/FodyWeavers.xml | 2 +- PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs | 2 +- PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.xcf | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Fody.Test/AutoKeyWeaverTests.cs b/Fody.Test/AutoKeyWeaverTests.cs index a81b5e0..44fdca9 100644 --- a/Fody.Test/AutoKeyWeaverTests.cs +++ b/Fody.Test/AutoKeyWeaverTests.cs @@ -20,7 +20,7 @@ public class AutoKeyWeaverTests static AutoKeyWeaverTests() { - var xElement = XElement.Parse(""); + var xElement = XElement.Parse(""); var weavingTask = new AutoMsgPackKeyWeaver { Config = xElement }; testResult = weavingTask.ExecuteTestRun("MsgPackDefineForInject.dll", runPeVerify: false); } diff --git a/MsgPackDefineForInject/FodyWeavers.xml b/MsgPackDefineForInject/FodyWeavers.xml index e7ed5ba..8415980 100644 --- a/MsgPackDefineForInject/FodyWeavers.xml +++ b/MsgPackDefineForInject/FodyWeavers.xml @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs b/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs index b7292a9..b5f496f 100644 --- a/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs +++ b/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs @@ -68,7 +68,7 @@ public override void Execute() return; } var markPrivateField = GetBoolFromConfig("AlsoMarkPrivateField"); - var markBaseTypeIgnoreMem = GetBoolFromConfig("BaseNonMarkTypeFieldMarkAsIgnore"); + var markBaseTypeIgnoreMem = GetBoolFromConfig("MarkIgnoreToFieldForNonMsgPackBaseType"); InitBasicRequireRef(); diff --git a/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.xcf b/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.xcf index 7e2a76b..cf0ef52 100644 --- a/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.xcf +++ b/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.xcf @@ -12,7 +12,7 @@ Also scan and mark private field - From 945a5078e58a144274deefd0028bbf088332d2e6 Mon Sep 17 00:00:00 2001 From: PatchouliTC <1009609373@qq.com> Date: Thu, 27 Apr 2023 17:01:40 +0800 Subject: [PATCH 18/28] - change config --- PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs b/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs index b5f496f..e5206ec 100644 --- a/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs +++ b/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs @@ -285,7 +285,7 @@ private bool CheckAndReigsterManualSetKeys(ReferenceTreeNode node,CustomAttribut x.CustomAttributes.Any(y => y.AttributeType.FullName == _autoPropFieldAttrDef.FullName)).ToList(); var considerProperties = new List(); - //Auto Field ignore :{System.Int64 MsgPackDefineForInject.Struct1::k__BackingField}+{System.Runtime.CompilerServices.CompilerGeneratedAttribute} + //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 From f3713cb50bb7050f05af442807ecee548dcec2e0 Mon Sep 17 00:00:00 2001 From: PatchouliTC <1009609373@qq.com> Date: Thu, 27 Apr 2023 17:22:04 +0800 Subject: [PATCH 19/28] Update README.md --- README.md | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e3fdcb2..1f36271 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,11 @@ # AutoPolymorphicMessagePack Auto Union Scanner For [MessagePack-CSharp](https://github.com/neuecc/MessagePack-CSharp) -Let you use more easy way to union messagepack object +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) @@ -116,6 +120,43 @@ Then set your `Project1` follow these steps: _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 + + + +``` + +`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 From 05a1ad198ee9e7790185cd87b677c9c7c66c6558 Mon Sep 17 00:00:00 2001 From: PatchouliTC <1009609373@qq.com> Date: Thu, 27 Apr 2023 17:22:56 +0800 Subject: [PATCH 20/28] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1f36271..de6f69a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # AutoPolymorphicMessagePack -Auto Union Scanner For [MessagePack-CSharp](https://github.com/neuecc/MessagePack-CSharp) +Auto Union Scanner & Auto Key Generator For [MessagePack-CSharp](https://github.com/neuecc/MessagePack-CSharp) Let you use more easy way to From b7e9ed625b07910c8b8cbf9eb417fa57d71c3c78 Mon Sep 17 00:00:00 2001 From: PatchouliTC <1009609373@qq.com> Date: Thu, 27 Apr 2023 19:58:31 +0800 Subject: [PATCH 21/28] - add prophasdefaultvalue check support --- MsgPackDefineForInject/MsgPackClass.cs | 4 +-- .../MsgPackDefineForInject.csproj | 2 +- .../AutoMsgPackKeyWeaver.cs | 34 +++++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/MsgPackDefineForInject/MsgPackClass.cs b/MsgPackDefineForInject/MsgPackClass.cs index 0944401..d692612 100644 --- a/MsgPackDefineForInject/MsgPackClass.cs +++ b/MsgPackDefineForInject/MsgPackClass.cs @@ -60,7 +60,7 @@ public class Class6 : IBase3 [Key(0)] public long CT6 { get; set; } - public long CT6_1 { get; set; } + public long CT6_1 { get; set; } = 12306; private long CT6_2; } @@ -87,7 +87,7 @@ public class Class8 : CBase6 [MessagePackObject] public struct Struct1 { - private long ST1 { get; } + private long ST1 { get; set; } [Key(1)] public long ST2 { get; set; } diff --git a/MsgPackDefineForInject/MsgPackDefineForInject.csproj b/MsgPackDefineForInject/MsgPackDefineForInject.csproj index 8fe1ad1..dd04bde 100644 --- a/MsgPackDefineForInject/MsgPackDefineForInject.csproj +++ b/MsgPackDefineForInject/MsgPackDefineForInject.csproj @@ -2,7 +2,7 @@ netstandard2.0 - false + true diff --git a/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs b/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs index e5206ec..9fc0484 100644 --- a/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs +++ b/PolymorphicMessagePack.Fody/AutoMsgPackKeyWeaver.cs @@ -1,6 +1,7 @@ using Fody; using MessagePack; using Mono.Cecil; +using Mono.Cecil.Cil; using Mono.Cecil.Rocks; using System; using System.Collections.Generic; @@ -43,6 +44,9 @@ public class AutoMsgPackKeyWeaver : BaseModuleWeaver MethodReference _msgIgnoreConstructor; MethodReference _msgKeyConstructor; + + MethodDefinition _objctor; + internal class ReferenceTreeNode { internal TypeDefinition node; @@ -57,6 +61,7 @@ internal class ReferenceTreeNode public AutoMsgPackKeyWeaver() { _msgPackAttrAsName = _msgObjAttr.Assembly.GetName().Name; + } public override void Execute() @@ -122,6 +127,7 @@ public override void Execute() typeNode.RequireAddKeyFields.Add(field); } } + foreach (var property in currentTypeRequireMarkCollection.Item2) { var keyAttrs = property.CustomAttributes.Where(x => x.AttributeType.FullName == _msgKeyAttr.FullName || @@ -139,6 +145,7 @@ public override void Execute() } else { + //bool HasDefaultValueSet = PropHasDefaultValue(typeNode.node, property); //not set attr typeNode.RequireAddKeyProperties.Add(property); } @@ -181,6 +188,31 @@ public override void Execute() 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) { @@ -414,6 +446,8 @@ public override IEnumerable GetAssembliesForScanning() 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(); From b75d9cb2736902ca8f34392c3a1fb993674c79dd Mon Sep 17 00:00:00 2001 From: PatchouliTC <1009609373@qq.com> Date: Thu, 27 Apr 2023 20:17:47 +0800 Subject: [PATCH 22/28] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index de6f69a..9f93463 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,9 @@ If you want to enable auto Key generate ,set `FodyWeavers.xml` with `AutoMsgPack ``` +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 From eaf7401aadfd502c43605c66d774a11705e74aec Mon Sep 17 00:00:00 2001 From: PatchouliTC <1009609373@qq.com> Date: Thu, 27 Apr 2023 20:20:06 +0800 Subject: [PATCH 23/28] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 9f93463..d1cfa69 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,8 @@ Mark which abstract class or interface you want to union for (e.g.these class ar 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` From b78059857e48a6deb6c7474ede80e5f5e649bff3 Mon Sep 17 00:00:00 2001 From: PatchouliTC <1009609373@qq.com> Date: Thu, 27 Apr 2023 20:27:03 +0800 Subject: [PATCH 24/28] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d1cfa69..99fb713 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Then set your `Project1` follow these steps: ``` - 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 before the projects consuming it. + 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`: From 53a041877f5e6ccdb2501fff18ea80becc17f96a Mon Sep 17 00:00:00 2001 From: PatchouliTC <1009609373@qq.com> Date: Mon, 15 May 2023 22:34:30 +0800 Subject: [PATCH 25/28] - add support for diff abstract assembly --- AbsInjectTypeDll/AbsInjectTypeDll.csproj | 12 +++++ AbsInjectTypeDll/Class1.cs | 38 +++++++++++++++ Fody.Test/AutoPolyWeaverTests.cs | 2 +- MsgPackDefineForInject/AbsDefine.cs | 46 +++++++++---------- MsgPackDefineForInject/FodyWeavers.xml | 2 +- MsgPackDefineForInject/MsgPackClass.cs | 3 +- .../MsgPackDefineForInject.csproj | 5 +- .../AutoPolyMsgPackWeaver.cs | 30 ++++++++++-- .../AutoPolyMsgPackWeaver.xcf | 6 +++ PolymorphicMessagePack.sln | 6 +++ 10 files changed, 118 insertions(+), 32 deletions(-) create mode 100644 AbsInjectTypeDll/AbsInjectTypeDll.csproj create mode 100644 AbsInjectTypeDll/Class1.cs 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..142ee5b --- /dev/null +++ b/AbsInjectTypeDll/Class1.cs @@ -0,0 +1,38 @@ +using ShareAttributes; +using System; + +namespace AbsInjectTypeDll +{ + + [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/Fody.Test/AutoPolyWeaverTests.cs b/Fody.Test/AutoPolyWeaverTests.cs index e6fdf7a..e05736b 100644 --- a/Fody.Test/AutoPolyWeaverTests.cs +++ b/Fody.Test/AutoPolyWeaverTests.cs @@ -18,7 +18,7 @@ public class AutoPolyWeaverTests static AutoPolyWeaverTests() { - var xElement = XElement.Parse(""); + var xElement = XElement.Parse(""); var weavingTask = new AutoPolyMsgPackWeaver { Config= xElement }; testResult = weavingTask.ExecuteTestRun("MsgPackDefineForInject.dll",runPeVerify:false); } diff --git a/MsgPackDefineForInject/AbsDefine.cs b/MsgPackDefineForInject/AbsDefine.cs index bc4ab7a..b564f8f 100644 --- a/MsgPackDefineForInject/AbsDefine.cs +++ b/MsgPackDefineForInject/AbsDefine.cs @@ -3,35 +3,35 @@ namespace MsgPackDefineForInject { - [UnionAbsOrInterface] - public abstract class CBase1 - { - } + //[UnionAbsOrInterface] + //public abstract class CBase1 + //{ + //} - public abstract class CBase2 - { - public long CTB2 { get; set; } - } + //public abstract class CBase2 + //{ + // public long CTB2 { get; set; } + //} - [UnionAbsOrInterface] - public abstract class CBase3 : CBase1 - { + //[UnionAbsOrInterface] + //public abstract class CBase3 : CBase1 + //{ - } + //} - [UnionAbsOrInterface] - public abstract class CBase4 - { + //[UnionAbsOrInterface] + //public abstract class CBase4 + //{ - } + //} - public abstract class CBase5 : CBase4 - { + //public abstract class CBase5 : CBase4 + //{ - } - [UnionAbsOrInterface] - public abstract class CBase6 : CBase4 - { + //} + //[UnionAbsOrInterface] + //public abstract class CBase6 : CBase4 + //{ - } + //} } diff --git a/MsgPackDefineForInject/FodyWeavers.xml b/MsgPackDefineForInject/FodyWeavers.xml index 8415980..c064b07 100644 --- a/MsgPackDefineForInject/FodyWeavers.xml +++ b/MsgPackDefineForInject/FodyWeavers.xml @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/MsgPackDefineForInject/MsgPackClass.cs b/MsgPackDefineForInject/MsgPackClass.cs index d692612..c7bb71b 100644 --- a/MsgPackDefineForInject/MsgPackClass.cs +++ b/MsgPackDefineForInject/MsgPackClass.cs @@ -1,4 +1,5 @@ -using MessagePack; +using AbsInjectTypeDll; +using MessagePack; using ShareAttributes; using System.Runtime.Serialization; diff --git a/MsgPackDefineForInject/MsgPackDefineForInject.csproj b/MsgPackDefineForInject/MsgPackDefineForInject.csproj index dd04bde..1b76c92 100644 --- a/MsgPackDefineForInject/MsgPackDefineForInject.csproj +++ b/MsgPackDefineForInject/MsgPackDefineForInject.csproj @@ -1,8 +1,8 @@  - netstandard2.0 - true + netstandard2.1 + false @@ -22,6 +22,7 @@ + diff --git a/PolymorphicMessagePack.Fody/AutoPolyMsgPackWeaver.cs b/PolymorphicMessagePack.Fody/AutoPolyMsgPackWeaver.cs index 607fae7..25e5e10 100644 --- a/PolymorphicMessagePack.Fody/AutoPolyMsgPackWeaver.cs +++ b/PolymorphicMessagePack.Fody/AutoPolyMsgPackWeaver.cs @@ -41,6 +41,8 @@ public AutoPolyMsgPackWeaver() public override void Execute() { var ns = GetValueFromConfig("NameSpace"); + var absns = GetValueFromConfig("AbsNameSpace"); + if(ns == null) { WriteError("Scan Assembly name not set in config"); @@ -48,10 +50,30 @@ public override void Execute() } InitBasicRequireRef(); - var requireConsiderAbsAndInterfaceTypes = ModuleDefinition.Types.Where( - x => x.HasCustomAttributes && - x.Namespace == ns && - x.CustomAttributes.Any(y => y.AttributeType.FullName == _absAttr.FullName)); + IEnumerable requireConsiderAbsAndInterfaceTypes; + + if (absns == null) + { + //find abs from target namespace + requireConsiderAbsAndInterfaceTypes = ModuleDefinition.Types.Where( + x => x.HasCustomAttributes && + x.Namespace == 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; + } + requireConsiderAbsAndInterfaceTypes= ModuleDefinition.AssemblyResolver.Resolve(absLocationAssembly). + MainModule.Types.Where( + x => x.HasCustomAttributes && + x.Namespace == absns && + x.CustomAttributes.Any(y => y.AttributeType.FullName == _absAttr.FullName)); + } var resultsForNonGenericTypes = GetNonGenericDerivedTypes(ModuleDefinition, requireConsiderAbsAndInterfaceTypes).OrderBy(x => x.FullName); diff --git a/PolymorphicMessagePack.Fody/AutoPolyMsgPackWeaver.xcf b/PolymorphicMessagePack.Fody/AutoPolyMsgPackWeaver.xcf index 1839755..feee850 100644 --- a/PolymorphicMessagePack.Fody/AutoPolyMsgPackWeaver.xcf +++ b/PolymorphicMessagePack.Fody/AutoPolyMsgPackWeaver.xcf @@ -6,4 +6,10 @@ Namespace to use for the injected type + + + Abstruct Type in space + + \ No newline at end of file diff --git a/PolymorphicMessagePack.sln b/PolymorphicMessagePack.sln index 0632001..3503567 100644 --- a/PolymorphicMessagePack.sln +++ b/PolymorphicMessagePack.sln @@ -18,6 +18,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PolymorphicMessagePack.Fody EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fody.Test", "Fody.Test\Fody.Test.csproj", "{863134AE-368E-46E9-8D60-72AC9EDBA107}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AbsInjectTypeDll", "AbsInjectTypeDll\AbsInjectTypeDll.csproj", "{FFC7FCC9-C9A6-4896-8807-670D52C725AD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -48,6 +50,10 @@ Global {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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 025dd893334da1d783f55afdd88bfa129c0864b9 Mon Sep 17 00:00:00 2001 From: PatchouliTC <1009609373@qq.com> Date: Mon, 15 May 2023 23:00:12 +0800 Subject: [PATCH 26/28] - fix assembly error --- AbsInjectTypeDll/Class1.cs | 2 +- MsgPackDefineForInject/MsgPackClass.cs | 2 +- MsgPackDefineForInject/MsgPackDefineForInject.csproj | 2 +- PolymorphicMessagePack.Fody/AutoPolyMsgPackWeaver.cs | 12 ++++++------ 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/AbsInjectTypeDll/Class1.cs b/AbsInjectTypeDll/Class1.cs index 142ee5b..48bbc04 100644 --- a/AbsInjectTypeDll/Class1.cs +++ b/AbsInjectTypeDll/Class1.cs @@ -1,7 +1,7 @@ using ShareAttributes; using System; -namespace AbsInjectTypeDll +namespace AbsInjectTypeDll.DllSubAssembly { [UnionAbsOrInterface] diff --git a/MsgPackDefineForInject/MsgPackClass.cs b/MsgPackDefineForInject/MsgPackClass.cs index c7bb71b..c8d5cb0 100644 --- a/MsgPackDefineForInject/MsgPackClass.cs +++ b/MsgPackDefineForInject/MsgPackClass.cs @@ -1,4 +1,4 @@ -using AbsInjectTypeDll; +using AbsInjectTypeDll.DllSubAssembly; using MessagePack; using ShareAttributes; using System.Runtime.Serialization; diff --git a/MsgPackDefineForInject/MsgPackDefineForInject.csproj b/MsgPackDefineForInject/MsgPackDefineForInject.csproj index 1b76c92..200c203 100644 --- a/MsgPackDefineForInject/MsgPackDefineForInject.csproj +++ b/MsgPackDefineForInject/MsgPackDefineForInject.csproj @@ -2,7 +2,7 @@ netstandard2.1 - false + true diff --git a/PolymorphicMessagePack.Fody/AutoPolyMsgPackWeaver.cs b/PolymorphicMessagePack.Fody/AutoPolyMsgPackWeaver.cs index 25e5e10..1444daa 100644 --- a/PolymorphicMessagePack.Fody/AutoPolyMsgPackWeaver.cs +++ b/PolymorphicMessagePack.Fody/AutoPolyMsgPackWeaver.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; namespace PolymorphicMessagePack.Fody { @@ -57,7 +58,7 @@ public override void Execute() //find abs from target namespace requireConsiderAbsAndInterfaceTypes = ModuleDefinition.Types.Where( x => x.HasCustomAttributes && - x.Namespace == ns && + x.Module.Assembly.Name.Name == ns && x.CustomAttributes.Any(y => y.AttributeType.FullName == _absAttr.FullName)); } else @@ -68,10 +69,11 @@ public override void Execute() WriteError($"Not Find abstract type assembly {absns} in {ns} reference assemblies"); return; } - requireConsiderAbsAndInterfaceTypes= ModuleDefinition.AssemblyResolver.Resolve(absLocationAssembly). - MainModule.Types.Where( + var resolveTypes = ModuleDefinition.AssemblyResolver.Resolve(absLocationAssembly). + MainModule.Types; + requireConsiderAbsAndInterfaceTypes = resolveTypes.Where( x => x.HasCustomAttributes && - x.Namespace == absns && + x.Module.Assembly.Name.Name == absns && x.CustomAttributes.Any(y => y.AttributeType.FullName == _absAttr.FullName)); } @@ -412,8 +414,6 @@ string GetValueFromConfig(string name) var value = attribute.Value; return value; } - - #endregion } } From db17b7e03b65ae1a38f52b2bbff458587285a296 Mon Sep 17 00:00:00 2001 From: PatchouliTC <1009609373@qq.com> Date: Tue, 16 May 2023 00:26:57 +0800 Subject: [PATCH 27/28] - fix --- AbsInjectTypeDll/Class1.cs | 33 +++++++++++++ MsgPackDefineForInject/InterfaceDefine.cs | 44 +++++++++--------- MsgPackDefineForInject/MsgPackClass.cs | 2 +- .../MsgPackDefineForInject.csproj | 3 +- PolyMsgPack.Test/PolyMsgPackTest.cs | 46 ++++++++++--------- PolymorphicMessagePack.csproj | 3 ++ .../PolymorphicMessagePackSettings.cs | 28 +++++------ 7 files changed, 100 insertions(+), 59 deletions(-) diff --git a/AbsInjectTypeDll/Class1.cs b/AbsInjectTypeDll/Class1.cs index 48bbc04..4aed4b6 100644 --- a/AbsInjectTypeDll/Class1.cs +++ b/AbsInjectTypeDll/Class1.cs @@ -35,4 +35,37 @@ 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/MsgPackDefineForInject/InterfaceDefine.cs b/MsgPackDefineForInject/InterfaceDefine.cs index cfb9eb3..6cf0cf0 100644 --- a/MsgPackDefineForInject/InterfaceDefine.cs +++ b/MsgPackDefineForInject/InterfaceDefine.cs @@ -2,36 +2,36 @@ namespace MsgPackDefineForInject { - [UnionAbsOrInterface] - public interface IBase1 - { + //[UnionAbsOrInterface] + //public interface IBase1 + //{ - } + //} - public interface IBase2 - { + //public interface IBase2 + //{ - } + //} - [UnionAbsOrInterface] - public interface IBase3 : IBase1 - { + //[UnionAbsOrInterface] + //public interface IBase3 : IBase1 + //{ - } + //} - [UnionAbsOrInterface] - public interface IBase4 - { + //[UnionAbsOrInterface] + //public interface IBase4 + //{ - } + //} - public interface IBase5 : IBase4 - { + //public interface IBase5 : IBase4 + //{ - } - [UnionAbsOrInterface] - public interface IBase6 : IBase4 - { + //} + //[UnionAbsOrInterface] + //public interface IBase6 : IBase4 + //{ - } + //} } diff --git a/MsgPackDefineForInject/MsgPackClass.cs b/MsgPackDefineForInject/MsgPackClass.cs index c8d5cb0..9d1327e 100644 --- a/MsgPackDefineForInject/MsgPackClass.cs +++ b/MsgPackDefineForInject/MsgPackClass.cs @@ -5,7 +5,7 @@ namespace MsgPackDefineForInject { - + [MessagePackObject] public class Class1 : CBase1 { diff --git a/MsgPackDefineForInject/MsgPackDefineForInject.csproj b/MsgPackDefineForInject/MsgPackDefineForInject.csproj index 200c203..b630ad5 100644 --- a/MsgPackDefineForInject/MsgPackDefineForInject.csproj +++ b/MsgPackDefineForInject/MsgPackDefineForInject.csproj @@ -2,7 +2,8 @@ netstandard2.1 - true + true + false diff --git a/PolyMsgPack.Test/PolyMsgPackTest.cs b/PolyMsgPack.Test/PolyMsgPackTest.cs index 6e5c895..987e380 100644 --- a/PolyMsgPack.Test/PolyMsgPackTest.cs +++ b/PolyMsgPack.Test/PolyMsgPackTest.cs @@ -1,4 +1,6 @@ -using MessagePack; +using AbsInjectTypeDll.DllSubAssembly; +using MessagePack; +using MessagePack.Resolvers; using MsgPackDefineForInject; using PolymorphicMessagePack; @@ -8,70 +10,72 @@ namespace PolyMsgPack.Test [TestClass] public class PolyMsgPackTest { - PolymorphicMessagePackSerializerOptions _options; + PolymorphicMessagePackSerializerOptions _polyOptions; public PolyMsgPackTest() { - var polySettings = new PolymorphicMessagePackSettings(); + var polySettings = new PolymorphicMessagePackSettings(StandardResolver.Instance); - polySettings.InjectUnionRequireFromAssembly(typeof(PolyMsgPackTest).Assembly); + polySettings.InjectUnionRequireFromAssembly(typeof(Class1).Assembly,typeof(CBase1).Assembly); - _options = new PolymorphicMessagePackSerializerOptions(polySettings); + _polyOptions = new PolymorphicMessagePackSerializerOptions(polySettings); + + MessagePackSerializer.DefaultOptions = _polyOptions; } [TestMethod] public void Test_NonGenericToAbs() { - var s = MessagePackSerializer.Serialize(new Class1 { CT1 = 1 }, _options); - var ds = MessagePackSerializer.Deserialize(s, _options); + var s = MessagePackSerializer.Serialize(new Class3 { CT1 = 3 }); + var ds = MessagePackSerializer.Deserialize(s); - Assert.IsTrue(ds is Class1 ds1 && ds1.CT1 == 1); + Assert.IsTrue(ds is Class1 ds1 && ds1.CT1 == 3); } [TestMethod] public void Test_NotMarkClassSer() { - var s = MessagePackSerializer.Serialize(new Class2 { CT2 = 1 }, _options); - Assert.ThrowsException(() => MessagePackSerializer.Deserialize(s, _options)); + 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 }, _options); - var ds = MessagePackSerializer.Deserialize(s, _options); + 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 }, _options); - var ds = MessagePackSerializer.Deserialize(s, _options); + 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 }, _options); - var ds = MessagePackSerializer.Deserialize>(s, _options); + 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 }, _options); - var ds = MessagePackSerializer.Deserialize>(s, _options); + 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(), _options); - var ds1 = MessagePackSerializer.Deserialize(s, _options); - var ds2 = MessagePackSerializer.Deserialize(s, _options); + 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/PolymorphicMessagePack.csproj b/PolymorphicMessagePack.csproj index 2733159..38a3510 100644 --- a/PolymorphicMessagePack.csproj +++ b/PolymorphicMessagePack.csproj @@ -5,6 +5,7 @@ + @@ -12,6 +13,7 @@ + @@ -19,6 +21,7 @@ + diff --git a/PolymorphicMessagePack/PolymorphicMessagePackSettings.cs b/PolymorphicMessagePack/PolymorphicMessagePackSettings.cs index 19f3378..83a3c4f 100644 --- a/PolymorphicMessagePack/PolymorphicMessagePackSettings.cs +++ b/PolymorphicMessagePack/PolymorphicMessagePackSettings.cs @@ -18,24 +18,24 @@ public static (Assembly, HashSet) GetMarkUnionAbsAttributeClasses(this Ass return (assembly, markdata); } - public static Dictionary> GetAbsDriveClassTypes(this (Assembly assembly, HashSet types) data) + public static Dictionary> GetAbsDriveClassTypes(this HashSet types,Assembly target) { - var require_search_types = data.assembly.GetTypes().Where(x => + 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 && !data.types.Contains(x) && !x.IsGenericType + !x.IsAbstract && !x.IsInterface && x.IsClass && x != _objType && !types.Contains(x) && !x.IsGenericType ); - return AnalysisTypes(require_search_types, data.types); + return AnalysisTypes(require_search_types, types); } - public static Dictionary> GetAbsDriveGenericClassTypes(this (Assembly assembly, HashSet types) data) + public static Dictionary> GetAbsDriveGenericClassTypes(this HashSet types, Assembly target) { - var require_search_types = data.assembly.GetTypes().Where(x => + 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 && !data.types.Contains(x) && x.IsGenericType + !x.IsAbstract && !x.IsInterface && x.IsClass && x != _objType && !types.Contains(x) && x.IsGenericType ); - return AnalysisTypes(require_search_types, data.types); + return AnalysisTypes(require_search_types, types); } private static Dictionary> AnalysisTypes(IEnumerable types, HashSet exceptTypes) @@ -156,17 +156,17 @@ public void RegisterType(uint typeId) BaseTypes.Add(typeof(B)); } - public void InjectUnionRequireFromAssembly(Assembly assembly) + public void InjectUnionRequireFromAssembly(Assembly assembly,Assembly absClassAssembly) { - if (Assemblies.Contains(assembly)) + if (Assemblies.Contains(assembly) && Assemblies.Contains(absClassAssembly)) return; Assemblies.Add(assembly); - + Assemblies.Add(absClassAssembly); //get all mark require union abs/interface - var markedUnionRequireAbsOrInterfaces = assembly.GetMarkUnionAbsAttributeClasses(); + var markedUnionRequireAbsOrInterfaces = absClassAssembly.GetMarkUnionAbsAttributeClasses(); //get all drive abs/interface non generic classes - var allNeedMapNonGenericClasses = markedUnionRequireAbsOrInterfaces.GetAbsDriveClassTypes(); + var allNeedMapNonGenericClasses = markedUnionRequireAbsOrInterfaces.Item2.GetAbsDriveClassTypes(assembly); //register them,record relate abs and interface foreach (var pair in allNeedMapNonGenericClasses) @@ -189,7 +189,7 @@ public void InjectUnionRequireFromAssembly(Assembly assembly) } //get all drive abs/interface generic classes - var allNeedRecordGenericClasses = markedUnionRequireAbsOrInterfaces.GetAbsDriveGenericClassTypes(); + var allNeedRecordGenericClasses = markedUnionRequireAbsOrInterfaces.Item2.GetAbsDriveGenericClassTypes(assembly); //record all need prepare generic type class type foreach (var pair in allNeedRecordGenericClasses) { From 7387a4f5a4c7983f7e85e1785d389e59f6278578 Mon Sep 17 00:00:00 2001 From: PatchouliTC <1009609373@qq.com> Date: Tue, 16 May 2023 04:46:45 +0800 Subject: [PATCH 28/28] - add test magiconion support --- PolyClient/PolyClient.csproj | 19 ++++++ PolyClient/Program.cs | 44 +++++++++++++ PolyMsgPack.Test/PolyMsgPackTest.cs | 2 +- PolyServer/MyFirstService.cs | 32 +++++++++ PolyServer/PolyServer.csproj | 22 +++++++ PolyServer/Program.cs | 35 ++++++++++ PolyServer/Properties/launchSettings.json | 48 ++++++++++++++ PolyServer/Startup.cs | 60 +++++++++++++++++ PolyServer/appsettings.Development.json | 21 ++++++ PolyServer/appsettings.json | 21 ++++++ PolymorphicMessagePack.csproj | 12 +++- PolymorphicMessagePack.sln | 20 +++++- .../MagicOnionPolyMsgPackFormatter.cs | 66 +++++++++++++++++++ ...PolymorphicMessagePackSerializerOptions.cs | 1 - PolymorphicMessagePack/PolymorphicResolver.cs | 2 +- Service.Shared/IMyFirstService.cs | 20 ++++++ Service.Shared/Service.Shared.csproj | 17 +++++ 17 files changed, 437 insertions(+), 5 deletions(-) create mode 100644 PolyClient/PolyClient.csproj create mode 100644 PolyClient/Program.cs create mode 100644 PolyServer/MyFirstService.cs create mode 100644 PolyServer/PolyServer.csproj create mode 100644 PolyServer/Program.cs create mode 100644 PolyServer/Properties/launchSettings.json create mode 100644 PolyServer/Startup.cs create mode 100644 PolyServer/appsettings.Development.json create mode 100644 PolyServer/appsettings.json create mode 100644 PolymorphicMessagePack/MagicOnionPolyMsgPackFormatter.cs create mode 100644 Service.Shared/IMyFirstService.cs create mode 100644 Service.Shared/Service.Shared.csproj 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/PolyMsgPackTest.cs b/PolyMsgPack.Test/PolyMsgPackTest.cs index 987e380..dd6eb35 100644 --- a/PolyMsgPack.Test/PolyMsgPackTest.cs +++ b/PolyMsgPack.Test/PolyMsgPackTest.cs @@ -25,7 +25,7 @@ public PolyMsgPackTest() [TestMethod] public void Test_NonGenericToAbs() { - var s = MessagePackSerializer.Serialize(new Class3 { CT1 = 3 }); + var s = MessagePackSerializer.Serialize(new Class3 { CT1 = 3,CT3=4}); var ds = MessagePackSerializer.Deserialize(s); Assert.IsTrue(ds is Class1 ds1 && ds1.CT1 == 3); 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.csproj b/PolymorphicMessagePack.csproj index 38a3510..dc02cad 100644 --- a/PolymorphicMessagePack.csproj +++ b/PolymorphicMessagePack.csproj @@ -1,37 +1,47 @@ - net6.0 + netstandard2.1 + + + + + + + + + + diff --git a/PolymorphicMessagePack.sln b/PolymorphicMessagePack.sln index 3503567..f56c37e 100644 --- a/PolymorphicMessagePack.sln +++ b/PolymorphicMessagePack.sln @@ -18,7 +18,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PolymorphicMessagePack.Fody EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fody.Test", "Fody.Test\Fody.Test.csproj", "{863134AE-368E-46E9-8D60-72AC9EDBA107}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AbsInjectTypeDll", "AbsInjectTypeDll\AbsInjectTypeDll.csproj", "{FFC7FCC9-C9A6-4896-8807-670D52C725AD}" +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 @@ -54,6 +60,18 @@ Global {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 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/PolymorphicMessagePackSerializerOptions.cs b/PolymorphicMessagePack/PolymorphicMessagePackSerializerOptions.cs index 6152253..cb09f0f 100644 --- a/PolymorphicMessagePack/PolymorphicMessagePackSerializerOptions.cs +++ b/PolymorphicMessagePack/PolymorphicMessagePackSerializerOptions.cs @@ -27,6 +27,5 @@ protected override MessagePackSerializerOptions Clone() { return new PolymorphicMessagePackSerializerOptions(this); } - } } diff --git a/PolymorphicMessagePack/PolymorphicResolver.cs b/PolymorphicMessagePack/PolymorphicResolver.cs index b1f9983..4be8fff 100644 --- a/PolymorphicMessagePack/PolymorphicResolver.cs +++ b/PolymorphicMessagePack/PolymorphicResolver.cs @@ -11,7 +11,7 @@ internal sealed class PolymorphicResolver : IFormatterResolver { private readonly PolymorphicMessagePackSettings _polymorphicSettings; - private readonly ConcurrentDictionary _innerDeserializeFormatterCache = new(); + private readonly ConcurrentDictionary _innerDeserializeFormatterCache = new ConcurrentDictionary(); public PolymorphicResolver(PolymorphicMessagePackSettings polymorphicSettings) { _polymorphicSettings = polymorphicSettings; 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 + + + + + + + + + + + +