c# - Redis 是本地缓存的可行解决方案吗?

标签 c# winforms caching asp.net-web-api redis

在我的场景中,我有一个连接到 WebApi2 的 Winforms 客户端。数据存储在 SQL Server 数据库中。

为了提高性能,我正在研究将数据存储在本地缓存中是否是一种可行的解决方案。本地缓存最好存储在文件中,而不是保存在内存中,因为 RAM 可能是个问题。数据都是 POCO 类,有些比其他类复杂得多,并且大多数类彼此相关。

我列出了可能可行的框架:

  1. 内存缓存
  2. 内存缓存
  3. 缓存管理器
  4. StackExchange.Redis
  5. 本地数据库

使用 MemoryCache,我需要实现我自己的解决方案,但它会满足我的初始要求。

但是,我看到的一个常见问题是相关类的更新。例如,我在 CustomerAddressPostCode 之间有一个关系。如果我更改邮政编码对象中的某些属性,我可以轻松更新其本地缓存。但是,如何更新/使使用此邮政编码的任何其他类(在本例中为 CustomerAddress)变得无效?

上述任何框架是否有帮助处理这种情况的方法,还是完全依赖于开发人员来处理此类缓存失效?

最佳答案

CachingFramework.Redis库提供了一种将标签与键和散列相关联的机制,因此您可以在单个操作中使它们无效。

我假设你会:

  • 使用“地址:{AddressId}”等键将客户地址存储在 Redis 中。
  • 使用“PostCode:{PostCodeId}”等键将邮政编码存储在 Redis 中。

你的模型是这样的:

public class CustomerAddress
{
    public int CustomerAddressId { get; set; }
    public int CustomerId { get; set; }
    public int PostCodeId { get; set; }
}
public class PostCode
{
    public int PostCodeId { get; set; }
    public string Code { get; set; }
}

我的建议是:

  • 使用“Tag-PostCode:{PostCodeId}”等标签在 Redis 上标记客户地址对象。
  • 使用 cache-aside pattern从缓存/数据库中检索客户地址和邮政编码。
  • 更改邮政编码时通过标签使缓存对象无效。

像这样的东西应该可以工作:

public class DataAccess
{
    private Context _cacheContext = new CachingFramework.Redis.Context("localhost:6379");

    private string FormatPostCodeKey(int postCodeId)
    {
        return string.Format("PostCode:{0}", postCodeId);
    }

    private string FormatPostCodeTag(int postCodeId)
    {
        return string.Format("Tag-PostCode:{0}", postCodeId);
    }

    private string FormatAddressKey(int customerAddressId)
    {
        return string.Format("Address:{0}", customerAddressId);
    }

    public void InsertPostCode(PostCode postCode)
    {
        Sql.InsertPostCode(postCode);
    }

    public void UpdatePostCode(PostCode postCode)
    {
        Sql.UpdatePostCode(postCode);
        //Invalidate cache: remove CustomerAddresses and PostCode related
        _cacheContext.Cache.InvalidateKeysByTag(FormatPostCodeTag(postCode.PostCodeId));
    }

    public void DeletePostCode(int postCodeId)
    {
        Sql.DeletePostCode(postCodeId);
        _cacheContext.Cache.InvalidateKeysByTag(FormatPostCodeTag(postCodeId));
    }

    public PostCode GetPostCode(int postCodeId)
    {
        // Get/Insert the postcode from/into Cache with key = PostCode{PostCodeId}. 
        // Mark the object with tag = Tag-PostCode:{PostCodeId}
        return _cacheContext.Cache.FetchObject(
            FormatPostCodeKey(postCodeId),              // Redis Key to use
            () => Sql.GetPostCode(postCodeId),          // Delegate to get the value from database
            new[] { FormatPostCodeTag(postCodeId) });   // Tags related
    }

    public void InsertCustomerAddress(CustomerAddress customerAddress)
    {
        Sql.InsertCustomerAddress(customerAddress);
    }

    public void UpdateCustomerAddress(CustomerAddress customerAddress)
    {
        var updated = Sql.UpdateCustomerAddress(customerAddress);
        if (updated.PostCodeId != customerAddress.PostCodeId)
        {
            var addressKey = FormatAddressKey(customerAddress.CustomerAddressId);
            _cacheContext.Cache.RenameTagForKey(addressKey, FormatPostCodeTag(customerAddress.PostCodeId), FormatPostCodeTag(updated.PostCodeId));
        }
    }

    public void DeleteCustomerAddress(CustomerAddress customerAddress)
    {
        Sql.DeleteCustomerAddress(customerAddress.CustomerAddressId);
        //Clean-up, remove the postcode tag from the CustomerAddress:
        _cacheContext.Cache.RemoveTagsFromKey(FormatAddressKey(customerAddress.CustomerAddressId), new [] { FormatPostCodeTag(customerAddress.PostCodeId) });
    }

    public CustomerAddress GetCustomerAddress(int customerAddressId)
    {
        // Get/Insert the address from/into Cache with key = Address:{CustomerAddressId}. 
        // Mark the object with tag = Tag-PostCode:{PostCodeId}
        return _cacheContext.Cache.FetchObject(
            FormatAddressKey(customerAddressId),
            () => Sql.GetCustomerAddress(customerAddressId),
            a => new[] { FormatPostCodeTag(a.PostCodeId) });
    }
}

关于c# - Redis 是本地缓存的可行解决方案吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36655913/

相关文章:

c# - Winforms 以另一种形式创建 2 种形式的实例

c# - 为什么在从字典中读取时锁定

c# - .NET 核心将命令行参数从 Program.cs 传递到 Startup.cs

C# iTextSharp 通过字节数组合并多个 pdf

c# - 如何将 ContextMenuStrip 添加到 ToolStripMenuItem

windows - 如何防止 IIS 7.5 缓存符号链接(symbolic link)内容?

iOS - 禁用 Alamofire 缓存参数

c# - 通过子类型参数 : is it possible? 从类接口(interface)继承方法

c# - session 状态何时准备好在 asp.net 中使用

c# - 如何在 WinForms 中更新 DataGridView