Mutasyona başka bir hedef türü için bir yüklem ifade ağacı
İntro
Uygulama I 'm üzerinde çalışmakta, her iş nesnesi iki tip insan vardır: "" ve "" tür. DataContract ActiveRecord içinde Yani, örneğin, olur
namespace ActiveRecord {
class Widget {
public int Id { get; set; }
}
}
namespace DataContract {
class Widget {
public int Id { get; set; }
}
}
DataContract.Widget bir güncelleme söyleyebilirsin ve sihirli bir şekilde aynı özellik değerleri ile ActiveRecord.Widget oluşturun ve bunun yerine kurtaracak. veritabanı Erişim Katmanı aileleri arasında çeviri ilgilenir:
Sorun bu veritabanı Erişim Katmanı yeniden çalışılırken ortaya çıktı.
Sorun
Veritabanı Erişim Katmanı için aşağıdaki gibi eklemek istiyorum:
// Widget is DataContract.Widget
interface IDbAccessLayer {
IEnumerable<Widget> GetMany(Expression<Func<Widget, bool>> predicate);
}
Basit yukarıdaki genel kullanım "get" özel yüklem yöntemi. Tek nokta ilgi olduğunu duyuyorum geçen bir ifade ağacı yerine bir lambda çünkü içinde IDbAccessLayer benim sorgulama IQueryable<ActiveRecord.Widget>; bunu verimli bir şekilde (sanırım ETMENİZ için SQL) ihtiyacım var geçer bir ifade ağacı bu yüzden bu yöntemi sorar sadece.
Sorun şu: parametre sihirli Expression<Func<ActiveRecord.Widget, bool>> Expression<Func<DataContract.Widget, bool>> dönüştürülmesi gerekiyor.
Denenen Bir Çözüm
'D GetMany içinde yapmak gibi . ne
IEnumerable<DataContract.Widget> GetMany(
Expression<Func<DataContract.Widget, bool>> predicate)
{
var lambda = Expression.Lambda<Func<ActiveRecord.Widget, bool>>(
predicate.Body,
predicate.Parameters);
// use lambda to query ActiveRecord.Widget and return some value
}
Bu, örneğin: tipik bir senaryo, çünkü işe yaramaz
predicate == w => w.Id == 0;
...ifade ağacı DataContract.Widget.Idtanımlayan özellik MemberInfo olan MemberAccessExpression bir örneği içerir.
Ayrıca ParameterExpression örnekleri de ifade ağaç ve onun parametre koleksiyonu (predicate.Parameters) bu tarif DataContract.Widget; tüm bu sonuçlara yol hataları beri sorgulanabilir vücut içermiyor bu tür widget ziyade ActiveRecord.Widget.
Biraz aradıktan sonra, bir ifade ağaç değiştirmek için uygun bir yol sunuyor System.Linq.Expressions.ExpressionVisitor (kaynağı here nasıl bir bağlamda bulunabilir), buldum. .NET 4, Bu sınıf, kutu dahil.
Bu ile silahlı, bir ziyaretçi uygulanmaktadır. Bu basit ziyaretçiler üye erişim ve parametre ifadelerde türleri değişen ilgilenir, ama bu yeterli yüklem w => w.Id == 0 ile çalışmak için işlevler.
internal class Visitor : ExpressionVisitor
{
private readonly Func<Type, Type> typeConverter;
public Visitor(Func<Type, Type> typeConverter)
{
this.typeConverter = typeConverter;
}
protected override Expression VisitMember(MemberExpression node)
{
var dataContractType = node.Member.ReflectedType;
var activeRecordType = this.typeConverter(dataContractType);
var converted = Expression.MakeMemberAccess(
base.Visit(node.Expression),
activeRecordType.GetProperty(node.Member.Name));
return converted;
}
protected override Expression VisitParameter(ParameterExpression node)
{
var dataContractType = node.Type;
var activeRecordType = this.typeConverter(dataContractType);
return Expression.Parameter(activeRecordType, node.Name);
}
}
Olur: bu ziyaretçi, GetMany ile
IEnumerable<DataContract.Widget> GetMany(
Expression<Func<DataContract.Widget, bool>> predicate)
{
var visitor = new Visitor(...);
var lambda = Expression.Lambda<Func<ActiveRecord.Widget, bool>>(
visitor.Visit(predicate.Body),
predicate.Parameters.Select(p => visitor.Visit(p));
var widgets = ActiveRecord.Widget.Repository().Where(lambda);
// This is just for reference, see below
Expression<Func<ActiveRecord.Widget, bool>> referenceLambda =
w => w.Id == 0;
// Here we 'd convert the widgets to instances of DataContract.Widget and
// return them -- this has nothing to do with the question though.
}
Sonuçları
İyi haber lambda gayet güzel kurgulanmış. Kötü haber, bir çalışma değil; kullanmaya çalıştığımda bana patlıyor ve özel durum mesajları gerçekten yararlı bir amacı yok.
Aynı ifade ile lambda benim kod üretir ve kodlanmış bir lambda inceledim; tam olarak aynı görünüyorlar. Hata bazı farklar bulmak için çalışırken saat geçirdim, ama yapamam.
Yüklem olduğunda, lambda w => w.Id == 0 tam olarak referenceLambda gibi görünüyor. Ama ikincisi eski etmese IQueryable<T>.Where örneğin, birlikte çalışır, hata ayıklayıcı komut penceresi içinde bu denedim.
Ayrıca yüklem 36 ** zaman her şey gayet iyi çalışıyor bahsetmeliyiz. Bu nedenle ben ziyaretçi olarak yeterince çaba değil, ama takip edilecek bir iz bulamıyorum. varsayıyorum
Nihai Çözüm
Sonra hesaba katılarak doğru cevap sorunu (ikisi aşağıda; biri kısa, biri ile kod) sorun çözüldü; koydum kodu ile birlikte birkaç önemli notlar separate answer tutmak için bu kadar uzun soru olmaktan bile daha uzun.
Cevaplar ve yorumlar için herkese teşekkürler!
CEVAP
İki parametre ifadesi, VisitMember üreten sensin gibi görünüyor() burada:
var converted = Expression.MakeMemberAccess(
base.Visit(node.Expression),
activeRecordType.GetProperty(node.Member.Name));
...beri taban.() Ziyaret hayal VisitParameter ve GetMany içinde sona erecek() kendisi:
var lambda = Expression.Lambda<Func<ActiveRecord.Widget, bool>>(
visitor.Visit(predicate.Body),
predicate.Parameters.Select(p => visitor.Visit(p));
Eğer vücutta bir ParameterExpression kullanıyorsanız, Lambda için ilan aynı örneği (sadece aynı tip ve adı değil) olmalı. Sorunları daha önce bu tür bir senaryo ile yaşadım, sonuç sadece ifade oluşturmak mümkün değildi gerçi, sadece istisna olur. Her durumda parametre örnek olur bakalım yeniden deneyebilirsiniz.

başka kullanarak lambda ifade için bir...
numaralama başka bir türü için bir num...
Düzenli ifade bir e-posta adresi doğru...
Satır eşleştirmek için düzenli ifade e...
Bir dize kontrol etmek için nasıl &quo...