From 2531e8e688cb9d65a265c7b7ca1dbf2e59541579 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sun, 25 Jul 2021 15:26:50 +0200 Subject: [PATCH] AutoMapper --- src-examples/ProxyInterfaceConsumer/Person.cs | 12 +- .../ProxyInterfaceConsumer/Program.cs | 125 ++++++++++++++++++ .../ProxyInterfaceConsumer.csproj | 2 + .../Extensions/SymbolExtensions.cs | 18 ++- .../FileGenerators/BaseGenerator.cs | 37 ++++++ .../PartialInterfacesGenerator.cs | 23 ++-- .../FileGenerators/ProxyClassesGenerator.cs | 69 +++++++++- .../ProxyInterfaceCodeGenerator.cs | 73 +--------- .../ProxyInterfaceSourceGenerator.csproj | 1 + 9 files changed, 264 insertions(+), 96 deletions(-) diff --git a/src-examples/ProxyInterfaceConsumer/Person.cs b/src-examples/ProxyInterfaceConsumer/Person.cs index 6ba0be7..6fcedad 100644 --- a/src-examples/ProxyInterfaceConsumer/Person.cs +++ b/src-examples/ProxyInterfaceConsumer/Person.cs @@ -1,10 +1,10 @@ -using Microsoft.CodeAnalysis; +using System.Collections.Generic; +using Microsoft.CodeAnalysis; namespace ProxyInterfaceConsumer { public class Person { - private int PrivateId { get; } public int Id { get; } @@ -14,6 +14,10 @@ namespace ProxyInterfaceConsumer public Address Address { get; set; } + public List
AddressesLIst { get; set; } + + public Dictionary AddressesDict { get; set; } + public E E { get; set; } public int Add(string s) @@ -28,11 +32,9 @@ namespace ProxyInterfaceConsumer public Compilation Compilation { get; set; } - public INamedTypeSymbol MyNamedTypeSymbol { get;set; } + public INamedTypeSymbol MyNamedTypeSymbol { get; set; } } - - public enum E { V1, diff --git a/src-examples/ProxyInterfaceConsumer/Program.cs b/src-examples/ProxyInterfaceConsumer/Program.cs index 78effc7..f5beef9 100644 --- a/src-examples/ProxyInterfaceConsumer/Program.cs +++ b/src-examples/ProxyInterfaceConsumer/Program.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using AutoMapper; namespace ProxyInterfaceConsumer { @@ -6,6 +8,25 @@ namespace ProxyInterfaceConsumer { public static void Main() { + + + + var c = new Clazz + { + Name = "n" + }; + var cp = new ClazzProxy(c); + + var t = new Test(); + t.Cs = new List { c }; + + var tp = new TestProxy(t); + tp.Cs = new List { cp }; + + Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(t)); + Console.WriteLine(new string('-', 80)); + + IPerson p = new PersonProxy(new Person()); p.Name = "test"; p.Address = new AddressProxy(new Address { HouseNumber = 42 }); @@ -17,4 +38,108 @@ namespace ProxyInterfaceConsumer Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(p)); } } + + public struct Test + { + public int Id { get; set; } + + public Clazz C { get; } + + public IList Cs { get; set; } + + public int Add(string s) + { + return 600; + } + } + + public sealed class Clazz + { + public string Name { get; set; } + } + + public interface ITest + { + int Id { get; set; } + + IClazz C { get; } + + IList Cs { get; set; } + + int Add(string s); + } + + public interface IClazz + { + string Name { get; set; } + } + + public class TestProxy : ITest + { + private Test _instance; + + private IClazz _clazz; + + private readonly IMapper _mapper; + + public TestProxy(Test instance) + { + _instance = instance; + + // _clazz = new ClazzProxy(_instance.C); + + _mapper = new MapperConfiguration(cfg => + { + //cfg.CreateMap(); + //cfg.CreateMap(); + + cfg.CreateMap(); + cfg.CreateMap(); + }).CreateMapper(); + } + + public int Id + { + get => _instance.Id; + set => _instance.Id = value; + } + + public IClazz C => _clazz; + + public IList Cs2 + { + get + { + //return null; // TinyMapper.Map>(_instance.Cs); //(IList)_instance.Cs.Select(x => new ClazzProxy(x)); + return _mapper.Map>(_instance.Cs); //(IList)_instance.Cs.Select(x => new ClazzProxy(x)); + } + + set + { + _instance.Cs = _mapper.Map>(value); + //_instance.Cs = TinyMapper.Map>(value); + } + } + + public IList Cs + { + get => _mapper.Map>(_instance.Cs); + + set => _instance.Cs = _mapper.Map>(value); + } + + public int Add(string s) => _instance.Add(s); + } + + public class ClazzProxy : IClazz + { + private Clazz _instance; + + public ClazzProxy(Clazz instance) + { + _instance = instance; + } + + public string Name { get => _instance.Name; set => _instance.Name = value; } + } } diff --git a/src-examples/ProxyInterfaceConsumer/ProxyInterfaceConsumer.csproj b/src-examples/ProxyInterfaceConsumer/ProxyInterfaceConsumer.csproj index c914ec4..46d5224 100644 --- a/src-examples/ProxyInterfaceConsumer/ProxyInterfaceConsumer.csproj +++ b/src-examples/ProxyInterfaceConsumer/ProxyInterfaceConsumer.csproj @@ -6,12 +6,14 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/ProxyInterfaceSourceGenerator/Extensions/SymbolExtensions.cs b/src/ProxyInterfaceSourceGenerator/Extensions/SymbolExtensions.cs index cea342e..87fdfe3 100644 --- a/src/ProxyInterfaceSourceGenerator/Extensions/SymbolExtensions.cs +++ b/src/ProxyInterfaceSourceGenerator/Extensions/SymbolExtensions.cs @@ -39,15 +39,23 @@ namespace ProxyInterfaceSourceGenerator.Extensions return $"{property.Type} {property.Name} {{ {get}{set}}}"; } - public static string ToPropertyTextForClass(this IPropertySymbol property, string interfaceName, string className) + public static string ToPropertyTextForClass(this IPropertySymbol property, string overrideType) { - var classNameProxy = $"{className}Proxy"; - var get = property.GetMethod != null ? $"get => new {classNameProxy}(_Instance.{property.Name}); " : string.Empty; - var set = property.SetMethod != null ? $"set => _Instance.{property.Name} = (({classNameProxy}) value)._Instance; " : string.Empty; + var get = property.GetMethod != null ? $"get => _mapper.Map<{overrideType}>(_Instance.{property.Name}); " : string.Empty; + var set = property.SetMethod != null ? $"set => _Instance.{property.Name} = _mapper.Map<{property.Type}>(value); " : string.Empty; - return $"{interfaceName} {property.Name} {{ {get}{set}}}"; + return $"{overrideType} {property.Name} {{ {get}{set}}}"; } + //public static string ToPropertyTextForClass(this IPropertySymbol property, string overrideType, string className) + //{ + // var classNameProxy = $"{className}Proxy"; + // var get = property.GetMethod != null ? $"get => new {classNameProxy}(_Instance.{property.Name}); " : string.Empty; + // var set = property.SetMethod != null ? $"set => _Instance.{property.Name} = (({classNameProxy}) value)._Instance; " : string.Empty; + + // return $"{overrideType} {property.Name} {{ {get}{set}}}"; + //} + public static string ToMethodText(this IMethodSymbol method) { var parameters = new List(); diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/BaseGenerator.cs b/src/ProxyInterfaceSourceGenerator/FileGenerators/BaseGenerator.cs index 9b80fb4..a599a2f 100644 --- a/src/ProxyInterfaceSourceGenerator/FileGenerators/BaseGenerator.cs +++ b/src/ProxyInterfaceSourceGenerator/FileGenerators/BaseGenerator.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using Microsoft.CodeAnalysis; namespace ProxyInterfaceSourceGenerator.FileGenerators @@ -12,6 +14,41 @@ namespace ProxyInterfaceSourceGenerator.FileGenerators _context = context; } + protected string GetPropertyType(IPropertySymbol property, out Dictionary differs) + { + differs = new Dictionary(); + + var existing = _context.CandidateInterfaces.Values.FirstOrDefault(x => x.TypeName == property.Type.ToString()); + if (existing is not null) + { + differs.Add(property.Type.ToString(), existing.InterfaceName); + return existing.InterfaceName; + } + + if (property.Type is INamedTypeSymbol namedTypedSymbol) + { + var type = property.Type.ToString(); + foreach (var typeArgument in namedTypedSymbol.TypeArguments) + { + var exist = _context.CandidateInterfaces.Values.FirstOrDefault(x => x.TypeName == typeArgument.ToString()); + + if (exist is not null) + { + if (!differs.ContainsKey(typeArgument.ToString())) + { + differs.Add(typeArgument.ToString(), exist.InterfaceName); + } + + type = type.Replace(typeArgument.ToString(), exist.InterfaceName); + } + } + + return type; + } + + return property.Type.ToString(); + } + protected INamedTypeSymbol GetType(string name) { var symbol = _context.GeneratorExecutionContext.Compilation.GetTypeByMetadataName(name); diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/PartialInterfacesGenerator.cs b/src/ProxyInterfaceSourceGenerator/FileGenerators/PartialInterfacesGenerator.cs index 2e83792..ea292d1 100644 --- a/src/ProxyInterfaceSourceGenerator/FileGenerators/PartialInterfacesGenerator.cs +++ b/src/ProxyInterfaceSourceGenerator/FileGenerators/PartialInterfacesGenerator.cs @@ -94,16 +94,19 @@ namespace {symbol.ContainingNamespace} //} //else - var existing = _context.CandidateInterfaces.Values.FirstOrDefault(x => x.TypeName == property.Type.ToString()); - if (existing is not null) - { - str.AppendLine($" {property.ToPropertyText(existing.InterfaceName)}"); - } - else - { - str.AppendLine($" {property.ToPropertyText()}"); - } - + var type = GetPropertyType(property, out _); + str.AppendLine($" {property.ToPropertyText(type)}"); + + //var existing = _context.CandidateInterfaces.Values.FirstOrDefault(x => x.TypeName == property.Type.ToString()); + //if (existing is not null) + //{ + // str.AppendLine($" {property.ToPropertyText(existing.InterfaceName)}"); + //} + //else + //{ + // str.AppendLine($" {property.ToPropertyText()}"); + //} + str.AppendLine(); } diff --git a/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.cs b/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.cs index a40a9d3..96c1b57 100644 --- a/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.cs +++ b/src/ProxyInterfaceSourceGenerator/FileGenerators/ProxyClassesGenerator.cs @@ -43,16 +43,21 @@ namespace ProxyInterfaceSourceGenerator.FileGenerators } private string CreateProxyClassCode(INamedTypeSymbol symbol, string interfaceName, string className, bool proxyAll) => $@"using System; +using AutoMapper; namespace {symbol.ContainingNamespace} {{ public class {className}Proxy : {interfaceName} {{ + private readonly IMapper _mapper; + public {className} _Instance {{ get; }} public {className}Proxy({className} instance) {{ _Instance = instance; + +{GenerateAutoMapper()} }} {GeneratePublicProperties(symbol, proxyAll)} @@ -61,6 +66,22 @@ namespace {symbol.ContainingNamespace} }} }}"; + private string GenerateAutoMapper() + { + var str = new StringBuilder(); + + str.AppendLine(" _mapper = new MapperConfiguration(cfg =>"); + str.AppendLine(" {"); + foreach (var x in _context.CandidateInterfaces) + { + str.AppendLine($" cfg.CreateMap<{x.Value.InterfaceName}, {x.Value.ClassName}>();"); + str.AppendLine($" cfg.CreateMap<{x.Value.ClassName}, {x.Value.InterfaceName}>();"); + } + str.AppendLine(" }).CreateMapper();"); + + return str.ToString(); + } + private string GeneratePublicProperties(INamedTypeSymbol symbol, bool proxyAll) { var str = new StringBuilder(); @@ -82,15 +103,51 @@ namespace {symbol.ContainingNamespace} // ComplexProperties foreach (var property in MemberHelper.GetPublicProperties(symbol, p => p.GetTypeEnum() == TypeEnum.Complex)) { - var existing = _context.CandidateInterfaces.Values.FirstOrDefault(x => x.TypeName == property.Type.ToString()); - if (existing is not null) - { - str.AppendLine($" public {property.ToPropertyTextForClass(existing.InterfaceName, existing.ClassName)}"); - } - else + var type = GetPropertyType(property, out var differs); + if (!differs.Any()) { str.AppendLine($" public {property.ToPropertyTextForClass()}"); } + else + { + str.AppendLine($" public {property.ToPropertyTextForClass(type)}"); + // var get = property.GetMethod != null ? $"get => _mapper.Map<{type}>(_Instance.{property.Name}); " : string.Empty; + // var set = property.SetMethod != null ? $"set => _Instance.{property.Name} = _mapper.Map<{property.Type}>(value);" : string.Empty; + //var p = $"{type} {property.Name} {{ {get}{set}}}"; + //str.AppendLine($" public {type} {property.Name} {{ {get}{set}}}"); + } + + /* + public IList Cs + { + get => _mapper.Map>(_instance.Cs); + + set => _instance.Cs = _mapper.Map>(value); + } + }*/ + + + //public static string ToPropertyTextForClass(this IPropertySymbol property, string overrideType) + //{ + // // var classNameProxy = $"Proxy"; + // var get = property.GetMethod != null ? $"get => _mapper.Map<{overrideType}>(_Instance.{property.Name}); " : string.Empty; + // var set = property.SetMethod != null ? $"set => _mapper.Map<{property.Type}>( = (({classNameProxy}) value)._Instance; " : string.Empty; + + // return $"{overrideType} {property.Name} {{ {get}{set}}}"; + //} + + + //str.AppendLine($" public {property.ToPropertyTextForClass(type)}"); + + //var existing = _context.CandidateInterfaces.Values.FirstOrDefault(x => x.TypeName == property.Type.ToString()); + //if (existing is not null) + //{ + // str.AppendLine($" public {property.ToPropertyTextForClass(existing.InterfaceName, existing.ClassName)}"); + //} + //else + //{ + // str.AppendLine($" public {property.ToPropertyTextForClass()}"); + //} str.AppendLine(); } diff --git a/src/ProxyInterfaceSourceGenerator/ProxyInterfaceCodeGenerator.cs b/src/ProxyInterfaceSourceGenerator/ProxyInterfaceCodeGenerator.cs index 088c410..e5c8f01 100644 --- a/src/ProxyInterfaceSourceGenerator/ProxyInterfaceCodeGenerator.cs +++ b/src/ProxyInterfaceSourceGenerator/ProxyInterfaceCodeGenerator.cs @@ -23,9 +23,6 @@ namespace ProxyInterfaceSourceGenerator public void Execute(GeneratorExecutionContext ctx) { - var attributeData = _proxyAttributeGenerator.GenerateFile(); - ctx.AddSource(attributeData.FileName, SourceText.From(attributeData.Text, Encoding.UTF8)); - if (ctx.SyntaxReceiver is not ProxySyntaxReceiver receiver) { return; @@ -37,6 +34,9 @@ namespace ProxyInterfaceSourceGenerator CandidateInterfaces = receiver.CandidateInterfaces }; + var attributeData = _proxyAttributeGenerator.GenerateFile(); + context.GeneratorExecutionContext.AddSource(attributeData.FileName, SourceText.From(attributeData.Text, Encoding.UTF8)); + var partialInterfacesGenerator = new PartialInterfacesGenerator(context); foreach (var data in partialInterfacesGenerator.GenerateFiles()) { @@ -50,71 +50,4 @@ namespace ProxyInterfaceSourceGenerator } } } - - public struct Test - { - public int Id { get; set; } - - public Clazz C { get; } - - public int Add(string s) - { - return 600; - } - } - - public sealed class Clazz - { - public string Name { get; set; } - } - - public interface ITest - { - int Id { get; set; } - - IClazz C { get; } - - int Add(string s); - } - - public interface IClazz - { - string Name { get; set; } - } - - public class TestMock : ITest - { - private Test _instance; - - private IClazz _clazz; - - public TestMock(Test instance) - { - _instance = instance; - - _clazz = new ClazzMock(_instance.C); - } - - public int Id - { - get => _instance.Id; - set => _instance.Id = value; - } - - public IClazz C => _clazz; - - public int Add(string s) => _instance.Add(s); - } - - public class ClazzMock : IClazz - { - private Clazz _instance; - - public ClazzMock(Clazz instance) - { - _instance = instance; - } - - public string Name { get => _instance.Name; set => _instance.Name = value; } - } } \ No newline at end of file diff --git a/src/ProxyInterfaceSourceGenerator/ProxyInterfaceSourceGenerator.csproj b/src/ProxyInterfaceSourceGenerator/ProxyInterfaceSourceGenerator.csproj index 6f0a7f0..bcac399 100644 --- a/src/ProxyInterfaceSourceGenerator/ProxyInterfaceSourceGenerator.csproj +++ b/src/ProxyInterfaceSourceGenerator/ProxyInterfaceSourceGenerator.csproj @@ -31,6 +31,7 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive