Skip to content

Commit b0f180b

Browse files
Merge pull request #360 from johelvisguzman/issue353-2
Allowed the ability to add an internal mapping provider which maps SQL query results into a new entity form
2 parents b46ebc2 + 01cc9c6 commit b0f180b

File tree

12 files changed

+692
-5
lines changed

12 files changed

+692
-5
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
namespace DotNetToolkit.Repository.Configuration.Mapper
2+
{
3+
using Conventions;
4+
using Extensions;
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Data;
8+
using System.Linq;
9+
using System.Reflection;
10+
11+
/// <summary>
12+
/// An implementation of <see cref="IMapper{T}" />.
13+
/// </summary>
14+
internal class DefaultMapper<T> : IMapper<T> where T : class
15+
{
16+
#region Fields
17+
18+
private readonly Dictionary<string, PropertyInfo> _propertiesMapping;
19+
20+
#endregion
21+
22+
#region Constructors
23+
24+
/// <summary>
25+
/// Initializes a new instance of the <see cref="DefaultMapper{T}"/> class.
26+
/// </summary>
27+
public DefaultMapper()
28+
{
29+
_propertiesMapping = typeof(T).GetRuntimeProperties()
30+
.Where(x => x.IsPrimitive() && x.IsColumnMapped())
31+
.OrderBy(x => x.GetColumnOrder())
32+
.ToDictionary(x => x.GetColumnName(), x => x);
33+
}
34+
35+
#endregion
36+
37+
#region Implementation of IMapper<out T>
38+
39+
/// <summary>
40+
/// Maps each element to a new form.
41+
/// </summary>
42+
/// <param name="reader">The data reader used for transforming each element.</param>
43+
/// <returns>The new projected element form.</returns>
44+
public T Map(IDataReader reader)
45+
{
46+
if (reader == null)
47+
throw new ArgumentNullException(nameof(reader));
48+
49+
var entity = Activator.CreateInstance<T>();
50+
51+
for (var i = 0; i < reader.FieldCount; i++)
52+
{
53+
var name = reader.GetName(i);
54+
var value = reader[name];
55+
56+
if (value == DBNull.Value)
57+
value = null;
58+
59+
if (_propertiesMapping.ContainsKey(name))
60+
{
61+
if (!reader.IsDBNull(reader.GetOrdinal(name)))
62+
{
63+
var pi = _propertiesMapping[name];
64+
65+
pi.SetValue(entity, value);
66+
}
67+
}
68+
}
69+
70+
return entity;
71+
}
72+
73+
#endregion
74+
}
75+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
namespace DotNetToolkit.Repository.Configuration.Mapper
2+
{
3+
using System.Data;
4+
5+
/// <summary>
6+
/// Represents a mapper which takes a data reader returned by an executed SQL query and maps it into new entity form.
7+
/// </summary>
8+
/// <typeparam name="T">The type of the entity.</typeparam>
9+
public interface IMapper<out T> where T : class
10+
{
11+
/// <summary>
12+
/// Maps each element to a new form.
13+
/// </summary>
14+
/// <param name="reader">The data reader used for transforming each element.</param>
15+
/// <returns>The new projected element form.</returns>
16+
T Map(IDataReader reader);
17+
}
18+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
namespace DotNetToolkit.Repository.Configuration.Mapper
2+
{
3+
/// <summary>
4+
/// Represents a mapping provider.
5+
/// </summary>
6+
public interface IMapperProvider
7+
{
8+
/// <summary>
9+
/// Creates a mapper for the specified entity type.
10+
/// </summary>
11+
/// <typeparam name="T">The type of the entity.</typeparam>
12+
/// <returns>The <see cref="IMapper{T}"/>.</returns>
13+
IMapper<T> Create<T>() where T : class;
14+
}
15+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
namespace DotNetToolkit.Repository.Configuration.Mapper
2+
{
3+
using System;
4+
using System.Collections.Generic;
5+
6+
/// <summary>
7+
/// An implementation of <see cref="IMapperProvider" />.
8+
/// </summary>
9+
public class MapperProvider : IMapperProvider
10+
{
11+
private static volatile MapperProvider _instance;
12+
private static readonly object _syncRoot = new object();
13+
private readonly Dictionary<Type, IMapper<object>> _mappings = new Dictionary<Type, IMapper<object>>();
14+
15+
/// <summary>
16+
/// Gets the instance.
17+
/// </summary>
18+
internal static MapperProvider Instance
19+
{
20+
get
21+
{
22+
if (_instance == null)
23+
{
24+
lock (_syncRoot)
25+
{
26+
if (_instance == null)
27+
_instance = new MapperProvider();
28+
}
29+
}
30+
31+
return _instance;
32+
}
33+
}
34+
35+
/// <summary>
36+
/// Creates a mapper for the specified entity type.
37+
/// </summary>
38+
/// <typeparam name="T">The type of the entity.</typeparam>
39+
/// <returns>The <see cref="IMapper{T}"/>.</returns>
40+
public IMapper<T> Create<T>() where T : class
41+
{
42+
if (_mappings.ContainsKey(typeof(T)))
43+
return (IMapper<T>)_mappings[typeof(T)];
44+
45+
return new DefaultMapper<T>();
46+
}
47+
48+
/// <summary>
49+
/// Registers a mapper for the specified entity type.
50+
/// </summary>
51+
/// <typeparam name="T">The type of the entity.</typeparam>
52+
/// <param name="mapper">The mapper to register.</param>
53+
public MapperProvider Register<T>(IMapper<T> mapper) where T : class
54+
{
55+
if (mapper == null)
56+
throw new ArgumentNullException(nameof(mapper));
57+
58+
if (_mappings.ContainsKey(typeof(T)))
59+
_mappings[typeof(T)] = mapper;
60+
else
61+
_mappings.Add(typeof(T), mapper);
62+
63+
return this;
64+
}
65+
}
66+
}

src/DotNetToolkit.Repository/Configuration/Options/IRepositoryOptions.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Factories;
55
using Interceptors;
66
using Logging;
7+
using Mapper;
78
using System;
89
using System.Collections.Generic;
910

@@ -27,6 +28,11 @@ public interface IRepositoryOptions
2728
/// </summary>
2829
ICacheProvider CachingProvider { get; }
2930

31+
/// <summary>
32+
/// Gets the configured mapper provider.
33+
/// </summary>
34+
IMapperProvider MapperProvider { get; }
35+
3036
/// <summary>
3137
/// Gets the configured internal context factory.
3238
/// </summary>

src/DotNetToolkit.Repository/Configuration/Options/RepositoryOptions.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Factories;
55
using Interceptors;
66
using Logging;
7+
using Mapper;
78
using System;
89
using System.Collections.Generic;
910
using System.Linq;
@@ -19,6 +20,7 @@ public class RepositoryOptions : IRepositoryOptions
1920
private IRepositoryContextFactory _contextFactory;
2021
private ILoggerProvider _loggerProvider;
2122
private ICacheProvider _cachingProvider;
23+
private IMapperProvider _mapperProvider;
2224

2325
#endregion
2426

@@ -39,6 +41,11 @@ public class RepositoryOptions : IRepositoryOptions
3941
/// </summary>
4042
public virtual ICacheProvider CachingProvider { get { return _cachingProvider; } }
4143

44+
/// <summary>
45+
/// Gets the configured mapper provider.
46+
/// </summary>
47+
public IMapperProvider MapperProvider { get { return _mapperProvider; } }
48+
4249
/// <summary>
4350
/// Gets the configured internal context factory.
4451
/// </summary>
@@ -54,6 +61,7 @@ public virtual bool IsConfigured
5461
return ContextFactory != null ||
5562
LoggerProvider != null ||
5663
CachingProvider != null ||
64+
MapperProvider != null ||
5765
Interceptors.Any();
5866
}
5967
}
@@ -167,7 +175,22 @@ internal RepositoryOptions With(ICacheProvider cacheProvider)
167175

168176
return this;
169177
}
170-
178+
179+
/// <summary>
180+
/// Returns the option instance with a configured mapper provider for mapping an query result to a valid entity object within the repository.
181+
/// </summary>
182+
/// <param name="mapperProvider">The entity mapper provider.</param>
183+
/// <returns>The same option instance.</returns>
184+
internal RepositoryOptions With(IMapperProvider mapperProvider)
185+
{
186+
if (mapperProvider == null)
187+
throw new ArgumentNullException(nameof(mapperProvider));
188+
189+
_mapperProvider = mapperProvider;
190+
191+
return this;
192+
}
193+
171194
#endregion
172195
}
173196
}

src/DotNetToolkit.Repository/Configuration/Options/RepositoryOptionsBuilder.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Factories;
55
using Interceptors;
66
using Logging;
7+
using Mapper;
78
using System;
89

910
/// <summary>
@@ -181,6 +182,21 @@ public RepositoryOptionsBuilder UseCachingProvider(ICacheProvider cacheProvider)
181182
return this;
182183
}
183184

185+
/// <summary>
186+
/// Configures the repository options with a mapper provider for mapping an query result to a valid entity object within the repository.
187+
/// </summary>
188+
/// <param name="mapperProvider">The entity mapper provider.</param>
189+
/// <returns>The same builder instance.</returns>
190+
public virtual RepositoryOptionsBuilder UseMapperProvider(IMapperProvider mapperProvider)
191+
{
192+
if (mapperProvider == null)
193+
throw new ArgumentNullException(nameof(mapperProvider));
194+
195+
_options.With(mapperProvider);
196+
197+
return this;
198+
}
199+
184200
/// <summary>
185201
/// Configures the repository options with an internal context factory.
186202
/// </summary>

0 commit comments

Comments
 (0)