Skip to content

Commit efe3ba4

Browse files
authored
Merge pull request #1169 from mikependon/generic-attributes
Generic Attribute for ClassHandler
2 parents f6286cc + 7e65f18 commit efe3ba4

File tree

3 files changed

+119
-12
lines changed

3 files changed

+119
-12
lines changed

RepoDb.Core/RepoDb.Tests/RepoDb.UnitTests/ClassHandlers/ClassHandlerPrecedenceTest.cs

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ public void Initialize()
2424
DbSettingMapper.Add<ClassHandlerForEntityWithAttributeConnection>(new CustomDbSetting(), true);
2525
DbHelperMapper.Add<ClassHandlerForEntityWithAttributeConnection>(new CustomDbHelper(), true);
2626
StatementBuilderMapper.Add<ClassHandlerForEntityWithAttributeConnection>(new CustomStatementBuilder(), true);
27+
28+
#if NET7_0_OR_GREATER
29+
// For Generic Attributed Entity
30+
DbSettingMapper.Add<ClassHandlerForEntityWithGenericAttributeConnection>(new CustomDbSetting(), true);
31+
DbHelperMapper.Add<ClassHandlerForEntityWithGenericAttributeConnection>(new CustomDbHelper(), true);
32+
StatementBuilderMapper.Add<ClassHandlerForEntityWithGenericAttributeConnection>(new CustomStatementBuilder(), true);
33+
#endif
2734
}
2835

2936
[TestCleanup]
@@ -73,7 +80,28 @@ protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
7380
return reader;
7481
}
7582
}
83+
84+
#if NET7_0_OR_GREATER
85+
private class ClassHandlerForEntityWithGenericAttributeConnection : CustomDbConnection
86+
{
87+
protected override DbCommand CreateDbCommand()
88+
{
89+
return new ClassHandlerForEntityWithGenericAttributeDbCommand();
90+
}
91+
}
7692

93+
private class ClassHandlerForEntityWithGenericAttributeDbCommand : CustomDbCommand
94+
{
95+
protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
96+
{
97+
var reader = new DataEntityDataReader<ClassHandlerTestClassWithGenericAttribute>(new[]
98+
{
99+
new ClassHandlerTestClassWithGenericAttribute { Id = 1, Name = "James Doe" }
100+
});
101+
return reader;
102+
}
103+
}
104+
#endif
77105
#endregion
78106

79107
#region ClassHandlers
@@ -103,6 +131,21 @@ public ClassHandlerTestClassWithAttribute Set(ClassHandlerTestClassWithAttribute
103131
return entity;
104132
}
105133
}
134+
135+
#if NET7_0_OR_GREATER
136+
public class TestClassHandlerForEntityWithGenericAttribute : IClassHandler<ClassHandlerTestClassWithGenericAttribute>
137+
{
138+
public ClassHandlerTestClassWithGenericAttribute Get(ClassHandlerTestClassWithGenericAttribute entity, ClassHandlerGetOptions options)
139+
{
140+
return entity;
141+
}
142+
143+
public ClassHandlerTestClassWithGenericAttribute Set(ClassHandlerTestClassWithGenericAttribute entity, ClassHandlerSetOptions options)
144+
{
145+
return entity;
146+
}
147+
}
148+
#endif
106149

107150
#endregion
108151

@@ -120,7 +163,15 @@ public class ClassHandlerTestClassWithAttribute
120163
public int Id { get; set; }
121164
public string Name { get; set; }
122165
}
123-
166+
167+
#if NET7_0_OR_GREATER
168+
[ClassHandler<TestClassHandlerForEntityWithGenericAttribute>]
169+
public class ClassHandlerTestClassWithGenericAttribute
170+
{
171+
public int Id { get; set; }
172+
public string Name { get; set; }
173+
}
174+
#endif
124175
#endregion
125176

126177
#region Methods
@@ -162,7 +213,27 @@ public void TestClassHandlerPrecedenceWithAttribute()
162213
// Assert
163214
classHandler.Verify(c => c.Get(It.IsAny<ClassHandlerTestClassWithAttribute>(), It.IsAny<ClassHandlerGetOptions>()), Times.Never);
164215
}
216+
217+
#if NET7_0_OR_GREATER
218+
[TestMethod]
219+
public void TestClassHandlerPrecedenceWithGenericAttribute()
220+
{
221+
// Prepare
222+
var classHandler = new Mock<IClassHandler<ClassHandlerTestClassWithGenericAttribute>>();
223+
FluentMapper
224+
.Entity<ClassHandlerTestClassWithGenericAttribute>()
225+
.ClassHandler(classHandler.Object);
165226

227+
// Act
228+
using (var connection = new ClassHandlerForEntityWithGenericAttributeConnection())
229+
{
230+
var result = connection.QueryAll<ClassHandlerTestClassWithGenericAttribute>();
231+
}
232+
233+
// Assert
234+
classHandler.Verify(c => c.Get(It.IsAny<ClassHandlerTestClassWithGenericAttribute>(), It.IsAny<ClassHandlerGetOptions>()), Times.Never);
235+
}
236+
#endif
166237
#endregion
167238
}
168239
}

RepoDb.Core/RepoDb/Attributes/ClassHandlerAttribute.cs

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public ClassHandlerAttribute(Type handlerType)
3434
///
3535
/// </summary>
3636
/// <param name="handlerType"></param>
37-
private void Validate(Type handlerType)
37+
private static void Validate(Type handlerType)
3838
{
3939
if (handlerType?.IsInterfacedTo(StaticType.IClassHandler) != true)
4040
{
@@ -44,4 +44,33 @@ private void Validate(Type handlerType)
4444

4545
#endregion
4646
}
47+
48+
#if NET7_0_OR_GREATER
49+
/// <summary>
50+
/// An attribute that is used to define a handler for the property transformation.
51+
/// </summary>
52+
public class ClassHandlerAttribute<T> : Attribute
53+
{
54+
/// <summary>
55+
/// Creates a new instance of <see cref="ClassHandlerAttribute{T}"/> class.
56+
/// </summary>
57+
public ClassHandlerAttribute() => Validate(typeof(T));
58+
59+
#region Methods
60+
61+
/// <summary>
62+
///
63+
/// </summary>
64+
/// <param name="handlerType"></param>
65+
private static void Validate(Type handlerType)
66+
{
67+
if (handlerType?.IsInterfacedTo(StaticType.IClassHandler) != true)
68+
{
69+
throw new InvalidTypeException($"Type '{handlerType.FullName}' must implement the '{StaticType.IClassHandler}' interface.");
70+
}
71+
}
72+
73+
#endregion
74+
}
75+
#endif
4776
}
Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using RepoDb.Attributes;
22
using RepoDb.Interfaces;
33
using System;
4+
using System.Linq;
45
using System.Reflection;
56

67
namespace RepoDb.Resolvers
@@ -17,23 +18,29 @@ public class ClassHandlerResolver : IResolver<Type, object>
1718
/// <returns>The equivalent <see cref="IClassHandler{TEntity}"/> object of the .NET CLR type.</returns>
1819
public object Resolve(Type type)
1920
{
20-
var classHandler = (object)null;
21-
22-
// Attribute
21+
object classHandler = null;
22+
2323
var attribute = type.GetCustomAttribute<ClassHandlerAttribute>();
24-
if (attribute != null)
24+
if (attribute is not null)
2525
{
26-
classHandler = Converter.ToType<object>(Activator.CreateInstance(attribute.HandlerType));
26+
classHandler = Activator.CreateInstance(attribute.HandlerType);
2727
}
2828

29-
// Type Level
30-
if (classHandler == null)
29+
if (classHandler is not null) return classHandler;
30+
31+
#if NET7_0_OR_GREATER
32+
var genericAttribute = type.GetCustomAttribute(typeof(ClassHandlerAttribute<>));
33+
if (genericAttribute is not null)
3134
{
32-
classHandler = ClassHandlerMapper.Get<object>(type);
35+
var handlerType = genericAttribute.GetType()
36+
.GetGenericArguments()
37+
.First();
38+
39+
classHandler = Activator.CreateInstance(handlerType);
3340
}
41+
#endif
3442

35-
// Return the value
36-
return classHandler;
43+
return classHandler ?? ClassHandlerMapper.Get<object>(type);
3744
}
3845
}
3946
}

0 commit comments

Comments
 (0)