sql >> データベース >  >> RDS >> Database

アプリケーションでのWhere式の重複を排除

    あなたが製品とカテゴリーを持っていると仮定します。クライアントは、評価値が50を超えるカテゴリには他のビジネスプロセスを使用する必要があると述べています。確かな経験があり、明日はこの値が異なる可能性があることを理解しています–127.37。この状況を回避したいので、次の方法でコードを記述します。

    public class Category : HasIdBase<int>
        {
            public static readonly Expression<Func<Category, bool>> NiceRating = x => x.Rating > 50;
    
           //...
        }
    
        var niceCategories = db.Query<Category>.Where(Category.NiceRating);

    残念ながら、対応するカテゴリから製品を選択する必要がある場合、これは機能しません。 NiceRatingには、Expression >タイプがあります。 Productの場合、Expression >。

    を使用する必要があります。

    したがって、Expression >をExpression>に変換する必要があります。

     public class Product: HasIdBase<int>
        {
            public virtual Category Category { get; set; }
    
           //...
        }
    
        var niceProductsCompilationError = db.Query<Product>.Where(Category.NiceRating);

    幸いなことに、それは非常に簡単です!

     // In fact, we implement a composition of statements, 
             // which returns the statement matching the composition of target functions 
         public static Expression<Func<TIn, TOut>> Compose<TIn, TInOut, TOut>(
                 this Expression<Func<TIn, TInOut>> input,
                 Expression<Func<TInOut, TOut>> inOutOut)
            {
                // this is the X parameter => blah-blah. For a lambda, we need null
                var param = Expression.Parameter(typeof(TIn), null);
                // we get an object, to which this statement is applied
                var invoke = Expression.Invoke(input, param);
                // and execute “get an object and apply its statement”
                var res = Expression.Invoke(inOutOut, invoke);
                
                // return a lambda of the required type 
                return Expression.Lambda<Func<TIn, TOut>>(res, param);
            }
            
            // add an “advanced” variant of Where
            public static IQueryable<T> Where<T, TParam>(this IQueryable<T> queryable,
                Expression<Func<T, TParam>> prop, Expression<Func<TParam, bool>> where)
            {
                return queryable.Where(prop.Compose(where));
            }
    	
            // check
    	[Fact]
    	public void AdvancedWhere_Works()
    	{
    		var product = new Product(new Category() {Rating = 700}, "Some Product", 100500);
    		var q = new[] {product}.AsQueryable();
    
    		var values = q.Where(x => x.Category, Category.NiceRating).ToArray();
    		Assert.Equal(700, values[0].Category.Rating);
    	}

    これは、LinqKitでのステートメント構成の実装です。ただし、Entity FrameworkはInvokeExpressionでは機能せず、NotSupportedExceptionをスローします。 LINQには欠点があることをご存知ですか?この制限を回避するために、LinqKitでは拡張メソッドAsExpandableを使用します。ピートモンゴメリーは彼のブログでこの問題について説明しました。彼のバージョンのPredicateBuilderは、IEnumerableとIQueryableの両方で機能します。

    これがそのままのコードです。

    public static class PredicateBuilder
    {
        /// <summary>
        /// Creates a predicate that evaluates to true.
        /// </summary>
        public static Expression<Func<T, bool>> True<T>() { return param => true; }
     
        /// <summary>
        /// Creates a predicate that evaluates to false.
        /// </summary>
        public static Expression<Func<T, bool>> False<T>() { return param => false; }
     
        /// <summary>
        /// Creates a predicate expression from the specified lambda expression.
        /// </summary>
        public static Expression<Func<T, bool>> Create<T>(Expression<Func<T, bool>> predicate) { return predicate; }
     
        /// <summary>
        /// Combines the first predicate with the second using the logical "and".
        /// </summary>
        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
        {
            return first.Compose(second, Expression.AndAlso);
        }
     
        /// <summary>
        /// Combines the first predicate with the second using the logical "or".
        /// </summary>
        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
        {
            return first.Compose(second, Expression.OrElse);
        }
     
        /// <summary>
        /// Negates the predicate.
        /// </summary>
        public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> expression)
        {
            var negated = Expression.Not(expression.Body);
            return Expression.Lambda<Func<T, bool>>(negated, expression.Parameters);
        }
     
        /// <summary>
        /// Combines the first expression with the second using the specified merge function.
        /// </summary>
        static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
        {
            // zip parameters (map from parameters of second to parameters of first)
            var map = first.Parameters
                .Select((f, i) => new { f, s = second.Parameters[i] })
                .ToDictionary(p => p.s, p => p.f);
     
            // replace parameters in the second lambda expression with the parameters in the first
            var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);
     
            // create a merged lambda expression with parameters from the first expression
            return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
        }
     
        class ParameterRebinder : ExpressionVisitor
        {
            readonly Dictionary<ParameterExpression, ParameterExpression> map;
     
            ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
            {
                this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
            }
     
            public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
            {
                return new ParameterRebinder(map).Visit(exp);
            }
     
            protected override Expression VisitParameter(ParameterExpression p)
            {
                ParameterExpression replacement;
     
                if (map.TryGetValue(p, out replacement))
                {
                    p = replacement;
                }
     
                return base.VisitParameter(p);
            }
        }
    }


    1. MariaDBのテーブルからランダムな行を返す

    2. MariaDBでのDATE_SUB()のしくみ

    3. PostgreSQLデータベースをSQLServerデータベースに移行する方法は?

    4. pg_dumpを使用して、データベース内の1つのテーブルからのみ挿入ステートメントを取得する