One to Many relationship - Mapping through reflection
Nobody has claimed this yet.
- Dominant language
- C#
- Stars
- 18.4k
- Forks
- 3.7k
- Avg merge
- 5h 8m
- Merged PRs (30d)
- 1
Description
In order to avoid boilerplate code with dictionary to keep values etc i was thinking to create a more generic solution for one to many scenarios. I came up with this:
public async Task<IReadOnlyCollection<TEntity>> ReadAsync<TEntity, TManyEntity, TParameters>(string query, TParameters parameters)
{
using var connection = _context.CreateConnection();
var dictionary = new Dictionary<int, TEntity>();
var result = await connection.QueryAsync<TEntity, TManyEntity, TEntity>(query,
(one, many) => Map(dictionary, one, many),
parameters,
commandType: CommandType.StoredProcedure);
return result.Distinct().ToList();
}
private static TEntity Map<TEntity, TManyEntity>(Dictionary<int, TEntity> dictionary, TEntity one, TManyEntity many)
{
var entityType = one?.GetType()!;
var idProperty = entityType.GetProperties()
.FirstOrDefault(p => p.IsDefined(typeof(IdAttribute), true))!;
var getId = PropertyHelper.InvokeGet<TEntity, int>(idProperty);
var id = getId(one);
if (!dictionary.TryGetValue(id, out var currentOne))
{
currentOne = one;
dictionary.Add(id, currentOne);
}
var manyProperty = entityType.GetProperties()
.FirstOrDefault(p => p.IsDefined(typeof(ManyRelationshipAttribute), true))!;
var collection = (List<TManyEntity>)manyProperty?.GetValue(currentOne)!;
var collectionType = collection.GetType();
var addToCollection = PropertyHelper.InvokeAdd<TEntity, TManyEntity>(collectionType);
addToCollection(collection, many);
return currentOne;
}
internal static class PropertyHelper
{
private static readonly ConcurrentDictionary<string, Delegate> _delegateCache = new();
public static Func<TClass, TResult> InvokeGet<TClass, TResult>(PropertyInfo property) =>
(Func<TClass, TResult>)_delegateCache.GetOrAdd(property.Name, key =>
{
var getMethod = property.GetMethod;
var res = getMethod?.CreateDelegate(typeof(Func<TClass, TResult>))!;
return res;
});
public static Action<List<TChildClass>, TChildClass> InvokeAdd<TParentClass, TChildClass>(Type type) =>
(Action<List<TChildClass>, TChildClass>)_delegateCache.GetOrAdd("Many", key =>
{
var addMethod = type.GetMethod("Add")!;
var delegateType = typeof(Action<List<TChildClass>, TChildClass>)!;
var res = addMethod?.CreateDelegate(delegateType)!;
return (Action<List<TChildClass>, TChildClass>)res;
});
}
By doing this i get no improvements in comparison with reflection without delegates. My question is, is it worth it, when it comes to performance, to use it this way or it is better to keep boilerplate code everytime i need to do a one-many releationship with no reflection at all?
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
The issue names no repository file, test, or entry point, so first locate Dapper's existing multi-mapping implementation and relevant performance tests. Clarify whether a generic reflection-based relationship mapper is desired and define completion through an agreed approach and comparative benchmark against boilerplate mapping.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- backend, databases
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100