From ab4f9279969da0ceff28ba5c86bc9644d1a6f28a Mon Sep 17 00:00:00 2001 From: Amy Date: Wed, 2 Oct 2024 15:59:19 +0100 Subject: [PATCH 1/6] feat: added AccessModifier property to the GenerateDataReaderMapper attribute --- MapDataReader/MapperGenerator.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/MapDataReader/MapperGenerator.cs b/MapDataReader/MapperGenerator.cs index 07e3b91..ad16e52 100644 --- a/MapDataReader/MapperGenerator.cs +++ b/MapDataReader/MapperGenerator.cs @@ -11,6 +11,17 @@ namespace MapDataReader [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class GenerateDataReaderMapperAttribute : Attribute { + public string AccessModifier { get; set; } + + public GenerateDataReaderMapperAttribute() + { + AccessModifier = "public"; + } + + public GenerateDataReaderMapperAttribute(string access = "public") + { + AccessModifier = access; + } } [Generator] From 8dab45155f8e4dc88b025b4e002a74cf56ad9c71 Mon Sep 17 00:00:00 2001 From: Amy Date: Wed, 2 Oct 2024 15:59:33 +0100 Subject: [PATCH 2/6] feat: added method to get the access modifier value --- MapDataReader/MapperGenerator.cs | 38 ++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/MapDataReader/MapperGenerator.cs b/MapDataReader/MapperGenerator.cs index ad16e52..1c72fcd 100644 --- a/MapDataReader/MapperGenerator.cs +++ b/MapDataReader/MapperGenerator.cs @@ -130,6 +130,44 @@ public void Initialize(GeneratorInitializationContext context) { context.RegisterForSyntaxNotifications(() => new TargetTypeTracker()); } + + private string GetAccessModifer(ClassDeclarationSyntax typeNode) + { + // Retrieve the attribute list + var attributeList = typeNode.AttributeLists + .SelectMany(al => al.Attributes) + .FirstOrDefault(attr => attr.Name.ToString() == "GenerateDataReaderMapper"); + + if (attributeList?.ArgumentList == null) + return "public"; + + var arguments = attributeList.ArgumentList.Arguments; + + if (arguments.Count == 0) + return "public"; + + if (arguments.Count == 1) + { + var argumentExpr = arguments[0].Expression as LiteralExpressionSyntax; + return argumentExpr?.Token.ValueText ?? "public"; + } + + foreach (var argument in arguments) + { + // Check if the argument is a named argument + if (argument is AttributeArgumentSyntax attributeArgument) + { + var nameEquals = attributeArgument.NameEquals; + if (nameEquals?.Name.Identifier.Text == "AccessModifier") + { + var argumentExpr = argument.Expression as LiteralExpressionSyntax; + return argumentExpr?.Token.ValueText ?? "public"; + } + } + } + + return "public"; + } } internal class TargetTypeTracker : ISyntaxContextReceiver From 7d7e83b589cbf1916091abce431018f78f2201b7 Mon Sep 17 00:00:00 2001 From: Amy Date: Wed, 2 Oct 2024 15:59:47 +0100 Subject: [PATCH 3/6] feat: using the access modifier for SetPropertyByName and To methods --- MapDataReader/MapperGenerator.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/MapDataReader/MapperGenerator.cs b/MapDataReader/MapperGenerator.cs index 1c72fcd..6863dee 100644 --- a/MapDataReader/MapperGenerator.cs +++ b/MapDataReader/MapperGenerator.cs @@ -39,6 +39,8 @@ public void Execute(GeneratorExecutionContext context) var allProperties = typeNodeSymbol.GetAllSettableProperties(); + var accessModifier = GetAccessModifer(typeNode); + var src = $@" // #pragma warning disable 8019 //disable 'unnecessary using directive' warning @@ -51,7 +53,7 @@ namespace MapDataReader {{ public static partial class MapperExtensions {{ - public static void SetPropertyByName(this {typeNodeSymbol.FullName()} target, string name, object value) + {accessModifier} static void SetPropertyByName(this {typeNodeSymbol.FullName()} target, string name, object value) {{ SetPropertyByUpperName(target, name.ToUpperInvariant(), value); }} @@ -90,7 +92,7 @@ private static void SetPropertyByUpperName(this {typeNodeSymbol.FullName()} targ { src += $@" - public static List<{typeNodeSymbol.FullName()}> To{typeNode.Identifier}(this IDataReader dr) + {accessModifier} static List<{typeNodeSymbol.FullName()}> To{typeNode.Identifier}(this IDataReader dr) {{ var list = new List<{typeNodeSymbol.FullName()}>(); From 238644154548142d7ad81621f9887406b292bad8 Mon Sep 17 00:00:00 2001 From: Amy Date: Wed, 2 Oct 2024 16:00:09 +0100 Subject: [PATCH 4/6] test: added tests for the new access modifer Tests added to test the generation and usage --- MapDataReader.Benchmarks/Program.cs | 2 +- MapDataReader.Tests/TestActualCode.cs | 31 +++++++++++++++++++++++++++ MapDataReader.Tests/TestGenerator.cs | 23 ++++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/MapDataReader.Benchmarks/Program.cs b/MapDataReader.Benchmarks/Program.cs index d402e0c..bf3afe3 100644 --- a/MapDataReader.Benchmarks/Program.cs +++ b/MapDataReader.Benchmarks/Program.cs @@ -93,7 +93,7 @@ public static void Setup() } } - [GenerateDataReaderMapper] + [GenerateDataReaderMapper(AccessModifier = "internal")] public class TestClass { public string String1 { get; set; } diff --git a/MapDataReader.Tests/TestActualCode.cs b/MapDataReader.Tests/TestActualCode.cs index 76ccaca..19a2054 100644 --- a/MapDataReader.Tests/TestActualCode.cs +++ b/MapDataReader.Tests/TestActualCode.cs @@ -3,6 +3,7 @@ using System.Data; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Reflection; using System.Text; using System.Threading.Tasks; @@ -227,6 +228,24 @@ public void TestWrongProperty() o.SetPropertyByName("Name", 123); //try to assign string prop to int Assert.IsTrue(o.Name == null); //wrong type. should be null } + + [TestMethod] + public void TestInternalAccessModifier() + { + var type = typeof(MapperExtensions); + var method = type.GetMethod("ToTestClassInternal", BindingFlags.Static | BindingFlags.NonPublic); + + Assert.IsNotNull(method, "Expected method 'ToTestClassInternal' to be 'internal'."); + } + + [TestMethod] + public void TestInternalAccessModifierNamed() + { + var type = typeof(MapperExtensions); + var method = type.GetMethod("ToTestClassInternalNamed", BindingFlags.Static | BindingFlags.NonPublic); + + Assert.IsNotNull(method, "Expected method 'ToTestClassInternalNamed' to be 'internal'."); + } } public class BaseClass @@ -239,5 +258,17 @@ public class ChildClass : BaseClass { public string Name { get; set; } } + + [GenerateDataReaderMapper("internal")] + internal class TestClassInternal + { + public int Id { get; set; } + } + + [GenerateDataReaderMapper(AccessModifier = "internal")] + internal class TestClassInternalNamed + { + public int Id { get; set; } + } } diff --git a/MapDataReader.Tests/TestGenerator.cs b/MapDataReader.Tests/TestGenerator.cs index 083790b..8ae70aa 100644 --- a/MapDataReader.Tests/TestGenerator.cs +++ b/MapDataReader.Tests/TestGenerator.cs @@ -29,6 +29,29 @@ public class MyClass public decimal Price {get;set;} } } +"; + var src = GetAndCheckOutputSource(userSource); + } + + [TestMethod] + public void TestAccessModifier() + { + string userSource = @" +using MapDataReader; + +namespace MyCode +{ + [GenerateDataReaderMapper(AccessModifier = ""internal"")] + public class MyClass + { + public string Name {get;set;} + public int Size {get;set;} + public bool Enabled {get;set;} + public System.DateTime Created {get;set;} + public System.DateTimeOffset Offset {get;set;} + public decimal Price {get;set;} + } +} "; var src = GetAndCheckOutputSource(userSource); } From acee9ce4b2984c61087ed94830698ad3da9205e9 Mon Sep 17 00:00:00 2001 From: Amy Date: Wed, 2 Oct 2024 16:00:53 +0100 Subject: [PATCH 5/6] refactor: fixed typo --- MapDataReader.Tests/TestActualCode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MapDataReader.Tests/TestActualCode.cs b/MapDataReader.Tests/TestActualCode.cs index 19a2054..2c71391 100644 --- a/MapDataReader.Tests/TestActualCode.cs +++ b/MapDataReader.Tests/TestActualCode.cs @@ -148,7 +148,7 @@ public void TestStringAssign() } [TestMethod] - public void TestDatatReader() + public void TestDataReader() { //create datatable with test data var dt = new DataTable(); From 42b5809c7b5d5a71c57f2d71de606824fc50604b Mon Sep 17 00:00:00 2001 From: Amy Date: Wed, 2 Oct 2024 17:47:02 +0100 Subject: [PATCH 6/6] docs: updated documentation with details of the new access modifier --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index ab78f76..21c89e8 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,20 @@ Some notes for the above * Properly maps `DBNull` to `null`. * Complex-type properties may not work. +### Access Modifier: `public` or `internal` + +You can now specify the access modifer to be used with the mapping methods. By default, the methods will be `public` for backwards compatability. + +For example, to prevent exposure outside your assembly you'd set it to `internal`. This would hide the mapping methods outside your model project: + +``` csharp +[GenerateDataReaderMapper("internal")] +public class MyClass +{ + public int ID { get; set; } +... +``` + ## Bonus API: `SetPropertyByName` This package also adds a super fast `SetPropertyByName` extension method generated at compile time for your class.