2016-09-05 70 views
7

QueryableBase ve INhQueryProvider etrafında sarıcı oluşturmaya çalışıyorum; bu da yapıcıda bir koleksiyon alacak ve veritabanına gitmek yerine bellekte sorgulayacaktır. Bu yüzden NHibernate'in ToFuture() davranışını alay edebilmem ve sınıflarımı düzgün bir şekilde test edebiliyorum. Sorun şu ki, sonsuz yineleme nedeniyle yığın taşmasıyla karşı karşıya olduğum ve sebebini bulmaya çalışıyorum.Bellek içi sorguyla özel bir QueryProvider uygulaması

public class NHibernateQueryableProxy<T> : QueryableBase<T>, IOrderedQueryable<T> 
{ 
    public NHibernateQueryableProxy(IQueryable<T> data) : base(new NhQueryProviderProxy<T>(data)) 
    { 
    } 

    public NHibernateQueryableProxy(IQueryParser queryParser, IQueryExecutor executor) : base(queryParser, executor) 
    { 
    } 

    public NHibernateQueryableProxy(IQueryProvider provider) : base(provider) 
    { 
    } 

    public NHibernateQueryableProxy(IQueryProvider provider, Expression expression) : base(provider, expression) 
    { 
    } 

    public new IEnumerator<T> GetEnumerator() 
    { 
     return Provider.Execute<IEnumerable<T>>(Expression).GetEnumerator(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 
} 

internal class NhQueryProviderProxy<T> : INhQueryProvider 
{ 
    private readonly IQueryProvider provider; 

    public NhQueryProviderProxy(IQueryable<T> data) 
    { 
     provider = data.AsQueryable().Provider; 
    } 

    public IQueryable CreateQuery(Expression expression) 
    { 
     return new NHibernateQueryableProxy<T>(this, expression); 
    } 

    public IQueryable<TElement> CreateQuery<TElement>(Expression expression) 
    { 
     return new NHibernateQueryableProxy<TElement>(this, expression); 
    } 

    public object Execute(Expression expression) 
    { 
     return provider.Execute(expression); 
    } 

    public TResult Execute<TResult>(Expression expression) 
    { 
     return provider.Execute<TResult>(expression); 
    } 

    public object ExecuteFuture(Expression expression) 
    { 
     return provider.Execute(expression); 
    } 

    public void SetResultTransformerAndAdditionalCriteria(IQuery query, NhLinqExpression nhExpression, IDictionary<string, Tuple<object, IType>> parameters) 
    { 
     throw new NotImplementedException(); 
    } 
} 

Düzenleme:: ben tür sorunu anladım

İşte benim uygulaması var. expression için yapılan argümanlardan biri, benim özel sipariş vermem. Bu ifade sağlayıcı tarafından yürütüldüğünde, CreateQuery ve Execute arasında sonsuz bir çağrı döngüsüne neden olur. Bu referansa göre sarf edilenler için tüm referanslarımı rücu edilebilir şekilde değiştirebilirmiyim?

+0

Bunu hiç anladın mı? –

+0

Hayır, sonunda, üretim kodunda NH'nin “ToFuture” ve test kodunda “ToList” olarak adlandırılan kendi “ToFuture” ı yarattım. Ama benim tahminim, parametreleri el ile değiştirmek için bir ifade ziyaretçisi yazmanız gerektiğidir. Bu benim bir sonraki denememdi. –

+0

@CallumBradbury hala ihtiyacınız varsa, bence alay etmeyi başardım. Cevabı kontrol et. –

cevap

3

Bir süre sonra başka bir denemeye karar verdim ve sanırım alay etmeyi başardım. Bunu gerçek senaryolarla test etmedim, ancak pek çok değişikliğin gerekli olacağını düşünmüyorum. Bu kodun çoğu ya this tutorial'dan alınmıştır. Bu sorgularla uğraşırken IEnumerable ile ilgili bazı uyarılar vardır. ToFuture kullanırken nolu NHibernate asserts the type kullanılmalıdır.

public class NHibernateQueryableProxy<T> : QueryableBase<T> 
{ 
    public NHibernateQueryableProxy(IQueryable<T> data) : base(new NhQueryProviderProxy<T>(data)) 
    { 
    } 

    public NHibernateQueryableProxy(IQueryProvider provider, Expression expression) : base(provider, expression) 
    { 
    } 
} 

Şimdi o LINQ sorguları bağlıdır ne yana QueryProvider alay gerekiyor ve bunun ToFuture çünkü() ayrıca uses itINhQueryProvider uygulaması gerekmektedir.

public class NhQueryProviderProxy<T> : INhQueryProvider 
{ 
    private readonly IQueryable<T> _data; 

    public NhQueryProviderProxy(IQueryable<T> data) 
    { 
     _data = data; 
    } 

    // These two CreateQuery methods get called by LINQ extension methods to build up the query 
    // and by ToFuture to return a queried collection and allow us to apply more filters 
    public IQueryable CreateQuery(Expression expression) 
    { 
     Type elementType = TypeSystem.GetElementType(expression.Type); 

     return (IQueryable)Activator.CreateInstance(typeof(NHibernateQueryableProxy<>) 
            .MakeGenericType(elementType), new object[] { this, expression }); 
    } 

    public IQueryable<TElement> CreateQuery<TElement>(Expression expression) 
    { 
     return new NHibernateQueryableProxy<TElement>(this, expression); 
    } 

    // Those two Execute methods are called by terminal methods like .ToList() and .ToArray() 
    public object Execute(Expression expression) 
    { 
     return ExecuteInMemoryQuery(expression, false); 
    } 

    public TResult Execute<TResult>(Expression expression) 
    { 
     bool IsEnumerable = typeof(TResult).Name == "IEnumerable`1"; 
     return (TResult)ExecuteInMemoryQuery(expression, IsEnumerable); 
    } 

    public object ExecuteFuture(Expression expression) 
    { 
     // Here we need to return a NhQueryProviderProxy so we can add more queries 
     // to the queryable and use another ToFuture if desired 
     return CreateQuery(expression); 
    } 

    private object ExecuteInMemoryQuery(Expression expression, bool isEnumerable) 
    { 
     var newExpr = new ExpressionTreeModifier<T>(_data).Visit(expression); 

     if (isEnumerable) 
     { 
      return _data.Provider.CreateQuery(newExpr); 
     } 

     return _data.Provider.Execute(newExpr); 
    } 

    public void SetResultTransformerAndAdditionalCriteria(IQuery query, NhLinqExpression nhExpression, IDictionary<string, Tuple<object, IType>> parameters) 
    { 
     throw new NotImplementedException(); 
    } 
} 

ifade ağacı ziyaretçi bizim için sorgu türünü değişecek: tipini almak için

internal class ExpressionTreeModifier<T> : ExpressionVisitor 
{ 
    private IQueryable<T> _queryableData; 

    internal ExpressionTreeModifier(IQueryable<T> queryableData) 
    { 
     _queryableData = queryableData; 
    } 

    protected override Expression VisitConstant(ConstantExpression c) 
    { 
     // Here the magic happens: the expression types are all NHibernateQueryableProxy, 
     // so we replace them by the correct ones 
     if (c.Type == typeof(NHibernateQueryableProxy<T>)) 
      return Expression.Constant(_queryableData); 
     else 
      return c; 
    } 
} 

Ve biz de (öğretici alınan) bir yardımcıya ihtiyacınız sorgulanan:

var arr = new NHibernateQueryableProxy<int>(Enumerable.Range(1, 10000).AsQueryable()); 

var fluentQuery = arr.Where(x => x > 1 && x < 4321443) 
      .Take(1000) 
      .Skip(3) 
      .Union(new[] { 4235, 24543, 52 }) 
      .GroupBy(x => x.ToString().Length) 
      .ToFuture() 
      .ToList(); 

var linqQuery = (from n in arr 
        where n > 40 && n < 50 
        select n.ToString()) 
        .ToFuture() 
        .ToList(); 
:

internal static class TypeSystem 
{ 
    internal static Type GetElementType(Type seqType) 
    { 
     Type ienum = FindIEnumerable(seqType); 
     if (ienum == null) return seqType; 
     return ienum.GetGenericArguments()[0]; 
    } 

    private static Type FindIEnumerable(Type seqType) 
    { 
     if (seqType == null || seqType == typeof(string)) 
      return null; 

     if (seqType.IsArray) 
      return typeof(IEnumerable<>).MakeGenericType(seqType.GetElementType()); 

     if (seqType.IsGenericType) 
     { 
      foreach (Type arg in seqType.GetGenericArguments()) 
      { 
       Type ienum = typeof(IEnumerable<>).MakeGenericType(arg); 
       if (ienum.IsAssignableFrom(seqType)) 
       { 
        return ienum; 
       } 
      } 
     } 

     Type[] ifaces = seqType.GetInterfaces(); 
     if (ifaces != null && ifaces.Length > 0) 
     { 
      foreach (Type iface in ifaces) 
      { 
       Type ienum = FindIEnumerable(iface); 
       if (ienum != null) return ienum; 
      } 
     } 

     if (seqType.BaseType != null && seqType.BaseType != typeof(object)) 
     { 
      return FindIEnumerable(seqType.BaseType); 
     } 

     return null; 
    } 
} 
yukarıdaki kodu test etmek için, aşağıdaki pasajı ran

Dediğim gibi, karmaşık senaryolar test edilmedi ancak sanırım gerçek dünya kullanımları için sadece birkaç ince ayar yapılması gerekecek.

+0

Vay, harika iş! Bir dahaki sefere bir şansım olsun diye birşeylerimi uygulamaya çalışacağım –

+0

Geri dönüp bunu uygulamaya çalışıyorum ve harika çalışıyor! ToFutureValue ile bazı sorunlar vardı ve bununla başa çıkmak için IFutureValue özel bir uygulama yazmak zorunda kaldı, ama bunun dışında bir çekicilik gibi çalışıyor :) –

+0

@CallumBradbury bana bildirdiğiniz için teşekkürler! İsterseniz, yaptığınız uygulamayı içerecek şekilde çözümü genişletebilirim. –

İlgili konular