1

ASP.NET MVC プロジェクトを作成しており、リポジトリ (LINQ2SQL) レイヤーでビジネス データ キャッシュを実装したいと考えています。エンティティは互いに関連しているため、基本エンティティを無効にするときに、関連するエンティティを無効にする必要があります。ブログ/投稿/コメントの関係があり、ユーザーが新しいコメントを作成すると、TotalComments フィールドが古くなっているため、キャッシュされた Post エンティティを無効にする必要があるとします。他のエンティティを無効にするためのより複雑なロジックがある場合があります。
そのためには、柔軟な無効化メカニズムを実装する必要があります。以前に見つけたもの:
- 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 Notification Services を使用することは、私には後ろ向きに思えます。サーバー側の Web アプリケーションは、ユーザーにとって最初のエントリ ポイントです。データベースはその後です。MVC アプリケーションでキャッシュをクリアする必要がある場合はわかっているので、そこからキャッシュをクリアしてみませんか?

編集:

データをキャッシュに保存し、キーを介してアクセスするためのもう 1 つのオプション (キャッシュ コレクション全体を反復処理する必要はありません)。

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

value は投稿List<Comment>postIdID です。このようにして、コメント コレクションを簡単に検索でき、キャッシュ キーは動的です。私は多くのアプリケーションでこのアプローチを使用してきました (実際には、コードの重複を少なくするために、より一般的な方法で記述します)。

于 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 に答える