Database
 sql >> Teknologi Basis Data >  >> RDS >> Database

Menghilangkan Duplikasi Ekspresi Where dalam Aplikasi

Asumsikan Anda memiliki produk dan kategori. Seorang klien mengatakan bahwa perlu menggunakan proses bisnis lain untuk kategori dengan nilai peringkat lebih tinggi dari 50. Anda memiliki pengalaman yang solid dan Anda memahami bahwa besok nilai ini mungkin berbeda – 127,37. Karena Anda ingin menghindari situasi ini, Anda menulis kode dengan cara berikut:

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);

Sayangnya, ini tidak akan berfungsi jika Anda perlu memilih produk dari kategori yang sesuai. NiceRating memiliki tipe Expression>. Dalam hal Produk, Anda harus menggunakan Expression>.

Jadi, kita perlu mengubah Expression> menjadi Expression>.

 public class Product: HasIdBase<int>
    {
        public virtual Category Category { get; set; }

       //...
    }

    var niceProductsCompilationError = db.Query<Product>.Where(Category.NiceRating);

Untungnya, ini cukup mudah!

 // 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);
	}

Ini adalah implementasi dari komposisi pernyataan di LinqKit. Namun, Entity Framework tidak berfungsi dengan InvokeExpression dan menampilkan NotSupportedException. Tahukah Anda bahwa LINQ memiliki kekurangan? Untuk mengatasi batasan ini, di LinqKit kami menggunakan metode ekstensi AsExpandable. Pete Montgomery menjelaskan masalah ini di blognya. Versi Predicate Builder-nya berfungsi baik untuk IEnumerable dan IQueryable.

Ini kodenya apa adanya.

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. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Cara Mengubah Teks ke Huruf Kecil di SQL

  2. Mitigasi Risiko Data melalui Penyembunyian Data

  3. Menggunakan RStudio dengan Versi Non-Sistem dari Manajer Driver unixODBC

  4. Model Basis Data untuk Sistem Pesan

  5. Cara Lain untuk Melihat Pembaruan Otomatis pada Statistik