简体   繁体   中英

Entity Framework Core: How to IgnoreQueryFilters for DbSet Find method

I am using Global Query Filters but do not want to apply it on DbSet .Find method. I can not use .IgnoreQueryFilters because it returns IQueryable . Also I do not want use .FirstOrDefault due to performance reasons.

Help is appreciated.

You can create 'Find' extension method for IDbSet<T> where you can "simulate" original Find method lookup flow.

public static async Task<T> FindAsync<T>(this IDbSet<T> source, Expression<Func<TSource,bool>> predicate)
    where T : class, new()
{
    // parameters validation if needed

    return
        source.Local.SingleOrDefault(predicate)
        ?? await source.IgnoreQueryFilters().SingleOrDefaultAsync(predicate);//.ConfigureAwait(false);
}

I didn't test it, but might work just fine.

In case you have some common table key convention you can even go for more generic extension method.

public static async Task<T> FindAsync<T, TKey>(this IDbSet<T> source, TKey id)
    where T : class, new()
    where TKey : IEquatable<TKey>
{
    // parameters validation if needed

    return
        source.Local.SingleOrDefault(e => e.Id == id)
        ?? await source.IgnoreQueryFilters().SingleOrDefaultAsync(e => e.Id == id);//.ConfigureAwait(false);
}

Hope it helps.

I created more generic approach based on dropoutcoder solution. It does not require that DbSet has primary key with strict Id name. It still has only one key restriction but it suits me well.

public static class DataContextProviderExtensions
{
    public static T FindWithIgnoreQueryFilters<T, TValue>(this DataContextProvider context, TValue id) where T : class, new()
    {
        var expr = GetByIdExpression<T, TValue>(context, id);

        return context.Set<T>().Local.SingleOrDefault(expr.Compile())
            ?? context.Set<T>().IgnoreQueryFilters().SingleOrDefault(expr);
    }

    private static Expression<Func<T, bool>> GetByIdExpression<T, TValue>(DataContextProvider context, TValue id) where T : class, new()
    {
        var keys = context.GetEntityKeys<T>().ToArray();
        if (keys.Length != 1)
            throw new Exception("GetByIdExpression works only with Entity which has one primary key column.");

        var param = Expression.Parameter(typeof(T), "p");
        var exp = Expression.Lambda<Func<T, bool>>(
            Expression.Equal(
                Expression.Property(param, keys[0]),
                ExpressionClosureFactory.GetField(id)
            ),
            param
        );
        return exp;
    }
}

internal class ExpressionClosureFactory
{
    public static MemberExpression GetField<TValue>(TValue value)
    {
        var closure = new ExpressionClosureField<TValue>
        {
            ValueProperty = value
        };

        return Expression.Field(Expression.Constant(closure), "ValueProperty");
    }

    class ExpressionClosureField<T>
    {
        public T ValueProperty;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM