Func<string> DoSomething:类型 [...] Controller+<>c__DisplayClass0_0 未标记为可序列化

Func<string> DoSomething: Type [...] Controller+<>c__DisplayClass0_0 is not marked as serializable

我正在尝试实现基于 ObjectCache 的 FileCache (https://github.com/acarteas/FileCache)。 我正在尝试检查缓存对象是否存在,或者在需要时添加它 return。但是,当对象不存在时,不会执行委托并抛出错误:Type 'myNamespace.Controllers.ListController+<>c__DisplayClass0_0' in [...] is not marked as serializable.

我尝试过的(简化):

private string generateString(int? Id)
{ 
    return "String";
}

public ActionResult GetSomething(int? Id)
{
    var cacheFilePath = $"{AppDomain.CurrentDomain.BaseDirectory}{"\cache"}";
    var cache = new FileCache(cacheFilePath, false, TimeSpan.FromSeconds(30));
    if (purgeCache)
        cache.Remove($"CacheKey{Id}");

    Func<string> createString = () => generateString(Id);

    var myString = (string) cache.AddOrGetExisting($"CacheKey{Id}", createString, new CacheItemPolicy() { AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(30) });
    return new JsonStringResult(myString);

}

好的,现在我已经尝试用 Serializable 指定委托 createString,但这不起作用。 有人能指出我正确的方向吗?

基本上我想要: - 运行 声明 return 是 generateString(123) 的先前输出;如果它不存在或已过期,它应该重新生成它。

感谢任何提示!

AddOrGetExisting 的签名说第二个参数是 object value 而不是回调委托:

https://github.com/acarteas/FileCache/blob/master/src/FileCache/FileCache.cs

public override object AddOrGetExisting(string key, object value, CacheItemPolicy policy, string regionName = null)

我想你只是想要这个(我也纠正了你代码中的其他潜在问题):

public ActionResult GetSomething(int? id)
{
    String cacheFilePath = Path.Combine( AppDomain.CurrentDomain.BaseDirectory, "Cache" );
    FileCache cache = new FileCache( cacheFilePath, false, TimeSpan.FromSeconds(30) );

    String cacheKey = String.Format( CultureInfo.InvariantCulture, "CacheKey{0}", id );
    if( purgeCache ) cache.Remove( cacheKey );

    String valueString = this.GenerateString( id );

    String myString = (String)cache.AddOrGetExisting( cacheKey, valueString, new CacheItemPolicy() { AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(30) });
    return new JsonStringResult( myString );

}
  1. C# 内插字符串 $"like {this}" 不适合在 UI 代码之外使用,因为它们会自动使用 CultureInfo.CurrentCulture,这会导致输出不一致,具体取决于 ASP.NET 自动设置为访问者浏览器的 Accept-Language header 值。最好改用显式 String.Format( CultureInfo.InvariantCulture, format, args )
  2. 您的代码在生成缓存键时有冗余步骤,我将其移至单个变量 cacheKey
  3. C# 命名约定对参数和局部变量使用 camelCase,对方法使用 PascalCase

由于 FileCache 的性质,我认为唯一合理的方法是回退到通常的方式 - 检查缓存中是否存在项目,如果不存在 - 添加它:

private static readonly _cacheLock = new object();
private static readonly FileCache _cache = new FileCache($"{AppDomain.CurrentDomain.BaseDirectory}{"\cache"}", false, TimeSpan.FromSeconds(30));
/// ...
lock (_cacheLock) {
    var myString = _cache.Get($"CacheKey{Id}");
    if (myString == null) {
        myString = generateString(Id);
        _cache.Set($"CacheKey{Id}", myString, new CacheItemPolicy() { AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(30) });
    }
}

锁定是必要的,因为 FileCache 写入和读取同一个文件,并且从多个线程执行此操作永远不安全。