Repository navigation
Versions and Upgrading
From nuget.org, read on 2026-10-01 at 03:32 UTC. No version is deprecated or unlisted.
| Version | Published (UTC) | Downloads | Builds | Depends on |
|---|---|---|---|---|
| 2.0.0 | 2026-09-28 | 47 |
lib/net10.0, lib/netstandard2.0
|
Autofac 6.5.0+, Autofac.Extras.DynamicProxy 7.1.0+, Castle.Core 5.1.1+, System.Runtime.Caching 10.0.12+ |
| 2.0.0-beta.1 | 2026-09-28 | 32 | the same | the same (the release rehearsal of 2.0.0) |
| 1.0.1 | 2015-04-18 | 2 313 | lib/net45 |
Autofac.Extras.DynamicProxy2 3.0.5 (which brings Autofac 3.3.1 and Castle.Core 3.2.2) |
| 1.0.0 | 2015-04-18 | 1 711 | none (the files sit under bin/Debug) |
The CHANGELOG dates 2.0.0 and 2.0.0-beta.1 on 2026-09-27, the maintainer's local date; nuget.org records both on 2026-09-28 in UTC.
Its package holds the build output under bin/Debug and obj/Debug instead of lib/, so a project that references it gets no assembly. The smallest program that names the namespace does not compile against 1.0.0 (on .NET 10):
using System;
using CachingServiceWithAOP;
Console.WriteLine(typeof(CacheAttribute).Assembly.GetName().Version);error CS0246: The type or namespace name 'CachingServiceWithAOP' could not be found (are you missing a using directive or an assembly reference?)
Use 2.0.0.
1.0.1 is a net45 build over Autofac 3.3 and Castle.Core 3.2.2. A .NET 10 project can reference it (NuGet warns NU1701), but the first proxy fails. This program ran on both runtimes against both versions:
using System;
using Autofac;
using CachingServiceWithAOP;
using CachingServiceWithAOP.Extensions;
try
{
var builder = new ContainerBuilder();
builder.RegisterCachingModule();
builder.RegisterType<Catalogue>().As<ICatalogue>().EnableCacheInterception();
using var container = builder.Build();
Console.WriteLine(container.Resolve<ICatalogue>().Title(7));
}
catch (Exception e)
{
for (var error = e; error != null; error = error.InnerException)
{
Console.WriteLine(error.GetType().Name + ": " + error.Message);
}
}
public interface ICatalogue
{
string Title(int id);
}
public class Catalogue : ICatalogue
{
[Cache]
public string Title(int id) => "title " + id;
}1.0.1 on .NET 10:
DependencyResolutionException: An exception was thrown while executing a resolve operation. See the InnerException for details. ---> The type initializer for 'Autofac.Extras.DynamicProxy2.RegistrationExtensions' threw an exception. (See inner exception for details.)
TypeInitializationException: The type initializer for 'Autofac.Extras.DynamicProxy2.RegistrationExtensions' threw an exception.
FileNotFoundException: Could not load file or assembly 'System.Security.Permissions, Version=0.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'. The system cannot find the file specified.
1.0.1 on .NET Framework 4.8, and 2.0.0 on .NET 10, .NET 8 and .NET Framework 4.8:
title 7
MemoryCacheService on its own did work on .NET 10 with 1.0.1, since it needs no proxy.
2.0.0 keeps every public type, member and parameter name of 1.0.1. What changes for a caller:
- Dependencies: Autofac 6.5 or later, Autofac.Extras.DynamicProxy 7.1 or later (it replaces Autofac.Extras.DynamicProxy2) and Castle.Core 5.1.1 or later. The API names Autofac and Castle types, so code that compiled against 1.0.1 is compiled again against these.
- Runtimes: .NET Framework 4.6.2 and later, .NET 8 and .NET 10, through
netstandard2.0andnet10.0builds. This wiki ran .NET Framework 4.8, .NET 8 and .NET 10. - Answers: the fixes below. Everything else answers as 1.0.1 did on .NET Framework (the golden replay at the end of this page).
The same programs on both versions, side by side.
using System;
using Autofac;
using CachingServiceWithAOP;
using CachingServiceWithAOP.Extensions;
var builder = new ContainerBuilder();
builder.RegisterCachingModule();
builder.RegisterType<Feed>().As<IFeed>().SingleInstance().EnableCacheInterception();
using var container = builder.Build();
var feed = container.Resolve<IFeed>();
for (var i = 0; i < 2; i++)
{
try
{
Console.WriteLine("Latest: " + (feed.Latest() ?? "null"));
}
catch (Exception e)
{
Console.WriteLine("Latest: " + e.GetType().Name);
}
}
try
{
feed.Refresh();
Console.WriteLine("Refresh: done");
}
catch (Exception e)
{
Console.WriteLine("Refresh: " + e.GetType().Name);
}
public interface IFeed
{
string? Latest();
void Refresh();
}
public class Feed : IFeed
{
[Cache]
public string? Latest()
{
Console.WriteLine("Latest ran");
return null;
}
[Cache]
public void Refresh() => Console.WriteLine("Refresh ran");
}1.0.1 on .NET Framework 4.8:
Latest ran
Latest: ArgumentNullException
Latest ran
Latest: ArgumentNullException
Refresh ran
Refresh: ArgumentNullException
2.0.0, every runtime and dependency set:
Latest ran
Latest: null
Latest: null
Refresh ran
Refresh: done
1.0.1 ran the method, then threw while storing its null result, so callers got an exception after the work was done. 2.0.0 keeps the null and runs void methods without storing anything.
using System;
using Autofac;
using CachingServiceWithAOP;
using CachingServiceWithAOP.Extensions;
var builder = new ContainerBuilder();
builder.RegisterCachingModule();
builder.RegisterType<Archive>().As<IArchive>().SingleInstance().EnableCacheInterception();
using var container = builder.Build();
var archive = container.Resolve<IArchive>();
for (var i = 0; i < 2; i++)
{
try
{
Console.WriteLine("Read: " + archive.Read());
}
catch (Exception e)
{
Console.WriteLine("Read: " + e.GetType().Name);
}
}
public interface IArchive
{
int Read();
}
public class Archive : IArchive
{
private int runs;
[Cache(long.MaxValue)]
public int Read()
{
Console.WriteLine("Read ran");
return ++runs;
}
}1.0.1 on .NET Framework 4.8:
Read ran
Read: ArgumentOutOfRangeException
Read ran
Read: ArgumentOutOfRangeException
2.0.0, every runtime and dependency set:
Read ran
Read: 1
Read: 1
The API reference programs, on 1.0.1 (.NET Framework 4.8 and .NET 10 alike). Get and Set:
42
(null)
0
42
(null)
42
2.0.0 prints seven and 0 for the last two lines: 1.0.1's Get(key, func) returned the default without running the function when the key held a value of another type. The constructors:
(): kept
(TimeSpan.FromMinutes(1)): kept
(600000000L): kept
(0, 1, 0): kept
(new MemoryCache(...)): nothing
(TimeSpan.Zero): nothing
(TimeSpan.FromMinutes(-5)): nothing
(TimeSpan.MaxValue): ArgumentOutOfRangeException
2.0.0 keeps the value in a MemoryCache you pass (1.0.1 gave it a lifetime of zero) and never expires TimeSpan.MaxValue.
From the CHANGELOG; each one is also a case in the golden replay or a test in the repository:
-
Get(key, func)whose function returns null threwArgumentNullExceptionand ran the function again next time; the null is now kept. - Concurrent calls with one key all ran the method; now one runs and the others wait (How results are cached).
- A faulted or cancelled
Taskstayed cached; it is now removed.ValueTaskmethods are not cached. - An argument that cannot be written into a key made the call throw before the method ran; the call now runs uncached (Errors and edge cases).
- Interception without
RegisterCachingModule()still fails, with Autofac's current error around the old one.
- Reference 2.0.0. If the project references Autofac, Autofac.Extras.DynamicProxy2 or Castle.Core itself, move them to Autofac 6.5 or later, Autofac.Extras.DynamicProxy 7.1 or later (remove DynamicProxy2) and Castle.Core 5.1.1 or later.
- Rename
using Autofac.Extras.DynamicProxy2;tousing Autofac.Extras.DynamicProxy;where your code uses that namespace directly. Calls toRegisterCachingModule(),EnableCacheInterception()and[Cache]stay as they are. - Your own Autofac code may need changes between Autofac 3 and 6; that is Autofac's upgrade, not this package's, and this wiki did not test it.
- Check the places where you relied on the old answers above: a
MemoryCacheService(ObjectCache)that kept nothing, code that caughtArgumentNullExceptionfrom a null result, and lifetimes oflong.MaxValueticks. - A .NET Framework project must target 4.6.2 or later (1.0.1 accepted 4.5).
Cache keys are 1.0.1's, so nothing in the store format changes; nothing is persisted between processes anyway.
The repository keeps recordings of 1.0.1 answering 158 calls (tests/Golden, made with the published 1.0.1 by tests/Golden/Capture). For this wiki the capture project was copied to a scratch folder, built once for each version (only the pinned version changed for 2.0.0) and run per framework with --no-build, on Windows on 2026-09-30. Cases are compared by group and name, by parsed value, with the header left out.
| Run today | Compared with | Identical | Differing |
|---|---|---|---|
| 1.0.1 on .NET Framework 4.8 | 1.0.1.net48-windows.json |
158 | 0 |
| 1.0.1 on .NET 10 | 1.0.1.net10.0-windows.json |
158 | 0 |
| 2.0.0 on .NET Framework 4.8 | 1.0.1.net48-windows.json |
147 | 11 |
| 2.0.0 on .NET 10 | 1.0.1.net48-windows.json |
141 | 17 |
| 2.0.0 on .NET 10 | 1.0.1.net10.0-windows.json |
72 | 86 |
- 1.0.1 still answers exactly as recorded, so the recordings still stand for it.
- The 11 differences on .NET Framework are the 10 fixes listed above (null and void results,
Get(key, func), theObjectCacheconstructor, lifetimes oflong.MaxValue,long.MinValueandTimeSpan.MaxValue) and the changed Autofac error for interception without the module. Each is a CHANGELOG line. - On .NET 10 the same 11 differ, plus 6 whose only difference is the
(Parameter 'name')that .NET adds toArgumentExceptionmessages. - The .NET 10 recording is 1.0.1 failing on its first proxy, so 2.0.0 differs from it wherever a proxy is made.
The public API, listed by reflection from the published 2.0.0 with the repository's tests/Golden/ApiList (net48, so the netstandard2.0 build), matched PublicApi-1.0.1.txt in 46 of 46 lines; 1.0.1 today matched it in 46 of 46 too.
This wiki describes CachingServiceWithAOPSupport 2.0.0 and was last updated on 2026-09-30. The library is MIT licensed. Report problems in the issues. On NuGet: CachingServiceWithAOPSupport.