Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion MapDataReader.Benchmarks/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public static void Setup()
}
}

[GenerateDataReaderMapper]
[GenerateDataReaderMapper(AccessModifier = "internal")]
public class TestClass
{
public string String1 { get; set; }
Expand Down
33 changes: 32 additions & 1 deletion MapDataReader.Tests/TestActualCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Data;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

Expand Down Expand Up @@ -147,7 +148,7 @@ public void TestStringAssign()
}

[TestMethod]
public void TestDatatReader()
public void TestDataReader()
{
//create datatable with test data
var dt = new DataTable();
Expand Down Expand Up @@ -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
Expand All @@ -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; }
}
}

23 changes: 23 additions & 0 deletions MapDataReader.Tests/TestGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
55 changes: 53 additions & 2 deletions MapDataReader/MapperGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -28,6 +39,8 @@ public void Execute(GeneratorExecutionContext context)

var allProperties = typeNodeSymbol.GetAllSettableProperties();

var accessModifier = GetAccessModifer(typeNode);

var src = $@"
// <auto-generated/>
#pragma warning disable 8019 //disable 'unnecessary using directive' warning
Expand All @@ -40,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);
}}
Expand Down Expand Up @@ -79,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()}>();

Expand Down Expand Up @@ -119,6 +132,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
Expand Down
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down