0

I want to be able use a type instead of T with Dbset

For example Dbset(myType) instead of DbSet

EF core 3.1 requires you to use DbSet and all the functionality is encapsulated in Extension methods.

1 Answer 1

1

Here's a solution I went with for EF 3.1

Hoping that since I've already been through the pain, I can share my solution for the next poor soul who has to do this.

Usage

var dbSet = new DbSetFacade(myType);
dynamic results = await dbSet.ToListAsync();

I'll just share ToList,Count for now to keep it brief!

public class DbSetFacade
{
    private static ReadOnlyDictionary<EfQueryExtensionType, MethodInfo> _extensionMethodInfos = GetExtensionMethodInfos();

    private readonly object _dbSet;
    private readonly ReadOnlyDictionary<EfQueryExtensionType, MethodInfo> _genericInstanceMethodInfos;

    public DbSetFacade(DbContext context, Type entityType)
    {
        if (context == null) throw new ArgumentNullException(nameof(context));
        if (entityType == null) throw new ArgumentNullException(nameof(entityType));

        _dbSet = GetDbSetInstance(context, entityType);

        _genericInstanceMethodInfos = new ReadOnlyDictionary<EfQueryExtensionType, MethodInfo>(new Dictionary<EfQueryExtensionType, MethodInfo>
        {
            [EfQueryExtensionType.AsNoTracking] = _extensionMethodInfos[EfQueryExtensionType.AsNoTracking].MakeGenericMethod(entityType),
            [EfQueryExtensionType.ToListAsync] = _extensionMethodInfos[EfQueryExtensionType.ToListAsync].MakeGenericMethod(entityType),
            [EfQueryExtensionType.CountAsyncWithoutPredicate] = _extensionMethodInfos[EfQueryExtensionType.CountAsyncWithoutPredicate].MakeGenericMethod(entityType),
        });
    }

    enum EfQueryExtensionType
    {
        AsNoTracking,
        ToListAsync,
        //FirstOrDefaultAsyncWithPredicate,
        //FirstOrDefaultAsyncWithoutPredicate,
        //ContainsAsync,
        //CountAsyncWithPredicate,
        CountAsyncWithoutPredicate
    }

    public Task<int> CountAsync(CancellationToken token = default) => _genericInstanceMethodInfos[EfQueryExtensionType.CountAsyncWithoutPredicate].Invoke(null, new object[] { _dbSet, token }) as Task<int>;


    public async Task<dynamic> ToListAsync(CancellationToken token = default)
    {
        var noTrackingSet = _genericInstanceMethodInfos[EfQueryExtensionType.AsNoTracking].Invoke(null, new object[] { _dbSet });
        var toList = _genericInstanceMethodInfos[EfQueryExtensionType.ToListAsync].Invoke(null, new object[] { noTrackingSet, token }) as Task;

        await toList.ConfigureAwait(false);

        return (object)((dynamic)toList).Result;
    }

    private static object GetDbSetInstance(DbContext context, Type type)
    {
        return typeof(DbContext).GetMethods()
            .FirstOrDefault(p => p.Name == "Set" && p.ContainsGenericParameters)
            .MakeGenericMethod(type)
            .Invoke(context, null);
    }

    static ReadOnlyDictionary<EfQueryExtensionType, MethodInfo> GetExtensionMethodInfos()
    {
        var extensionMethods = typeof(EntityFrameworkQueryableExtensions)
            .GetTypeInfo()
            .GetMethods()
            .Where(x => x.ContainsGenericParameters && x.GetParameters()[0].ParameterType.IsGenericType);

        return new ReadOnlyDictionary<EfQueryExtensionType, MethodInfo>(new Dictionary<EfQueryExtensionType, MethodInfo>
        {
            [EfQueryExtensionType.AsNoTracking] = extensionMethods.FirstOrDefault(x => x.Name == "AsNoTracking"),
            [EfQueryExtensionType.ToListAsync] = extensionMethods.FirstOrDefault(x => x.Name == "ToListAsync" && x.GetParameters().Count() == 2),
            [EfQueryExtensionType.CountAsyncWithoutPredicate] = extensionMethods.FirstOrDefault(x => x.Name == "CountAsync" && x.GetParameters().Count() == 2)
        });
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.