English 中文(简体)
通过 LINQ 表达式到另一个查询程序
原标题:Pass LINQ expression to another QueryProvider

我有一个简单的自定义 Query Provider, 它使用表达式, 将其翻译到 SQL 并查询 sql 数据库 。

我想在QueryProvider中创建一个小缓存, 存储常用访问对象, 这样检索就可以在数据库不被点击的情况下进行 。

查询Provider 拥有该方法

public object Execute(System.Linq.Expressions.Expression expression)
{
    /// Builds an SQL statement from the expression, 
    /// executes it and returns matching objects
}

缓存作为字段在此 Query Provider 类中, 是一个简单的通用列表 。

如果我使用 List.As 可查询的方法, 并将上述表达式传递到 List.As可查询 s sublicer s Provider 执行方法中, 则该表达式不会按要求工作。 它看起来像当一个表达式被编译时, 初始的 Query Provider 变成一个不可分割的部分 。

能否将一个表达方式传递到随后的QueryProvider, 并按要求执行该表达方式?

电话密码看起来含糊不清如下:

public class QueryProvider<Entity>()
{
    private List<TEntity> cache = new List<Entity>();

    public object Execute(System.Linq.Expressions.Expression expression)
    {
        /// check whether expression expects single or multiple result
        bool isSingle = true;

        if (isSingle)
        {
            var result = this.cache.AsQueryable<Entity>().Provider.Execute(expression);
            if (result != null) 
                return result;
        }

        /// cache failed, hit database
        var qt = new QueryTranslator();
        string sql = qt.Translate(expression);
        /// .... hit database
    }
} 

它不返回一个错误, 相反它会被困在循环中, 当同一个供应商被反复调用时 。

这里还有一些代码 显示我想做什么:

收藏 :

class Collection<Entity>
{

    internal List<Entity> cacheOne { get; private set; }
    internal Dictionary<Guid, Entity> cacheTwo { get; private set; }

    internal Collection()
    {
        this.cacheOne = new List<Entity>();
        this.cacheTwo = new Dictionary<Guid, Entity>();
    }

    public IQueryable<Entity> Query()
    {
        return new Query<Entity>(this.cacheOne, this.cacheTwo);
    }

}

查询 :

class Query<Entity> : IQueryable<Entity>
{
    internal Query(List<Entity> cacheOne, Dictionary<Guid, Entity> cacheTwo)
    {
        this.Provider = new QueryProvider<Entity>(cacheOne, cacheTwo);
        this.Expression = Expression.Constant(this);
    }

    internal Query(IQueryProvider provider, Expression expression)
    {
        this.Provider = provider;
        if (expression != null)
            this.Expression = expression;
    }

    public IEnumerator<Entity> GetEnumerator()
    {
        return this.Provider.Execute<IEnumerator<Entity>>(this.Expression);
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }

    public Type ElementType
    {
        get { return typeof(Entity); }
    }

    public System.Linq.Expressions.Expression Expression { get; private set; }

    public IQueryProvider Provider { get; private set; }
}

查询 provider :

class QueryProvider<Entity> : IQueryProvider
{

    private List<Entity> cacheOne;
    private Dictionary<Guid, Entity> cacheTwo;

    internal QueryProvider(List<Entity> cacheOne, Dictionary<Guid, Entity> cacheTwo)
    {
        this.cacheOne = cacheOne;
        this.cacheTwo = cacheTwo;   
    }

    public IQueryable<TElement> CreateQuery<TElement>(System.Linq.Expressions.Expression expression)
    {
        return new Query<TElement>(this, expression);
    }

    public IQueryable CreateQuery(System.Linq.Expressions.Expression expression)
    {
        throw new NotImplementedException();
    }

    public TResult Execute<TResult>(System.Linq.Expressions.Expression expression)
    {
        return (TResult)this.Execute(expression);
    }

    public object Execute(System.Linq.Expressions.Expression expression)
    {
        Iterator<Entity> iterator = new Iterator<Entity>(expression, cacheOne, cacheTwo);
        return (iterator as IEnumerable<Entity>).GetEnumerator();
    }
}

迭代 :

class Iterator<Entity> : IEnumerable<Entity>
{
    private Expression expression;
    private List<Entity> cacheOne;
    private Dictionary<Guid, Entity> cacheTwo;

    internal Iterator(Expression expression, List<Entity> cacheOne, Dictionary<Guid, Entity> cacheTwo)
    {
        this.expression = expression;
        this.cacheOne = cacheOne;
        this.cacheTwo = cacheTwo;
    }

    public IEnumerator<Entity> GetEnumerator()
    {
        foreach (var result in (IEnumerable<Entity>)this.cacheOne.AsQueryable<Entity>().Provider.Execute(expression))
        {
            yield return result;
        }

        foreach (var more in (IEnumerable<Entity>)this.cacheTwo.Values.AsQueryable<Entity>().Provider.Execute(expression))
        {
            yield return more;
        }
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

方案:

class Program
{
    static void Main(string[] args)
    {
        /// Create collection + caches
        var collection = new Collection<Giraffe>();
        collection.cacheOne.AddRange(new Giraffe[] {
            new Giraffe() { Id = Guid.NewGuid(), DateOfBirth = new DateTime(2011, 03, 21), Height = 192, Name = "Percy" },
            new Giraffe() { Id = Guid.NewGuid(), DateOfBirth = new DateTime(2005, 12, 25), Height = 188, Name = "Santa" },
            new Giraffe() { Id = Guid.NewGuid(), DateOfBirth = new DateTime(1999, 04, 01), Height=144, Name="Clown" }
        });
        var cachetwo = new List<Giraffe>(new Giraffe[] {
            new Giraffe() { Id = Guid.NewGuid(), DateOfBirth = new DateTime(1980, 03,03), Height = 599, Name="Big Ears" },
            new Giraffe() { Id = Guid.NewGuid(), DateOfBirth = new DateTime(1985, 04, 02), Height= 209, Name="Pug" }
        });
        foreach (var giraffe in cachetwo)
            collection.cacheTwo.Add(giraffe.Id, giraffe);

        /// Iterate through giraffes born before a certain date
        foreach (var result in collection.Query().Where(T => T.DateOfBirth < new DateTime(2006, 01, 01)))
        {
            Console.WriteLine(result.Name);
        }

    }
}

希拉菲:

class Giraffe
{
    public Guid Id { get; set; }
    public string Name { get; set;  }
    public long Height { get; set; }
    public DateTime DateOfBirth { get; set; }
}

特殊案例,如单和默认等,被遗漏。我想工作的部分发生于试音器中,它首先执行列表 s Query Provider,然后执行字典 。

两个可查询对象之一可能是数据库或其他东西。

最佳回答

否, 查询不会被连接到一个提供者。 这就是为什么您有 I 查询界面的原因: 它既提供表达式, 也提供提供提供者, 所以 LINQ 可以调用提供者执行表达式 。

执行中的问题在于 _uery< Entity> 代表自身的方式: 您重新将根表达式设置为 Expression. Constant( this) , 其中 > this query (不是收藏) 。

所以,当您用 LINQ 到 Objects 执行查询时, 它会调用 GetEnumberat或 on >, 然后调用 LINQ 到Objects 执行 Expression , 它有根表达式 Expression.Constant (类型为 ), 以及 LINQ -to- Objects, 然后调用 getEcodest或 在 < Codeçuery< & gt; 等来补充这个根表达式表达式 。

问题在于

(IEnumerable<Entity>)this.cacheOne.AsQueryable<Entity>().Provider.Execute(expression)

基本上等于

new Entity[0].AsQueryable().Provider.Execute(expression)

linqToObjectsProvider.Execute(expression)

查询返回的提供方是 non 与源链接到源( thiss.cacheone ),所以您只是重新执行表达式,而不是在缓存上查询。

以下内容有什么错?

class Collection<Entity>
{
    ...

    public IQueryable<Entity> Query()
    {
        return this.cacheOne.Concat(this.cacheTwo.Values).AsQueryable();
    }
}

Note that Concat uses delayed evaluation, so only when you execute the query are cacheOne and cacheTwo concatenated and then manipulated using the additional LINQ operat或s.

(在这种情况下,我将 collection< Entity> 制作为可查询的 代码 Expression 等同于 Expression.Constant( this.cacheone.Concat( this.cache2.Values)) '. 我想你可以取消所有其他类别 。)


答复 < /强 >

然而,我认为这样把LINQ 搭载到Objects 将永远无法做你认为应该做的事。

At the very least, you should keep the 或iginal query provider so you can call that one when you have a cache miss. If you don t, and use your own query provider (you did not show the code you are using to do the actual call), your query provider one will call itself again, and again.

所以您需要创建一个 Caching Query Provider Caching Query :

class CachingQuery<T> : IQueryable<T>
{
    private readonly CachingQueryProvider _provider;
    private readonly Expression _expression;

    public CachingQuery(CachingQueryProvider provider, Expression expression)
    {
        _provider = provider;
        _expression = expression;
    }

    // etc.
}

class CachingQueryProvider : IQueryProvider
{
    private readonly IQueryProvider _或iginal;

    public CachingQueryProvider(IQueryProvider 或iginal)
    {
        _或iginal = 或iginal;
    }

    // etc.
}

public static class CachedQueryable
{
    public static IQuerable<T> AsCached(this IQueryable<T> source)
    {
        return new CachingQuery<T>(
             new CachingQueryProvider(source.Provider), 
             source.Expression);
    }
}

Also if you want to cache a result, you ll need to materialize the result bef或e you cache it, otherwise you cache the query, not the result. And the result itself should never be executed again, as it is already the data you should return.

我前进的方向如下:

class CachingQueryProvider : IQueryProvider
{
    public object Execute(Expression expression)
    {
        var key = TranslateExpressionToCacheKey(expression);

        object cachedValue;
        if (_cache.TryGetValue(key, out cachedValue))
            return cachedValue;

        object result = _或iginalProvider.Execute(expression);

        // Won t compile because we don t know T at compile time
        IEnumerable<T> sequence = result as IEnumerable<T>;
        if (sequence != null && !(sequence is ICollection<T>)) 
        {
            result = sequence.ToList<T>();
        }

        _cache[key] = result; 

        return result;
    }
}

F或 the part marked as Won t compile, you ll have to do some reflection trickery.

提醒: 字符串执行 IEO 数不胜数, 所以要小心 < em> 而不是 试图实现单个字符串结果值 。

问题回答

暂无回答




相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...