cachesourcegenerator by Jeppe Roi Kristensen
Nuget / site data
Details
Info
Name: cachesourcegenerator
A tool to wrap a method call with caching
Author: Jeppe Roi Kristensen
NuGet: https://www.nuget.org/packages/cachesourcegenerator/
You can find more details at https://github.com/jeppevammenkristensen/cachesourcegenerator
Source : https://github.com/jeppevammenkristensen/cachesourcegenerator
Original Readme
Cache source generator
Important CachoAttribute
has been renamed to GenerateMemoryCacheAttribute
A source generator that can generate simple cache boilerplate to wrap around a method
Getting started
This generator works by wrapping a method in another method with the same signature, that ensures calls are cached.
In a partial class decorate the method with the GenerateMemoryCache
attribute
public partial class SampleEntity
{
private readonly IMemoryCache _memoryCache;
public SampleEntity(IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
}
[GenerateMemoryCache(MethodName = "GetId", CacheEnricherProcessor = nameof(ProcessCacheEntry))]
private string? DoGetSomeValue(int id)
{
return "Someresult";
}
public void ProcessCacheEntry(ICacheEntry entry)
{
entry.SlidingExpiration = TimeSpan.FromMinutes(2);
}
}
And it will generate
public partial class SampleEntity
{
public string? GetId(int id)
{
var _key_ = new
{
_MethodName = "DoGetSomeValue",
_ClassName = "SampleEntity",
id
};
IMemoryCache _cache_ = _memoryCache;
return _cache_.GetOrCreate(_key_, _entry_ =>
{
ProcessCacheEntry(_entry_);
return DoGetSomeValue(id);
});
}
public void GetId_Evict(int id)
{
var _key_ = new
{
_MethodName = "DoGetSomeValue",
_ClassName = "SampleEntity",
id
};
IMemoryCache _cache_ = _memoryCache;
_cache_.Remove(_key_);
}
}
Note that that defining the CacheEnricherProcessor is optional and can be left out
Cache access
The IMemoryCache can be retrieved in two ways. Autogenerated or by providing it in the class
Autogenerated cache access code
This requires that you install the nuget package Microsoft.Extensions.Caching.Memory.
Decorate a method that returns a value on a partial class with the GenerateMemoryCache Attribute
public partial class SomeClass
{
[CacheSourceGenerator.Cache(MethodName = "SomeMethod")]
private string DoSomeMethod(string id, int age)
{
return $"{id}{age}";
}
}
This will generate the code below.
public partial class SomeClass
{
private static class CacheInit
{
static CacheInit()
{
_memoryCache = new Lazy<IMemoryCache>(() => new MemoryCache(new MemoryCacheOptions()));
}
private static Lazy<IMemoryCache> _memoryCache;
public static IMemoryCache MemoryCache => _memoryCache.Value;
}
public string SomeMethod(string id, int age)
{
var key = new
{
_MethodName = "DoSomeMethod",
_ClassName = "SomeClass",
id,
age
};
IMemoryCache cache = CacheInit.MemoryCache;
return cache.GetOrCreate(key, entry =>
{
return DoSomeMethod(id, age);
});
}
}
Providing the cache from the class
An alternative is to provide a IMemoryCache instance from the class. This can be done through a
- Field
- Property
- Method (parameter less)
public static partial class SomeOtherClass
{
private static IMemoryCache GetCache() => new MemoryCache(new MemoryCacheOptions());
[CacheSourceGenerator.GenerateMemoryCache(MethodName = "SomeMethod")]
public static Task<string> ExecuteCall()
{
return Task.FromResult("Hello");
}
}
This will generate the code below.
public static partial class SomeOtherClass
{
public async static Task<string> SomeMethod()
{
var key = new
{
_MethodName = "ExecuteCall",
_ClassName = "SomeOtherClass",
};
IMemoryCache cache = GetCache();
var result = await cache.GetOrCreateAsync(key, async entry =>
{
return await ExecuteCall();
});
return result ?? throw new InvalidOperationException("Expected non empty result");
}
}
Method generation
if the method is async or returning a Task<T>
the generated method will take that into consideration.
If the return type is not nullable, the generated method will throw an exception if the result of the method call is null.
GenerateMemoryCache Atrribute
MethodName
The GenerateMemoryCache needs to as a minimum have MethodName set as this is the desired method name of the generated method.
CacheEnricherProcessor
If you want to control the ICacheEntry
object, you can use this property to point to a method that takes a ICacheEntry
as input and
returns void or if async as Task. This method will be called like below and can be used set for instance expiration
var _result_ = _cache_.GetOrCreate(_key_, _entry_ =>
{
CacheEnricher(_entry_);
return DoGetName(id);
});
KeyGenerator
Out of the box a key will be auto generated that will consist of
- MethodName
- ClassName
- The parameters of the method
If you want to create a custom key, you can use the KeyGenerator property to point to a method that will generate the key. The method must match the parameters of the decorated method by type (it's okay if there is a mismatch between names)
The return type can be anything but void
So for
[GenerateMemoryCache(KeyGenerator=nameof(GenerateKey), MethodName="SomeName")]
public string Somemethod(string id, int number, bool boolValue)
a valid KeyGenerator method could be
public (string id, int number, bool boolean)
About
Caching methods results
How to use
Example ( source csproj, source files )
- CSharp Project
- Program.cs
- Fib.cs
This is the CSharp Project that references cachesourcegenerator
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CacheSourceGenerator" Version="0.4.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
</ItemGroup>
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
</Project>
This is the use of cachesourcegenerator in Program.cs
using CacheDemo;
using Microsoft.Extensions.Caching.Memory;
var f=new FibTest(new MemoryCache(new MemoryCacheOptions()));
Console.WriteLine(f.FibMemo(5));
Console.WriteLine("and now with cache hit:");
Console.WriteLine(f.FibMemo(5));
This is the use of cachesourcegenerator in Fib.cs
using Microsoft.Extensions.Caching.Memory;
using CacheSourceGenerator;
namespace CacheDemo;
internal partial class FibTest
{
private readonly IMemoryCache _memoryCache;
public FibTest(IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
}
void ProcessCacheEntry(ICacheEntry entry)
{
entry.SlidingExpiration = TimeSpan.FromMinutes(2);
}
[GenerateMemoryCache(MethodName = "FibMemo", CacheEnricherProcessor = nameof(ProcessCacheEntry))]
public int Fib(int n)
{
if (n <= 1)
{
return n;
}
Console.WriteLine($"Calculating Fib({n})");
//return Fib(n - 1) + Fib(n - 2);
return FibMemo(n - 1) + FibMemo(n - 2);
}
}
Generated Files
Those are taken from $(BaseIntermediateOutputPath)\GX
- FibTest.g.cs
- GenerateMemoryCache.g.cs
- IgnoreKey.g.cs
#nullable enable
//autogenerated
using Microsoft.Extensions.Caching.Memory;
using CacheSourceGenerator;
using System;
namespace CacheDemo;
internal partial class FibTest
{
public int FibMemo(int n)
{
var _key_ = new
{
_MethodName = "Fib",
_ClassName = "FibTest",
n
};
IMemoryCache _cache_ = _memoryCache;
var _result_ = _cache_.GetOrCreate(_key_, _entry_ =>
{
ProcessCacheEntry(_entry_);
OnCallingFib(n);
var _callResult_ = Fib(n);
OnCalledFib(n, _callResult_);
;
return _callResult_;
});
return _result_;
}
public void FibMemo_Evict(int n)
{
var _key_ = new
{
_MethodName = "Fib",
_ClassName = "FibTest",
n
};
IMemoryCache _cache_ = _memoryCache;
_cache_.Remove(_key_);
}
partial void OnCallingFib(int n);
partial void OnCalledFib(int n, int _returned_);
}
// <auto-generated/>
#nullable enable
namespace CacheSourceGenerator
{
[System.AttributeUsage(System.AttributeTargets.Method)]
public class GenerateMemoryCacheAttribute : System.Attribute
{
/// <summary>
/// The name of the generated cache method
/// </summary>
public string MethodName { get;init; } = default!;
/// <summary>
/// The name of a method in the current class that takes
/// an CacheEntry and processes it
/// </summary>
public string? CacheEnricherProcessor { get;set; }
/// <summary>
/// The name of a method in the current class that can
/// generate a custom cache key. The method must take the same parameters
/// as the method being decorated. But can return any type.
/// </summary>
public string? KeyGenerator {get;set;}
/// <summary>
/// Set this to true to not generate an evict method
/// </summary>
public bool SuppressEvictMethod {get;set;}
}
}
// <auto-generated/>
#nullable enable
namespace CacheSourceGenerator
{
[System.AttributeUsage(System.AttributeTargets.Parameter)]
public class IgnoreKeyAttribute : System.Attribute
{
}
}
Usefull
Download Example (.NET C# )
Share cachesourcegenerator
https://ignatandrei.github.io/RSCG_Examples/v2/docs/cachesourcegenerator