Database
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Database

Loại bỏ sự trùng lặp của biểu thức vị trí trong ứng dụng

Giả sử bạn có sản phẩm và danh mục. Một khách hàng nói rằng cần sử dụng các quy trình nghiệp vụ khác cho các danh mục có giá trị xếp hạng cao hơn 50. Bạn có kinh nghiệm vững chắc và bạn hiểu rằng ngày mai giá trị này có thể khác - 127,37. Khi bạn muốn tránh trường hợp này, bạn viết mã theo cách sau:

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

Thật không may, điều này sẽ không hoạt động nếu bạn cần chọn sản phẩm từ các danh mục tương ứng. NiceRating có kiểu Expression >. Trong trường hợp Sản phẩm, bạn sẽ cần sử dụng Biểu thức >.

Do đó, chúng ta cần chuyển đổi Biểu thức > thành Expression >.

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

       //...
    }

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

May mắn thay, nó khá dễ dàng!

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

Đây là việc thực hiện thành phần câu lệnh trong LinqKit. Tuy nhiên, Entity Framework không hoạt động với InvokeExpression và ném NotSupportedException. Bạn có biết rằng LINQ có nhược điểm không? Để khắc phục hạn chế này, trong LinqKit, chúng tôi sử dụng phương thức mở rộng AsExpandable. Pete Montgomery đã mô tả vấn đề này trong blog của mình. Phiên bản Predicate Builder của anh ấy hoạt động cho cả IEnumerable và IQueryable .

Đây là mã hiện tại.

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. Điều chỉnh:Một nơi tốt để bắt đầu

  2. SQL Luôn sẵn sàng Nhóm:Đối tượng Máy tính

  3. Đánh giá sách:Benjamin Nevarez:Điều chỉnh &Tối ưu hóa Truy vấn

  4. Lệnh TCL trong SQL

  5. Xu hướng ScyllaDB - Cách người dùng triển khai cơ sở dữ liệu dữ liệu lớn theo thời gian thực