DapperLib / DapperLib/Dapper

One to Many relationship - Mapping through reflection

Open
#1,808 1 comment 0 reactions 0 assignees View on GitHub

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

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.