6

所以,这可能看起来像一个边缘案例,但我只是想知道这是否可能。我所拥有的是一个静态集和延迟集的组合,包裹在一个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 的延迟性质,它们实际上从未得到评估。

但是,我认为不可能缓存此查询以供以后使用(我不相信迭代器的状态将保留在缓存中,对吗?)或者是否有一种简单的方法可以在不枚举结果的情况下做到这一点节省?

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

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();
}

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

4

5 回答 5

2

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

正如 default.krammer 指出的那样,您可能真正想要缓存的是结果,LoadMyStaticDataFromDatabase()因为 ifnumberOfGuys总是小于LoadMyStaticDataFromDatabase(),您最终会反复访问数据库,直到numberOfGuys大于返回的数字LoadMyStaticDataFromDatabase()。因此,您可以LoadMyStaticDataFromDatabase()MakeMyQuery<T>()方法query.Take(numberOfGuys).ToList()中的缓存SomeMethod<T>(int numberOfGuys)IEnumerable<T>.

于 2013-02-15T20:58:31.547 回答
0

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

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

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

于 2013-02-15T20:16:11.797 回答
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();
}
于 2013-02-15T21:04:38.377 回答
0

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

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);
    }
}

最后是一个带有缓存列表的实现。我使用 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;

第一次调用它时,它会从 DB 中获取它。之后,它将在缓存之后。

于 2013-02-15T21:41:34.877 回答
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 次。
  • 第四个项目没有产出。

一个惰性列表的实现示例在这里

于 2013-10-12T20:42:53.987 回答