You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core中MemoryCache获取对象副本的实现方案求助

Fixing the MemoryCache Reference Issue in Your ASP.NET Core Web App

Got it, I totally get why this is frustrating—when you pull data from MemoryCache, you're getting a direct reference to the cached object, so any changes you make (like toggling that Active status) end up messing with the original cached materialized view data. Let's walk through the best solutions tailored to your business scenario, where you need to keep the cached source intact while modifying copies for API requests.

1. Return a Deep Copy Every Time You Fetch from Cache

Since your cached data is a collection of reference-type objects (your product parameter entities), a shallow copy (like calling ToList() or ToArray()) won't cut it—it only copies the collection's reference, not the individual objects inside. You need a deep copy to fully isolate the cached original from the working copy you modify.

Implementation Options:

Option A: Serialization/Deserialization (Quick & Universal)

This works for almost any object type without modifying your entity classes:

// Fetch the original cached data
var cachedMvData = _memoryCache.Get<List<ProductParameter>>("MaterializedView_Products");

// Create a deep copy using System.Text.Json
var workingCopy = JsonSerializer.Deserialize<List<ProductParameter>>(
    JsonSerializer.Serialize(cachedMvData)
);

// Now you can safely filter and update the Active status without touching the cached original
var processedData = workingCopy
    .Where(p => p.link == "/product_group1/product_group_2")
    .Select(p => 
    {
        // Apply your filter rules here
        bool meetsFilter = p.FieldName switch
        {
            "money" => p.Value == "1200",
            "rating" => double.Parse(p.Value) >= 1.5 && double.Parse(p.Value) <= 3,
            _ => true
        };
        return p with { Active = meetsFilter };
    })
    .ToList();

If you prefer Newtonsoft.Json, the logic is nearly identical—swap out JsonSerializer with JsonConvert.

Option B: Custom Clone Method (Better Performance for Complex Objects)

If serialization feels too slow for your data size, add a clone method to your entity class:

public class ProductParameter
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Value { get; set; }
    public string Type { get; set; }
    public string FieldName { get; set; }
    public bool Active { get; set; } = true; // Default to active as per your scenario

    // Deep clone all properties (recurse if you have nested reference types)
    public ProductParameter Clone()
    {
        return new ProductParameter
        {
            Id = this.Id,
            Name = this.Name,
            Value = this.Value,
            Type = this.Type,
            FieldName = this.FieldName,
            Active = this.Active // Preserve original active state from cache
        };
    }
}

// Use it to create a working copy
var cachedMvData = _memoryCache.Get<List<ProductParameter>>("MaterializedView_Products");
var workingCopy = cachedMvData.Select(p => p.Clone()).ToList();

2. Use Immutable Objects to Avoid Accidental Modification

If your workflow allows, design your entity as an immutable type (like a C# record). With immutability, you can't modify the original cached object—any changes create a new instance instead:

// Define your parameter as an immutable record
public record ProductParameter(
    int Id,
    string Name,
    string Value,
    string Type,
    string FieldName,
    bool Active = true
);

// When processing, create new instances instead of modifying the cached ones
var cachedMvData = _memoryCache.Get<List<ProductParameter>>("MaterializedView_Products");
var processedData = cachedMvData
    .Where(p => p.link == "/product_group1/product_group_2")
    .Select(p => 
    {
        bool meetsFilter = /* your filter logic here */;
        return p with { Active = meetsFilter }; // Creates a new record instance
    })
    .ToList();

This eliminates the need for copying entirely, as the cached data remains untouched by design.

3. Wrap Cache Access in a Service (Clean & Reusable)

To avoid repeating copy logic across your codebase, create a dedicated service that handles fetching the cached materialized view and returning a copy automatically:

public class MaterializedViewCacheService
{
    private readonly IMemoryCache _memoryCache;
    private readonly YourDbContext _dbContext;

    public MaterializedViewCacheService(IMemoryCache memoryCache, YourDbContext dbContext)
    {
        _memoryCache = memoryCache;
        _dbContext = dbContext;
    }

    public async Task<List<ProductParameter>> GetFreshCopyAsync()
    {
        const string cacheKey = "MaterializedView_Products";
        
        // Check cache first
        if (_memoryCache.TryGetValue(cacheKey, out List<ProductParameter> cachedData))
        {
            // Return a deep copy of the cached data
            return JsonSerializer.Deserialize<List<ProductParameter>>(
                JsonSerializer.Serialize(cachedData)
            );
        }

        // Load from database if cache is empty
        var dbData = await _dbContext.ProductParameters
            .FromSqlRaw("SELECT * FROM Your_Materialized_View")
            .ToListAsync();

        // Cache the original, unmodified data
        _memoryCache.Set(cacheKey, dbData, new MemoryCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1) // Adjust expiry as needed
        });

        // Return a copy of the fresh database data
        return JsonSerializer.Deserialize<List<ProductParameter>>(
            JsonSerializer.Serialize(dbData)
        );
    }
}

Now any part of your app can call GetFreshCopyAsync() and get a clean, unmodified copy of the materialized view every time.

Do You Need a Different Cache Library?

Nope—Microsoft.Extensions.Caching.Memory is already the official, recommended in-memory cache for ASP.NET Core. The issue isn't the cache itself, but how you handle object references. The solutions above will fix your problem without switching libraries.

内容的提问来源于stack exchange,提问作者Jan Sršeň

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 14:52:28