diff --git a/src/DivertR/DiverterSettings.cs b/src/DivertR/DiverterSettings.cs index e454630e..516f9bfc 100644 --- a/src/DivertR/DiverterSettings.cs +++ b/src/DivertR/DiverterSettings.cs @@ -18,6 +18,8 @@ public class DiverterSettings public bool DefaultWithDummyRoot { get; } + public bool CacheRedirectProxies { get; } + public IDummyFactory DummyFactory { get; } public ICallInvoker CallInvoker { get; } @@ -41,11 +43,11 @@ public static DiverterSettings Global } } - public DiverterSettings( - IProxyFactory? proxyFactory = null, + public DiverterSettings(IProxyFactory? proxyFactory = null, IDiverterProxyFactory? diverterProxyFactory = null, IRedirectProxyDecorator? redirectProxyDecorator = null, bool defaultWithDummyRoot = true, + bool cacheRedirectProxies = true, IDummyFactory? dummyFactory = null, ICallInvoker? callInvoker = null) { @@ -53,6 +55,7 @@ public DiverterSettings( DiverterProxyFactory = diverterProxyFactory ?? new DiverterProxyFactory(); RedirectProxyDecorator = redirectProxyDecorator ?? new RedirectProxyDecorator(); DefaultWithDummyRoot = defaultWithDummyRoot; + CacheRedirectProxies = cacheRedirectProxies; DummyFactory = dummyFactory ?? new DummyFactory(); CallInvoker = callInvoker ?? DefaultCallInvoker; } diff --git a/src/DivertR/Redirect.cs b/src/DivertR/Redirect.cs index 8e26b905..b5e812c8 100644 --- a/src/DivertR/Redirect.cs +++ b/src/DivertR/Redirect.cs @@ -13,6 +13,7 @@ public class Redirect : IRedirect where TTarget : class? private readonly IProxyFactory _proxyFactory; private readonly Relay _relay; private readonly RedirectProxyCall _redirectProxyCall; + private readonly ConditionalWeakTable _proxyCache = new(); public Redirect(string? name = null, DiverterSettings? diverterSettings = null, IRedirectRepository? redirectRepository = null) : this(RedirectId.From(name), new RedirectSet(diverterSettings), redirectRepository) @@ -102,9 +103,25 @@ public TTarget Proxy(object? root) [return: NotNull] public TTarget Proxy(TTarget? root) { - var proxy = _proxyFactory.CreateProxy(_redirectProxyCall, root); + TTarget proxy; - return RedirectSet.Settings.RedirectProxyDecorator.Decorate(this, proxy); + if (root is null || !RedirectSet.Settings.CacheRedirectProxies) + { + proxy = _proxyFactory.CreateProxy(_redirectProxyCall, root); + RedirectSet.Settings.RedirectProxyDecorator.Decorate(this, proxy); + } + else + { + proxy = _proxyCache.GetValue(root, x => + { + var createdProxy = _proxyFactory.CreateProxy(_redirectProxyCall, x); + RedirectSet.Settings.RedirectProxyDecorator.Decorate(this, createdProxy); + + return createdProxy; + }); + } + + return proxy!; } /// diff --git a/test/DivertR.UnitTests/RedirectTests.cs b/test/DivertR.UnitTests/RedirectTests.cs index 2ee9d666..f64098e2 100644 --- a/test/DivertR.UnitTests/RedirectTests.cs +++ b/test/DivertR.UnitTests/RedirectTests.cs @@ -61,6 +61,35 @@ public void GivenInvalidRootObjectType_WhenCreateProxyObject_ShouldThrowArgument // ASSERT testAction.ShouldThrow(); } + + [Fact] + public void GivenDefaultDiverterSettings_WhenCreateProxiesWithSameRootInstance_ShouldCache() + { + // ARRANGE + var foo = new Foo(); + var proxy = _redirect.Proxy(foo); + + // ACT + var testProxy = _redirect.Proxy(foo); + + // ASSERT + testProxy.ShouldBeSameAs(proxy); + } + + [Fact] + public void GivenCacheRedirectProxiesDisabled_WhenCreateProxiesWithSameRootInstance_ShouldNotCache() + { + // ARRANGE + var redirect = new Redirect(new DiverterSettings(cacheRedirectProxies: false)); + var foo = new Foo(); + var proxy = redirect.Proxy(foo); + + // ACT + var testProxy = redirect.Proxy(foo); + + // ASSERT + testProxy.ShouldNotBeSameAs(proxy); + } [Fact] public void GivenStrictModeWithNoVia_ShouldThrowException()