From 940ba25c1135e285c2eabd1045addf787f23a16d Mon Sep 17 00:00:00 2001 From: lin Date: Thu, 10 Nov 2022 16:38:02 +0800 Subject: [PATCH] =?UTF-8?q?1.=E5=A2=9E=E5=8A=A0=E5=AF=B9=E7=BB=A7=E6=89=BF?= =?UTF-8?q?=E7=9A=84=E6=94=AF=E6=8C=81=202.=E5=A2=9E=E5=8A=A0=E7=BB=A7?= =?UTF-8?q?=E6=89=BF=E7=9A=84=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- MongoIncUpdate.Base/IncUpdate.cs | 3 +- MongoIncUpdate.Fody.Example/Benchmark.cs | 4 +- MongoIncUpdate.Fody.Example/Program.cs | 4 +- .../IncUpdateDirtyTest.cs | 6 +- .../IncUpdateInheritTest.cs | 188 ++++++++++++++++++ MongoIncUpdate.Fody.Test/IncUpdateTests.cs | 18 +- MongoIncUpdate.Fody/CallMapper.cs | 29 --- MongoIncUpdate.Fody/CecilExtensions.cs | 131 +----------- MongoIncUpdate.Fody/InjectOverrideProperty.cs | 8 +- MongoIncUpdate.Fody/InjectSetterPropChange.cs | 4 +- MongoIncUpdate.Fody/MemberSelector.cs | 19 -- MongoIncUpdate.Fody/MemberVirtualizer.cs | 15 -- MongoIncUpdate.Fody/ModuleWeaver.cs | 106 ++++++---- MongoIncUpdate.Fody/TypeDefine.cs | 6 +- MongoIncUpdate.Fody/TypeSelector.cs | 47 ++--- 15 files changed, 304 insertions(+), 284 deletions(-) create mode 100644 MongoIncUpdate.Fody.Test/IncUpdateInheritTest.cs delete mode 100644 MongoIncUpdate.Fody/CallMapper.cs delete mode 100644 MongoIncUpdate.Fody/MemberSelector.cs delete mode 100644 MongoIncUpdate.Fody/MemberVirtualizer.cs diff --git a/MongoIncUpdate.Base/IncUpdate.cs b/MongoIncUpdate.Base/IncUpdate.cs index eb81a8c..85924d7 100644 --- a/MongoIncUpdate.Base/IncUpdate.cs +++ b/MongoIncUpdate.Base/IncUpdate.cs @@ -50,7 +50,8 @@ public static async Task IncUpdate(this IMongoCollection //更新 var setter = Builders.Update.Combine(defs); // Console.WriteLine($"update data count:{defs.Count}"); - return await collection.UpdateOneAsync(filter, setter, UpdateOptions); + var result = await collection.UpdateOneAsync(filter, setter, UpdateOptions); + return result; } catch (Exception) //增量更新失败则退化为全量替换(逻辑上没上用,增量异常了全量替换也大概率一场--仅仅是为了保证逻辑完整--) { diff --git a/MongoIncUpdate.Fody.Example/Benchmark.cs b/MongoIncUpdate.Fody.Example/Benchmark.cs index 56ba920..fa98583 100644 --- a/MongoIncUpdate.Fody.Example/Benchmark.cs +++ b/MongoIncUpdate.Fody.Example/Benchmark.cs @@ -26,7 +26,6 @@ public class NestOldItem public Dictionary OItems { get; set; } //1 } -[MongoIncUpdate] public class DirtyItem { public int Int { get; set; } //0 @@ -35,7 +34,6 @@ public class DirtyItem public string? Str { get; set; } //3 } -[MongoIncUpdate] public class DirtyNestItem { [BsonId] public int Id { get; set; } @@ -48,7 +46,7 @@ public class DirtyNestItem [MemoryDiagnoser] [RankColumn] -public class IncUpdateBenchmark +public partial class IncUpdateBenchmark { private const int _stateMapCount = 1000; private static DirtyNestItem _benchmarkIncUpdateData; diff --git a/MongoIncUpdate.Fody.Example/Program.cs b/MongoIncUpdate.Fody.Example/Program.cs index 1b598e2..abf58a4 100644 --- a/MongoIncUpdate.Fody.Example/Program.cs +++ b/MongoIncUpdate.Fody.Example/Program.cs @@ -4,9 +4,9 @@ namespace MongoIncUpdate.Fody.Example; public class Program { - public static void Main(string[] args) + public static async Task Main(string[] args) { - Benchmark(args); + // Benchmark(args); // Test(args); } diff --git a/MongoIncUpdate.Fody.Test/IncUpdateDirtyTest.cs b/MongoIncUpdate.Fody.Test/IncUpdateDirtyTest.cs index a558b8f..0ee92c0 100644 --- a/MongoIncUpdate.Fody.Test/IncUpdateDirtyTest.cs +++ b/MongoIncUpdate.Fody.Test/IncUpdateDirtyTest.cs @@ -9,7 +9,7 @@ namespace MongoIncUpdate.Fody.Test; [MongoIncUpdate] -public class DirtyItem +public sealed class DirtyItem { public int Int { get; set; } //0 public float Flo { get; set; } //1 @@ -18,7 +18,7 @@ public class DirtyItem } [MongoIncUpdate] -public class DirtyNestItem +public sealed class DirtyNestItem { [BsonId] public int Id { get; set; } @@ -56,7 +56,7 @@ private void DirtyTest() StateMap = new StateMap { { 2, new DirtyItem { Int = 2, Str = "2", Flo = 2.0f, Dou = 2.0 } } } }; - var b = (IDiffUpdateable)a; + var b = (IDiffUpdateable)((object)a); Assert.NotNull(b); _output.WriteLine("全量写入结果检查"); diff --git a/MongoIncUpdate.Fody.Test/IncUpdateInheritTest.cs b/MongoIncUpdate.Fody.Test/IncUpdateInheritTest.cs new file mode 100644 index 0000000..ed415d2 --- /dev/null +++ b/MongoIncUpdate.Fody.Test/IncUpdateInheritTest.cs @@ -0,0 +1,188 @@ +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Driver; +using MongoIncUpdate.Base; +using Xunit; + +namespace MongoIncUpdate.Fody.Test; + +public interface IItemDataBase +{ + // 道具id + int ItemId { get; set; } + + // 道具数量 + int GetCount(); + + // 设置道具数量 + void SetCount(int count) + { + } +} + +public interface IIncrItemData : IItemDataBase +{ + // 自增id + int IncrId { get; set; } +} + +public abstract class BaseIncrSingleItemData : IIncrItemData +{ + public int ItemId { get; set; } + + public int GetCount() + { + return 1; + } + + public int IncrId { get; set; } +} + +[MongoIncUpdate] +public sealed class WeaponItemData : BaseIncrSingleItemData +{ + [BsonId] public int PlayerId { get; set; } + + // 等级 + public int Level { get; set; } + + // 经验 + public int LevelExp { get; set; } + + // 突破等级 + public int Breach { get; set; } + + // 绑定角色id + public int RoleId { get; set; } + + // 共鸣等级 + public int ResonLevel { get; set; } +} + +[MongoIncUpdate] +public sealed class EquipItemData : BaseIncrSingleItemData +{ + [BsonId] public int PlayerId { get; set; } + + // 等级 + public int Level { get; set; } + + // 经验 + public int LevelExp { get; set; } + + // 突破等级 + public int Breach { get; set; } + + // 绑定角色id + public int RoleId { get; set; } + + // 共鸣等级 + public int ResonLevel { get; set; } +} + +public abstract class NoUsedBaseIncrSingleItemData : IIncrItemData +{ + public int ItemId { get; set; } + public int GetCount() => 1; + + public int IncrId { get; set; } +} + +public class IncUpdateInheritTest +{ + private static bool _init; + private static IMongoCollection _cc; + private static IMongoCollection _cc2; + + public IncUpdateInheritTest() + { + lock (this) + { + if (_init) return; + _init = true; + var connectionString = "mongodb://admin:123456@127.0.0.1:27017/test?authSource=admin"; + var mongoClient = new MongoClient(connectionString); + var db = mongoClient.GetDatabase(new MongoUrlBuilder(connectionString).DatabaseName); + _cc = db.GetCollection(nameof(WeaponItemData)); + _cc2 = db.GetCollection(nameof(EquipItemData)); + } + } + + [Fact] + public async Task TestItem() + { + { + var a = new WeaponItemData + { + PlayerId = 1, ItemId = 1, IncrId = 2, Level = 3, LevelExp = 4, Breach = 5, RoleId = 6, ResonLevel = 7 + }; + await _cc.IncUpdate(a); + a.ItemId = 11; + a.IncrId = 12; + a.Level = 13; + a.LevelExp = 14; + a.Breach = 15; + a.RoleId = 16; + a.ResonLevel = 17; + await _cc.IncUpdate(a); + var filer = Builders.Filter.Eq(x => x.PlayerId, a.PlayerId); + var v = (await _cc.FindAsync(filer)).First(); + Assert.Equal(v.ItemId, a.ItemId); + Assert.Equal(v.IncrId, a.IncrId); + Assert.Equal(v.Level, a.Level); + Assert.Equal(v.LevelExp, a.LevelExp); + Assert.Equal(v.Breach, a.Breach); + Assert.Equal(v.RoleId, a.RoleId); + Assert.Equal(v.ResonLevel, a.ResonLevel); + } + + { + var a = new WeaponItemData + { + PlayerId = 2, ItemId = 1, IncrId = 2, Level = 3, LevelExp = 4, Breach = 5, RoleId = 6, ResonLevel = 7 + }; + await _cc.IncUpdate(a); + a.ItemId = 111; + a.IncrId = 121; + a.Level = 131; + a.LevelExp = 141; + a.Breach = 151; + a.RoleId = 161; + a.ResonLevel = 171; + await _cc.IncUpdate(a); + var filer = Builders.Filter.Eq(x => x.PlayerId, a.PlayerId); + var v = (await _cc.FindAsync(filer)).First(); + Assert.Equal(v.ItemId, a.ItemId); + Assert.Equal(v.IncrId, a.IncrId); + Assert.Equal(v.Level, a.Level); + Assert.Equal(v.LevelExp, a.LevelExp); + Assert.Equal(v.Breach, a.Breach); + Assert.Equal(v.RoleId, a.RoleId); + Assert.Equal(v.ResonLevel, a.ResonLevel); + } + + { + var a = new EquipItemData + { + PlayerId = 1, ItemId = 1, IncrId = 2, Level = 3, LevelExp = 4, Breach = 5, RoleId = 6, ResonLevel = 7 + }; + await _cc2.IncUpdate(a); + a.ItemId = 111; + a.IncrId = 112; + a.Level = 113; + a.LevelExp = 114; + a.Breach = 115; + a.RoleId = 116; + a.ResonLevel = 117; + await _cc2.IncUpdate(a); + var filer = Builders.Filter.Eq(x => x.PlayerId, a.PlayerId); + var v = (await _cc2.FindAsync(filer)).First(); + Assert.Equal(v.ItemId, a.ItemId); + Assert.Equal(v.IncrId, a.IncrId); + Assert.Equal(v.Level, a.Level); + Assert.Equal(v.LevelExp, a.LevelExp); + Assert.Equal(v.Breach, a.Breach); + Assert.Equal(v.RoleId, a.RoleId); + Assert.Equal(v.ResonLevel, a.ResonLevel); + } + } +} \ No newline at end of file diff --git a/MongoIncUpdate.Fody.Test/IncUpdateTests.cs b/MongoIncUpdate.Fody.Test/IncUpdateTests.cs index e4590a3..83c3eb3 100644 --- a/MongoIncUpdate.Fody.Test/IncUpdateTests.cs +++ b/MongoIncUpdate.Fody.Test/IncUpdateTests.cs @@ -9,28 +9,28 @@ namespace MongoIncUpdate.Fody.Test; [MongoIncUpdate] -public class ItemInt +public sealed class ItemInt { [BsonId] public int Id { get; set; } public int I { get; set; } } [MongoIncUpdate] -public class ItemNestInt +public sealed class ItemNestInt { [BsonId] public int Id { get; set; } public ItemInt ItemInt { get; set; } } [MongoIncUpdate] -public class ItemNestNestInt +public sealed class ItemNestNestInt { [BsonId] public int Id { get; set; } public ItemNestInt ItemNestInt { get; set; } } [MongoIncUpdate] -public class ItemIntKeyStateMapInt +public sealed class ItemIntKeyStateMapInt { [BsonId] public int Id { get; set; } @@ -39,7 +39,7 @@ public class ItemIntKeyStateMapInt } [MongoIncUpdate] -public class ItemStringKeyStateMapString +public sealed class ItemStringKeyStateMapString { [BsonId] public int Id { get; set; } @@ -48,7 +48,7 @@ public class ItemStringKeyStateMapString } [MongoIncUpdate] -public class NestStateMapItem +public sealed class NestStateMapItem { [BsonId] public int Id { get; set; } @@ -57,20 +57,20 @@ public class NestStateMapItem } [MongoIncUpdate] -public class NestStateMapStateMapItem +public sealed class NestStateMapStateMapItem { [BsonId] public int Id { get; set; } public StateMap> StateMapStateMapItem { get; set; } = new(); } [MongoIncUpdate] -public class ItemNestItemStateMap +public sealed class ItemNestItemStateMap { public ItemIntKeyStateMapInt NestItem { get; set; } } [MongoIncUpdate] -public class ItemStateMapItemStateMapItem +public sealed class ItemStateMapItemStateMapItem { [BsonId] public int Id { get; set; } public StateMap Item { get; set; } = new(); diff --git a/MongoIncUpdate.Fody/CallMapper.cs b/MongoIncUpdate.Fody/CallMapper.cs deleted file mode 100644 index df1b184..0000000 --- a/MongoIncUpdate.Fody/CallMapper.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Mono.Cecil; -using Mono.Cecil.Cil; - -namespace MongoIncUpdate.Fody; - -public class CallMapper -{ - public void MapCallsToVirtual(IEnumerable members, ModuleDefinition moduleDefinition) - { - foreach (var typeDefinition in moduleDefinition.GetTypes()) - { - if (typeDefinition.IsAbstract || typeDefinition.IsEnum) continue; - - foreach (var methodDefinition in typeDefinition.Methods) - if (methodDefinition.HasBody) - ReplaceCallsTo(methodDefinition, members); - } - } - - private static void ReplaceCallsTo(MethodDefinition methodDefinition, IEnumerable members) - { - foreach (var instruction in methodDefinition.Body.Instructions) - { - if (instruction.OpCode != OpCodes.Call) continue; - - if (members.Any(member => member == instruction.Operand)) instruction.OpCode = OpCodes.Callvirt; - } - } -} \ No newline at end of file diff --git a/MongoIncUpdate.Fody/CecilExtensions.cs b/MongoIncUpdate.Fody/CecilExtensions.cs index 7a9081e..01ff28f 100644 --- a/MongoIncUpdate.Fody/CecilExtensions.cs +++ b/MongoIncUpdate.Fody/CecilExtensions.cs @@ -6,51 +6,23 @@ namespace MongoIncUpdate.Fody; public static class CecilExtensions { - public static string GetName(this PropertyDefinition propertyDefinition) - { - return $"{propertyDefinition.DeclaringType.FullName}.{propertyDefinition.Name}"; - } - - public static bool IsCallToBaseMethod(this Instruction instruction, MethodDefinition method) - { - return instruction.OpCode == OpCodes.Call && instruction.IsCallToMethod(method); - } - - public static bool IsCallToMethod(this Instruction instruction, MethodDefinition method) + public static AssemblyNameReference? FindAssembly(this ModuleDefinition module, string name) { - if (!instruction.OpCode.IsCall()) return false; - - if (!(instruction.Operand is MethodReference methodReference)) return false; - - if (methodReference.Name != method.Name) return false; - - if (methodReference.Resolve() != method) return false; - - return true; + return module.AssemblyReferences.Where(x => x.Name == name).MaxBy(x => x.Version); } - public static bool IsCallToMethod(this Instruction instruction, string methodName, out int propertyNameIndex) + public static TypeReference FindType(this ModuleDefinition currentModule, string @namespace, string typeName, + IMetadataScope? scope = null, params string[] typeParameters) { - propertyNameIndex = 1; - if (!instruction.OpCode.IsCall()) return false; - - if (!(instruction.Operand is MethodReference methodReference)) return false; - - if (methodReference.Name != methodName) return false; - - var parameterDefinition = methodReference.Parameters.FirstOrDefault(x => x.Name == "propertyName"); - if (parameterDefinition != null) - propertyNameIndex = methodReference.Parameters.Count - parameterDefinition.Index; - - return true; - } + var result = new TypeReference(@namespace, typeName, currentModule, scope); + foreach (var typeParameter in typeParameters) + { + result.GenericParameters.Add(new GenericParameter(typeParameter, result)); + } - public static bool IsCall(this OpCode opCode) - { - return opCode.Code == Code.Call || - opCode.Code == Code.Callvirt; + return currentModule.ImportReference(result); } - + public static TypeReference GetGeneric(this TypeReference reference) { if (!reference.HasGenericParameters) return reference; @@ -106,86 +78,5 @@ public static MethodReference MakeGeneric(this MethodReference self, params Type return reference; } - public static IEnumerable GetAllCustomAttributes(this TypeDefinition typeDefinition) - { - foreach (var attribute in typeDefinition.CustomAttributes) yield return attribute; - - if (!(typeDefinition.BaseType is TypeDefinition baseDefinition)) yield break; - - foreach (var attribute in baseDefinition.GetAllCustomAttributes()) yield return attribute; - } - - public static IEnumerable GetAttributes(this IEnumerable attributes, - string attributeName) - { - return attributes.Where(attribute => attribute.Constructor.DeclaringType.FullName == attributeName); - } - - public static CustomAttribute? GetAttribute(this IEnumerable attributes, string attributeName) - { - return attributes.FirstOrDefault(attribute => attribute.Constructor.DeclaringType.FullName == attributeName); - } - - public static bool ContainsAttribute(this IEnumerable attributes, string attributeName) - { - return attributes.Any(attribute => attribute.Constructor.DeclaringType.FullName == attributeName); - } - - public static IEnumerable GetAllInterfaces(this TypeDefinition? type) - { - while (type != null) - { - if (type.HasInterfaces) - foreach (var face in type.Interfaces) - yield return face.InterfaceType; - - type = type.BaseType?.Resolve(); - } - } - - public static bool GetBaseMethod(this MethodDefinition method, out MethodDefinition baseMethod) - { - baseMethod = method.GetBaseMethod(); - return baseMethod != null - && baseMethod != method; // cecil's GetBaseMethod() returns self if the method has no base method... - } - - public static IEnumerable GetSelfAndBaseMethods(this MethodDefinition method) - { - yield return method; - - while (method.GetBaseMethod(out method)) yield return method; - } - - public static OpCode GetCallOpCode(this TypeReference type) - { - return type.IsValueType ? OpCodes.Call : OpCodes.Callvirt; - } - - public static void AddConditionalBoxInstructions(this ICollection instructions, TypeReference type) - { - if (type.IsValueType) - { - var genericType = type.GetGeneric(); - instructions.Add(Instruction.Create(OpCodes.Ldobj, genericType)); - instructions.Add(Instruction.Create(OpCodes.Box, genericType)); - } - } - public static AssemblyNameReference? FindAssembly(this ModuleDefinition module, string name) - { - return module.AssemblyReferences.Where(x => x.Name == name).MaxBy(x => x.Version); - } - - public static TypeReference FindType(this ModuleDefinition currentModule, string @namespace, string typeName, - IMetadataScope? scope = null, params string[] typeParameters) - { - var result = new TypeReference(@namespace, typeName, currentModule, scope); - foreach (var typeParameter in typeParameters) - { - result.GenericParameters.Add(new GenericParameter(typeParameter, result)); - } - - return currentModule.ImportReference(result); - } } \ No newline at end of file diff --git a/MongoIncUpdate.Fody/InjectOverrideProperty.cs b/MongoIncUpdate.Fody/InjectOverrideProperty.cs index db8f4be..1e8d0bd 100644 --- a/MongoIncUpdate.Fody/InjectOverrideProperty.cs +++ b/MongoIncUpdate.Fody/InjectOverrideProperty.cs @@ -8,7 +8,7 @@ public partial class ModuleWeaver public void InjectOverrideProperty(TypeDefinition typ, string propName, bool isStatic = false) { //找到基类Prop - var baseProp = _typeSelector.SelectPropFromType(MongoIncUpdateInterface, $"{propName}"); + var baseProp = _typeSelector.SelectPropFromType(_mongoIncUpdateInterface, $"{propName}"); //创建field//字段 var fieldDef = new FieldDefinition($"_{propName.FirstCharToLowerCase()}", isStatic ? FieldAttributes.Static | FieldAttributes.Private : FieldAttributes.Private, // @@ -17,7 +17,7 @@ public void InjectOverrideProperty(TypeDefinition typ, string propName, bool isS //插入getter var baseGetPropMethodDef = - _typeSelector.SelectMethodFromType(ModuleDefinition, MongoIncUpdateInterface, $"get_{propName}"); + _typeSelector.SelectMethodFromType(ModuleDefinition, _mongoIncUpdateInterface, $"get_{propName}"); var getPropMethodDef = new MethodDefinition( $"{baseGetPropMethodDef.DeclaringType.FullName}.{baseGetPropMethodDef.Name}", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.NewSlot | @@ -42,7 +42,7 @@ public void InjectOverrideProperty(TypeDefinition typ, string propName, bool isS //插入setter var baseSetPropMethodDef = - _typeSelector.SelectMethodFromType(ModuleDefinition, MongoIncUpdateInterface, $"set_{propName}"); + _typeSelector.SelectMethodFromType(ModuleDefinition, _mongoIncUpdateInterface, $"set_{propName}"); var setPropMethodDef = new MethodDefinition( $"{baseSetPropMethodDef.DeclaringType.FullName}.{baseSetPropMethodDef.Name}", MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.NewSlot | @@ -71,7 +71,7 @@ public void InjectOverrideProperty(TypeDefinition typ, string propName, bool isS typ.Methods.Add(setPropMethodDef); //Prop重载 - var prop = new PropertyDefinition($"{MongoIncUpdateInterface}.{baseProp.Name}", + var prop = new PropertyDefinition($"{_mongoIncUpdateInterface}.{baseProp.Name}", baseProp.Attributes, typ); typ.Properties.Add(prop); diff --git a/MongoIncUpdate.Fody/InjectSetterPropChange.cs b/MongoIncUpdate.Fody/InjectSetterPropChange.cs index 47c0c3f..7195dae 100644 --- a/MongoIncUpdate.Fody/InjectSetterPropChange.cs +++ b/MongoIncUpdate.Fody/InjectSetterPropChange.cs @@ -5,9 +5,9 @@ namespace MongoIncUpdate.Fody; public partial class ModuleWeaver { - public void InjectPropSetterPropChangeNotify(TypeDefinition type) + private void InjectPropSetterPropChangeNotify(TypeDefinition type) { - var propChangeMethod = _typeSelector.SelectMethodFromType(ModuleDefinition, MongoIncUpdateInterface, "PropChange"); + var propChangeMethod = _typeSelector.SelectMethodFromType(ModuleDefinition, _mongoIncUpdateInterface, "PropChange"); foreach (var prop in type.Properties) { var setter = prop.SetMethod; diff --git a/MongoIncUpdate.Fody/MemberSelector.cs b/MongoIncUpdate.Fody/MemberSelector.cs deleted file mode 100644 index e232c1b..0000000 --- a/MongoIncUpdate.Fody/MemberSelector.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Mono.Cecil; - -namespace MongoIncUpdate.Fody; - -public class MemberSelector -{ - public IEnumerable Select(TypeDefinition type) - { - var membersToProcess = new List(); - foreach (var member in type.Methods) - if (member.IsPublic - && !member.IsStatic - && !member.IsConstructor - && !member.IsVirtual) - membersToProcess.Add(member); - - return membersToProcess; - } -} \ No newline at end of file diff --git a/MongoIncUpdate.Fody/MemberVirtualizer.cs b/MongoIncUpdate.Fody/MemberVirtualizer.cs deleted file mode 100644 index 0baad44..0000000 --- a/MongoIncUpdate.Fody/MemberVirtualizer.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Mono.Cecil; - -namespace MongoIncUpdate.Fody; - -public class MemberVirtualizer -{ - public void Virtualize(IEnumerable members) - { - foreach (var member in members) - { - member.IsVirtual = true; - member.IsNewSlot = true; - } - } -} \ No newline at end of file diff --git a/MongoIncUpdate.Fody/ModuleWeaver.cs b/MongoIncUpdate.Fody/ModuleWeaver.cs index 51df1fa..9d9a9e2 100644 --- a/MongoIncUpdate.Fody/ModuleWeaver.cs +++ b/MongoIncUpdate.Fody/ModuleWeaver.cs @@ -1,4 +1,5 @@ -using Fody; +using System.Diagnostics; +using Fody; using Mono.Cecil; using Mono.Cecil.Cil; @@ -6,28 +7,21 @@ namespace MongoIncUpdate.Fody; public partial class ModuleWeaver : BaseModuleWeaver { - private readonly CallMapper _callMapper; - - private readonly MemberSelector _memberSelector; - private readonly MemberVirtualizer _memberVirtualizer; private readonly TypeSelector _typeSelector; + private readonly HashSet _typeHasInjectSetter = new(); public ModuleWeaver() - : this(new TypeSelector(), new MemberSelector(), new MemberVirtualizer(), new CallMapper()) + : this(new TypeSelector()) { // Debugger.Launch(); } +#pragma warning disable CS8618 public ModuleWeaver( - TypeSelector typeSelector, - MemberSelector memberSelector, - MemberVirtualizer memberVirtualizer, - CallMapper callMapper) +#pragma warning restore CS8618 + TypeSelector typeSelector) { _typeSelector = typeSelector; - _memberSelector = memberSelector; - _memberVirtualizer = memberVirtualizer; - _callMapper = callMapper; } public override void Execute() @@ -43,35 +37,63 @@ public override void Execute() foreach (var typ in selectedTypes) { - //继承这个接口 - typ.Interfaces.Add(new InterfaceImplementation(MongoIncUpdateInterface)); - - //插入init函数调用 - //init方法 - var initMethodDef = _typeSelector.SelectMethodFromType(ModuleDefinition, MongoIncUpdateInterface, "Init"); - var ctors = typ.Methods.Where(m => m.IsConstructor).ToList(); - foreach (var ctor in ctors) - { - var last = ctor.Body.Instructions.Count - 1; //插入到ret之前的位置 - ctor.Body.Instructions.Insert(last, - Instruction.Create(OpCodes.Ldarg_0), - Instruction.Create(OpCodes.Callvirt, initMethodDef), - Instruction.Create(OpCodes.Nop)); - } - - //一定要先做 - //注入属性变化监听(警告:一定不要调换顺序,一定要先注入监听,因为这里有属性遍历,避免属性被后面污染了) - InjectPropSetterPropChangeNotify(typ); - // - //再后做------因为注入属性会影响前面的注入监听 - //脏标记 - InjectOverrideProperty(typ, "Dirties"); - //Init - InjectOverrideProperty(typ, "IsOnceInitDone", true); - //NameMapping - InjectOverrideProperty(typ, "NameMapping", true); - //IdxMapping - InjectOverrideProperty(typ, "IdxMapping", true); + InjectTargetType(typ); + } + } + + //被标记的类为最终类,需要全量注入 + private void InjectTargetType(TypeDefinition typ) + { + //继承这个接口 + typ.Interfaces.Add(new InterfaceImplementation(_mongoIncUpdateInterface)); + + //插入init函数调用 + //init方法 + var initMethodDef = _typeSelector.SelectMethodFromType(ModuleDefinition, _mongoIncUpdateInterface, "Init"); + var ctors = typ.Methods.Where(m => m.IsConstructor).ToList(); + foreach (var ctor in ctors) + { + var last = ctor.Body.Instructions.Count - 1; //插入到ret之前的位置 + ctor.Body.Instructions.Insert(last, + Instruction.Create(OpCodes.Ldarg_0), + Instruction.Create(OpCodes.Callvirt, initMethodDef), + Instruction.Create(OpCodes.Nop)); + } + + //一定要先做 + //注入属性变化监听(警告:一定不要调换顺序,一定要先注入监听,因为这里有属性遍历,避免属性被后面污染了) + InjectPropSetterPropChangeNotify(typ); + // + //再后做------因为注入属性会影响前面的注入监听 + //脏标记 + InjectOverrideProperty(typ, "Dirties"); + //Init + InjectOverrideProperty(typ, "IsOnceInitDone", true); + //NameMapping + InjectOverrideProperty(typ, "NameMapping", true); + //IdxMapping + InjectOverrideProperty(typ, "IdxMapping", true); + + //递归注入BaseClass + if (typ.BaseType != null) + { + InjectBaseType(typ.BaseType.Resolve()); + } + } + + //基类只需要注入setter即可 + private void InjectBaseType(TypeDefinition typ) + { + //已经注入过的跳过 + if (_typeHasInjectSetter.Contains(typ.FullName)) return; + _typeHasInjectSetter.Add(typ.FullName); + + InjectPropSetterPropChangeNotify(typ); + + //递归注入BaseClass + if (typ.BaseType != null) + { + InjectBaseType(typ.BaseType.Resolve()); } } diff --git a/MongoIncUpdate.Fody/TypeDefine.cs b/MongoIncUpdate.Fody/TypeDefine.cs index a64725e..1d91d4c 100644 --- a/MongoIncUpdate.Fody/TypeDefine.cs +++ b/MongoIncUpdate.Fody/TypeDefine.cs @@ -5,11 +5,11 @@ namespace MongoIncUpdate.Fody; public partial class ModuleWeaver { - public TypeReference MongoIncUpdateInterface; + private TypeReference _mongoIncUpdateInterface; - public void FindCoreReferences() + private void FindCoreReferences() { //Mongo - MongoIncUpdateInterface = _typeSelector.SelectMongoIncUpdateInterface(ModuleDefinition); + _mongoIncUpdateInterface = _typeSelector.SelectMongoIncUpdateInterface(ModuleDefinition); } } \ No newline at end of file diff --git a/MongoIncUpdate.Fody/TypeSelector.cs b/MongoIncUpdate.Fody/TypeSelector.cs index f912ed6..8ab0125 100644 --- a/MongoIncUpdate.Fody/TypeSelector.cs +++ b/MongoIncUpdate.Fody/TypeSelector.cs @@ -10,16 +10,27 @@ public void CheckTypeLegal(IEnumerable types) { foreach (var typ in types) { - if (!IsContainer(typ)) - throw new WeavingException($"{typ.Name} member must only property. [means:method only getter/setter"); + // if (!IsContainer(typ)) + // throw new WeavingException($"{typ.Name} member must only property. [means:method only getter/setter"); if (HasPublicFiled(typ)) { var v = typ.Fields.Where(v => v.IsPublic).Select(v => v.Name).ToList(); throw new WeavingException( $"{typ.Name} member must only property. [means: no public filed:{v[0]}]"); } - if (!CanVirtualize(typ)) - throw new WeavingException($"{typ.Name} must only public seal class. [means: public seal class]"); + + if (!typ.IsClass) + { + throw new WeavingException($"{typ.Name} type must: class"); + } + if (!typ.IsPublic) + { + throw new WeavingException($"{typ.Name} type must: public"); + } + if (!typ.IsSealed) + { + throw new WeavingException($"{typ.Name} type must: sealed"); + } } } @@ -82,34 +93,11 @@ public PropertyDefinition SelectPropFromType(TypeReference typeReference, string return propToProcess[0]; } - private static bool CanVirtualize(TypeDefinition type) - { - return IsPublicClass(type) - && IsExtensible(type); - } - private static bool HasMongoIncUpdateAttribute(TypeDefinition type) { return type.CustomAttributes.Any(_ => _.AttributeType.Name == "MongoIncUpdateAttribute"); } - private static bool HasMongoIncUpdateInterfaceAttribute(TypeDefinition type) - { - return type.CustomAttributes.Any(_ => _.AttributeType.Name == "MongoIncUpdateInterfaceAttribute"); - } - - private static bool IsPublicClass(TypeDefinition type) - { - return type.IsPublic - && type.IsClass - && !type.IsNested; - } - - private static bool IsExtensible(TypeDefinition type) - { - return !type.IsSealed; - } - public static bool IsContainer(TypeDefinition type) { return type.Methods.All(_ => _.IsGetter || _.IsSetter || _.IsConstructor); @@ -119,9 +107,4 @@ public static bool HasPublicFiled(TypeDefinition type) { return type.Fields.Any(t => t.IsPublic); } - - private static bool ImplementsInterfaces(TypeDefinition type) - { - return type.HasInterfaces; - } } \ No newline at end of file