2013-02-28 19 views
6

Bir veritabanında "nerede" ifadesi olarak geçmek için bazı kod oluşturma ifadeleri var ve bazı şeyleri hızlandırmaya çalışıyorum. gerçek bir şey için tabloyu almaya kodu -Önceden var olan bir BinaryExpression içinde bir ConstantExpression değerinin değiştirilmesi

private Expression MakeWhereForPK(int id) 
{ 
    var paramExp = Expression.Parameter(typeof(Brand),"b"); 

    //Expression to get value from the entity 
    var leftExp = Expression.Property(paramExp,"ID"); 

    //Expression to state the value to match (from the passed in variable) 
    var rightExp = Expression.Constant(id,typeof(int)); 

    //Expression to compare the two 
    var whereExp = Expression.Equal(leftExp,rightExp); 

    return Expression.Lambda<Func<Brand,bool>>(whereExp,paramExp); 
} 

yukarıdaki soru için basitleştirme geçerli:

Bu örnek, aşağıda açıklamada değerindeki geçirilen bir bir tablonun PK maç için nerede bir hale getirir sorgulamak ve bulmak onun PK vb etkili bir genellikle kodda yapabilir aynı şeyi yapıyor: buldum işleri biraz optimize etmek testine yaparken,

ctx.Brands.Where(b => b.ID = id); 

Bu Tamam çalışır, ancak onun Oldukça yavaş - yukarıdaki 1000000 kez yapmak, yaklaşık 25 sn sürer. Yukarıdaki son satırı atlarsanız daha iyi (ama tabii ki işe yaramaz!), Bu yüzden zamanın 2/3rds alma, ancak geri kalan büyük değil Expression.Lamba gibi görünüyor.

tüm sorguları Ben bir IN stil ifadesi çevirmek ve bir kez oluşturmak, ama ne yazık ki yukarıdaki kuşağının en tasarruf etmektir için umduğum şey, mümkün değildir olabilir kerede ne olacak olsaydı, ve sadece oluşturulan ifadeyi yeniden kullanın, ancak id farklı bir değerde iletin.

Bu, Linq'e iletileceğinden, bu ifadeyi, çağrıda iletebileceğim bir tamsayı parametresine sahip olacak şekilde derleyemiyorum - ifade ağacı olarak kalmalıdır.

Expression<Func<Brand,bool>> savedExp; 

private Expression MakeWhereForPKWithCache(int id) 
{ 
    if (savedExp == null) 
    { 
     savedExp = MakeWhereForPK(id); 
    } 
    else 
    { 
     var body = (BinaryExpression)savedExp.Body; 
     var rightExp = (ConstantExpression)body.Right; 

     //At this point, value is readonly, so is there some otherway to "inject" id, 
     //and save on compilation? 
     rightExp.Value = id; 
    } 

    return savedExp; 
} 

nasıl sadece id farklı bir değerle, ifadeyi yeniden kullanabilirsiniz:

Yani şu zamanlama egzersiz yapmak amacıyla bir basit versiyonu olabilir?

+0

bir fikirdir [a kullanmayı olurdu

class ExpressionHolder { public int Value { get; set; } public Expression<Func<Brand, bool>> Expr { get; private set; } public ExpressionHolder() { Expr = MakeWhereForPK(); } private Expression<Func<Brand, bool>> MakeWhereForPK() { var paramExp = Expression.Parameter(typeof(Brand), "b"); var leftExp = Expression.Property(paramExp, "ID"); var rightExp = Expression.Property(Expression.Constant(this), "Value"); var whereExp = Expression.Equal(leftExp, rightExp); return Expression.Lambda<Func<Brand, bool>>(whereExp, paramExp); } } 

Bu kod yaklaşık 500 × hızlıdır 'ConstantExpression' yerine' ParameterExpression'] (http://msdn.microsoft.com/en-us/library/system.linq.expressions.parameterexpression.aspx)? (Btw, performans bir sorun ise, neden LINQ kullanıyoruz? LINQ, SQL öğrenmek zorunda kalmamak için performans gösteriyor.) – Andomar

+2

@Andomar: ikinci 'ParameterExpression' 'Func ' yapacaktır. 'Ner' için bir yük olarak kullanılamaz. – Dennis

cevap

4

İfade ağaçlarını değiştiremezsiniz - bunlar değişmezdir. Ama kendi ziyaretçinin yapma yoluyla sabit ifadesini değiştirebilirsiniz:

class MyVisitor : ExpressionVisitor 
{ 
    private readonly ConstantExpression newIdExpression; 

    public MyVisitor(int newId) 
    { 
     this.newIdExpression = Expression.Constant(newId); 
    } 

    public Expression ReplaceId(Expression sourceExpression) 
    { 
     return Visit(sourceExpression); 
    } 

    protected override Expression VisitConstant(ConstantExpression node) 
    { 
     return newIdExpression; 
    } 
} 

Kullanımı:

  var expr = MakeWhereForPK(0); // p => p.ID == 0 
      var visitor = new MyVisitor(1); 
      var newExpr = visitor.ReplaceId(expr); p => p.ID == 1 

Not, bu mevcut ağacın bir kopyasını yapar. ve bunu performans için test etmedim. Emin değilim, daha hızlı olsun ya da olmasın.

Bu kod: 00:: 00: 02.3644953
Yapıldı 04.1714254
sabit ifade yer değiştirmesi: 00: 00 ifade oluşturur

:

  // warming up 
      var visitor = new MyVisitor(1); 
      var expr = MakeWhereForPK(0); 
      visitor.ReplaceId(MakeWhereForPK(0)); 

      var sw = new System.Diagnostics.Stopwatch(); 
      sw.Start(); 

      for (var i = 0; i < 1000000; i++) 
      { 
       MakeWhereForPK(i); 
      } 

      sw.Stop(); 
      Console.WriteLine("Make expression: {0}", sw.Elapsed); 

      sw.Restart(); 

      for (var i = 0; i < 1000000; i++) 
      { 
       visitor.Visit(expr); 
      } 

      sw.Stop(); 
      Console.WriteLine("Replace constant expression: {0}", sw.Elapsed); 

      Console.WriteLine("Done.");  

benim makinede bu sonuçlar üretir .

Yeni bir ifade oluşturmadan ziyaretçi daha hızlı görünüyor.

+0

Güzel - bunu kabul etmeye çalışacağım - makul bir fark yaratabilir. Teşekkürler –

+0

BTW, kodunuz bir şekilde yanlıştır, çünkü her zaman 0 ile 1 arasında değişir (ve 'i' değil). Ama performansta bir fark yaratacağını sanmıyorum. – svick

6

İfade ağacının yalnızca basit sabitler içermesi gerekmediği gerçeğini kullanabilirsiniz, ayrıca erişilen özelliği de içerebilir.Yani, yapacağınız şey, bazı özelliklerin değerine erişen tek bir ifade oluşturmak ve her defasında yalnızca bu özelliği değil, ifade ağacını değiştirmektir.

şey gibi: Ben şu sonuçlar elde Dennis'in ölçüm kodu kullanarak:

Make expression: 00:00:02.9869921 
Replace constant expression: 00:00:02.3332857 
Set property: 00:00:00.0056485 
+0

Bu ilginç bir alternatif - biraz yeniden çalışma gerektirecek, ama buna değecek gibi görünüyor. –

+0

Mükemmel mükemmelliğin yanı sıra, bu çok yönlü bir yöntemdir çünkü bir şeyin kullandığı ifade ağacını değiştirme yeteneğiniz olmasa bile sabitleri değiştirmenize izin verir. Bu, 'public Expression MakeAccessExpression() => Expression.Property (Expression.Constant (this), (Proxy .Value)) adlı bir Proxy sınıfı ile kullandığım şeydir; – jnm2

İlgili konular