1

我正在制作一个 ASP.NET MVC 项目,我想在存储库(LINQ2SQL)层实现业务数据缓存。由于实体彼此相关,因此当我使某些基本实体无效时,我需要使相关实体无效。假设我有 Blog/Post/Comments 关系,当用户发表新评论时,我需要使缓存的 Post 实体无效,因为它已经过时 TotalComments 字段。有时,使其他实体失效的逻辑更为复杂。
好吧,我需要为此实施灵活的失效机制。我之前发现的:
- SQL 通知服务。每次时间表发生更改时,它都会通知应用程序。由于我将拥有高负载的应用程序,因此某些表的更改会非常频繁。每次添加新评论时,任何帖子的所有缓存评论都会删除。
- 缓存 LINQ(或 SQL)查询。在这种情况下,呈现的查询使用其哈希作为键放置到缓存中。不错,但不可能删除“所有具有 BlogPostId = deletedBlogPostId 的评论实体”

现在我的想法是什么。我想对 HttpRuntime.Cache 项目使用 LINQ 查询来查找要通过其属性删除的项目(例如,在删除我查找的博客文章的情况下

cachedItem => cachedItem.GetType() == typeof(Comment) 
       && ((Comment)cachedItem).BlogPostId == deletedBlogPostId

删除所有相关评论)。但我在谷歌中找不到这种方法被广泛使用。这不是使用缓存的相关实体进行操作的好方法吗?在我的笔记本上,超过 100 万个缓存项目的查询性能为 600 毫秒。
谢谢!

4

2 回答 2

1

我会在你的控制器中为相应的对象调用一个缓存清除方法。例如,如果用户正在编辑单个帖子,那么在处理该 POST 请求的控制器方法中,您应该清除该帖子的缓存。

使用 SQL Server 通知服务对我来说似乎是倒退的。您的服务器端 Web 应用程序是用户的第一个入口点;数据库紧随其后。您知道何时需要清除 MVC 应用程序中的缓存,那么为什么不从那里清除缓存呢?

编辑:

将数据存储在缓存中并通过密钥访问它的另一种选择(因此您不必遍历整个缓存集合):

HttpRuntime.Cache.Insert(string.Format("CommentsForPost-{0}", postId), value);

其中 value 是 aList<Comment>并且postId是您的帖子的 id。通过这种方式,您可以轻松查找您的评论集合,并且您的缓存键是动态的。我已经在许多应用程序中使用了这种方法(尽管我实际上会将它写得更通用,以减少代码重复)。

于 2012-06-04T13:53:05.720 回答
0

好吧,过了一会儿,我制作了访问缓存的扩展方法:

public static class MyExtensions
{
    // temlplate for cache item key name
    private const string CacheKeyTemplate = "{0}_{1}";

    private static string GetCachePrefixByType(Type type)
    {
        // this is just sample, implement it any way you want
        switch (type.Name)
        {
            case "Blog": return "b"; break;
            case "BlogPost": return "bp"; break;
            case "Comment": return "c"; break;
            default: throw new NotImplementedException();
        }
    }

    // insert with key containing object type custom prefix and object id
    public static void Put(this Cache cache, object obj, long id)
    {
        cache.Insert(String.Format(CacheKeyTemplate, GetCachePrefixByType(obj.GetType()), id), obj);
    }

    // get by object type and id
    public static T Get<T>(this Cache cache, long id)
    {
        return (T)cache[String.Format(CacheKeyTemplate, GetCachePrefixByType(typeof(T)), id)];
    }

    // get objects by WHERE expression
    public static List<object> Get(this Cache cache, Func<object, bool> f)
    {
        return cache.Cast<DictionaryEntry>().Select(e => e.Value).Where(f).ToList();
    }

    // remove by object type and id
    public static void Remove<T>(this Cache cache, long id)
    {
        cache.Remove(String.Format(CacheKeyTemplate, GetCachePrefixByType(typeof(T)), id));
    }

    // remove cache items by WHERE expression against stored objects
    public static void Remove(this Cache cache, Func<object, bool> f)
    {
        foreach (string key in cache.Cast<DictionaryEntry>().Where(de => f.Invoke(de.Value)).Select(de => de.Key))
        {
            cache.Remove(key);
        }
    }
}

这是我要测试的课程:

private class Blog
{
    public int BlogId { get; set; }
    public string Name { get; set; }
}

private class BlogPost
{
    public int PostId { get; set; }
    public int BlogId { get; set; }
    public string Text { get; set; }
}

private class Comment
{
    public int PostId { get; set; }
    public int CommentId { get; set; }
    public string Text { get; set; }
}

和测试代码本身:

    // a blog
    Blog blog = new Blog{ BlogId = 1, Name = "Jim" };
    // two blog posts
    BlogPost post1 = new BlogPost { PostId = 1, BlogId = 1, Text = "Aaaaaaaa" };
    BlogPost post2 = new BlogPost { PostId = 2, BlogId = 1, Text = "Bbbbbbbbbb" };
    // two comments to the 1st blog post
    Comment comment11 = new Comment { CommentId = 11, PostId = 1, Text = "qwerty" };
    Comment comment12 = new Comment { CommentId = 12, PostId = 1, Text = "asdfg" };
    // one comment to the 2nd blog post
    Comment comment21 = new Comment { CommentId = 21, PostId = 2, Text = "zxcvbn" };

    // put it all to cache
    HttpRuntime.Cache.Put(blog, blog.BlogId);
    HttpRuntime.Cache.Put(post1, post1.PostId);
    HttpRuntime.Cache.Put(post2, post2.PostId);
    HttpRuntime.Cache.Put(comment11, comment11.CommentId);
    HttpRuntime.Cache.Put(comment12, comment12.CommentId);
    HttpRuntime.Cache.Put(comment21, comment21.CommentId);

    // get post #2 by its id
    BlogPost testPost = HttpRuntime.Cache.Get<BlogPost>(2); // testPost.Text = "Bbbbbbbbbb"

    // get all comments for post #1
    IEnumerable<Comment> testComments = HttpRuntime.Cache.Get(
        x => (x is Comment) && ((Comment)x).PostId == 1).Cast<Comment>(); // comments 11 and 12 are in the list

    // remove comment 21
    HttpRuntime.Cache.Remove<Comment>(21);
    // test if it was removed
    comment21 = HttpRuntime.Cache.Get<Comment>(21); // null

    // remove anything having text property = "qwerty"
    HttpRuntime.Cache.Remove(x => x.GetType().GetProperty("Text") != null && ((dynamic)x).Text == "qwerty");
    // test if comment 11 it was removed
    comment11 = HttpRuntime.Cache.Get<Comment>(11); // null

    // but comment 12 should still exist
    comment12 = HttpRuntime.Cache.Get<Comment>(12); // it's there

    // remove anything from cache
    HttpRuntime.Cache.Remove(x => true);
    // cache items count should be zero
    int count = HttpRuntime.Cache.Count; // it is!
于 2012-06-04T17:35:58.653 回答