缓存Linq查询 - 这可能吗?

时间:2013-02-15 19:56:11

标签: c# linq caching iterator deferred-execution

所以,这似乎是一个边缘情况,但我只是想知道这是否可能。我所拥有的是包含在IEnumerable中的静态集和延迟集的组合 - 例如:

public IEnumerable<T> MakeMyQuery<T>()
{
    // returns a List<T>
    var someStaticData = LoadMyStaticDataFromDatabase();

    // Returns IEnumerable<T>, but from another resource
    var deferredQuery = CreateADeferredQueryUsingYieldReturn(); 

    return someStaticData.Concat(deferredQuery);
}

所以这里发生的事情是,当我在我的枚举上调用.Take(someNumber)时,它会在尝试评估延迟组件之前首先从静态数据返回元素 - 实际上,我已经“隐藏”了一些可能耗时的生成可枚举背后的任务,如果我从不需要获取这些元素,它们实际上永远不会被评估,因为LINQ的延迟性质。

但是,我不认为可以缓存此查询以供以后使用(我不相信Iterator的状态将保留在缓存中,对吧?)或者是否有一种简单的方法可以在不枚举的情况下执行此操作保存结果?

理想情况下,我的流程就是这样:

public List<T> SomeMethod<T>(int numberOfGuys)
{
     IEnumerable<T> query = null;

     if(// Is in Cache)
       query = Cache["MyQuery"];
     else
     {
         query = MakeMyQuery();
         Cache["MyQuery"] = query;
     }

     return query.Take(numberOfGuys).ToList();
}

因此,我可以反复使用相同的查询来请求数据,但可能永远不必重新查询数据库。有没有办法做到这一点?

5 个答案:

答案 0 :(得分:2)

我认为您希望将query.Take(numberOfGuys).ToList()的结果缓存到新列表中。在调用MakeMyQuery()之前,您可以查看缓存列表中的元素数量(如果存在),如果缓存列表中的元素数量大于或等于numberOfGuys,那么您将返回来自缓存列表的numberOfGuys。否则,您将使用query.Take(numberOfGuys).ToList()的新结果替换缓存列表。

正如default.krammer指出的那样,你可能真正想要缓存的是LoadMyStaticDataFromDatabase()的结果,因为如果numberOfGuys总是小于LoadMyStaticDataFromDatabase(),你最终会遇到数据库重复,直到numberOfGuys大于LoadMyStaticDataFromDatabase()返回的数字。因此,您可以在LoadMyStaticDataFromDatabase()方法中对MakeMyQuery<T>()进行缓存,并在query.Take(numberOfGuys).ToList()中缓存SomeMethod<T>(int numberOfGuys),这样您只需点击一次数据库,但仍然需要延迟执行IEnumerable<T>

的优势

答案 1 :(得分:0)

我知道这可能有点老式,但你可能会从数据库填充ADO.NET DataSet,因为DataSet和ADO.NET层的DataTable是断开连接的层。然后,应用程序可以将数据集保存在内存中一段确定的时间。然后你可以将它发回数据库。创建你的数据集,从实体,ADO.NET连接层或Linq到SQL层填充它,它存在填充,你可以根据需要进一步填充新数据,然后将它在最终查询中比较回数据库进行合并变化。

我知道我曾经做过一个项目,我将Linq,ADO.NET和xml序列化混合在一起,基本上将数据从ADO.NET序列化为带有内置xml序列化的ADO.NET的xml文件。然后使用Linq to XML读取它。它与你所说的类似,因为XML文件本质上是以文件格式存储的缓存,我只是通过计算代表数据库中键值的不同元素来更新它。如果它的计数已关闭它更新,否则它保持不变。这不适用于数百万行的大型集合,但对于我希望始终可以访问它的小东西,这很好并且非常快。

我知道在关于.NET 4.0数据访问的70-516 MS Press书中,如果你能在网上找到它,那么在本书末尾有一个关于缓存的实验室。它基本上以数据库为目标,收集自上次以来的更改,然后进行更改,然后在最后进行合并。这样你就可以使用不同的差异,这种差异在内存中更小但跟踪你的工作变化。

答案 2 :(得分:0)

也许我不完全理解你的问题。如果是这样,请告诉我,我会重新制定我的答案。

我相信你所写的内容已经按照你的意愿行事了。请考虑以下玩具示例(类似于您显示的代码)。我没有测试过,但您应该看到的是,如果您Take少于4件商品,则永远不会输入SuperExpensiveQuery

static IEnumerable<int> SuperExpensiveQuery()
{
    Console.WriteLine("super expensive query (#1)");
    yield return 100;
    Console.WriteLine("super expensive query (#2)");
    yield return 200;
    Console.WriteLine("super expensive query (#3)");
    yield return 300;
    Console.WriteLine("super expensive query (#4)");
}

static IEnumerable<int> MakeMyQuery()
{
    var someStaticData = new int[] { 1, 2, 3 };
    var deferredQuery = SuperExpensiveQuery();
    return someStaticData.Concat(deferredQuery);
}

static void Test()
{
    var query = MakeMyQuery();
    for (int i = 0; i <= 7; i++)
    {
        Console.WriteLine("BEGIN Take({0})", i);
        foreach (var n in query.Take(i))
            Console.WriteLine("    {0}", n);
        Console.WriteLine("END Take({0})", i);
    }
    Console.ReadLine();
}

答案 3 :(得分:0)

我的一个项目中有类似的要求。我最终做的是创建一个DataAccesLayer(DAL)缓存基础,我在DAL中为每个组件继承。我有一个单独的缓存类来保存缓存。请注意,我的所有对象都有ID和Name。您可以根据需要定制基类。

DAL基类:

public abstract class DALBaseCache<T>
    {
        public List<T> ItemList
        {
            get
            {
                List<T> itemList = DALCache.GetItem<List<T>>(typeof(T).Name + "Cache");

                if (itemList != null)
                    return itemList;
                else
                {
                    itemList = GetItemList();
                    DALCache.SetItem(typeof(T).Name + "Cache", itemList);
                    return itemList;
                }
            }
        }

        /// <summary>
        /// Get a list of all the Items
        /// </summary>
        /// <returns></returns>
        protected abstract List<T> GetItemList();

        /// <summary>
        /// Get the Item based on the ID
        /// </summary>
        /// <param name="name">ID of the Item to retrieve</param>
        /// <returns>The Item with the given ID</returns>
        public T GetItem(int id)
        {
            return (from item in ItemList
                    where (int)item.GetType().GetProperty("ID").GetValue(item, null) == id
                    select item).SingleOrDefault();
        }

        /// <summary>
        /// Get the Item based on the Name
        /// </summary>
        /// <param name="name">Name of the Item to retrieve</param>
        /// <returns>The Item with the given Name</returns>
        public T GetItem(string name)
        {
            return (from item in ItemList
                    where (string)item.GetType().GetProperty("Name").GetValue(item, null) == name
                    select item).SingleOrDefault();
        }
    }

然后我的缓存类基本上包含我的查询字典

public static class DALCache
{
    static Dictionary<string, object> _AppCache = new Dictionary<string, object>();

    public static T GetItem<T>(string key)
    {
        if(_AppCache.ContainsKey(key))
        {
            return (T) _AppCache[key];
        }
        else
        {
            return default(T);
        }
    }

    public static void SetItem(string key, object obj)
    {
        _AppCache.Add(key, obj);
    }
}

最后是一个带缓存List的实现。我使用EF获取我的CustomerType列表并在应用程序生命周期的剩余时间内缓存它。您可以根据需要进行更改。

public class CustomerTypeDAL: DALBaseCache<CustomerType>
{
    protected override List<CustomerType> GetItemList()
    {
        DBEntities entities = new DBEntities();
        return Mapper.Map <List<CustomerType>>(entities.GetAllCustomerTypes().ToList());
    }
}

您的代码中的任何位置都可以将其用作:

CustomerTypeDAL customerTypeDAL = new CustomerTypeDAL();
List<CustomerType> custTypes = customerTypeDAL.ItemList;

第一次调用它时,它会从数据库中获取它。之后它会在缓存后继续。

答案 4 :(得分:0)

是的,如果您在迭代时缓存,则可以。

看起来像这样:

var lazyList = MakeMyQuery<int>().ToLazyList();
var list1 = lazyList.Take(2).Sum();
var list2 = lazyList.Take(3).Sum();
var list3 = lazyList.Take(1).Sum();

在这种情况下: f

  • 前3个项目仅从MakeMyQuery中获得1次。
  • 第四项尚未屈服。

implementation of a lazy list is here的一个例子。