├── .nuget
├── NuGet.exe
├── NuGet.Config
└── NuGet.targets
├── MethodCache.Tests.TestAssembly
├── MethodResult.cs
├── Cache
│ ├── EnumParameter.cs
│ ├── ICache.cs
│ ├── ComplexCacheAttribute
│ │ ├── EnumType.cs
│ │ └── CacheAttribute.cs
│ ├── ICacheWithRemove.cs
│ ├── CacheAttribute.cs
│ ├── ICacheWithStoreParameters.cs
│ └── DictionaryCache.cs
├── TestClass4.cs
├── TestClass5.cs
├── TestClass6.cs
├── TestClass9.cs
├── TestClass8.cs
├── TestClass1.cs
├── TestClass2.cs
├── TestClass7.cs
├── TestClass3.cs
├── TestClassAllExplicitlyIncluded.cs
├── TestClassStaticProperties.cs
├── TestClassMethodsExcluded.cs
├── TestClassPropertiesExcluded.cs
├── TestClassGeneric.cs
├── TestClassIndividualProperties.cs
├── TestClassWithPropertiesWithoutRemove.cs
├── TestClassWithProperties.cs
├── Properties
│ └── AssemblyInfo.cs
├── MethodCache.Tests.TestAssembly.csproj
└── TestClassWithParameterizedCacheAttribute.cs
├── MethodCache.Fody
├── packages.config
├── MethodsForWeaving.cs
├── Properties
│ └── AssemblyInfo.cs
├── MethodCache.Fody.csproj
├── ReferenceFinder.cs
├── CecilHelper.cs
└── ModuleWeaver.cs
├── MethodCache.Attributes
├── Members.cs
├── NoCacheAttribute.cs
├── CacheAttribute.cs
├── Properties
│ └── AssemblyInfo.cs
└── MethodCache.Attributes.csproj
├── packages
└── repositories.config
├── MethodCache.Tests
├── packages.config
├── ModuleWeaverTestsBase.cs
├── Verifier.cs
├── AssemblyResolverMock.cs
├── Properties
│ └── AssemblyInfo.cs
├── WeaverHelper.cs
├── GenericTests.cs
├── MethodCacheDisabledAtConfigTests.cs
├── PropertyCacheDisabledAtConfigTests.cs
├── MethodCache.Tests.csproj
├── ModuleWeaverTests.cs
├── PropertyCachingTests.cs
└── ParameterizedCacheAttributeWithParameterTests.cs
├── MethodCache.Nuget
├── make.bat
├── readme.txt
├── tools
│ ├── uninstall.ps1
│ └── install.ps1
└── MethodCache.nuspec
├── .gitattributes
├── MethodCache.sln
├── .gitignore
├── Settings.StyleCop
├── README.md
└── MethodCache.sln.DotSettings
/.nuget/NuGet.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dresel/MethodCache/HEAD/.nuget/NuGet.exe
--------------------------------------------------------------------------------
/MethodCache.Tests.TestAssembly/MethodResult.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests.TestAssembly
2 | {
3 | public class MethodResult
4 | {
5 | }
6 | }
--------------------------------------------------------------------------------
/MethodCache.Fody/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/MethodCache.Tests.TestAssembly/Cache/EnumParameter.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests.TestAssembly.Cache
2 | {
3 | public enum EnumParameter
4 | {
5 | Entry1,
6 |
7 | Entry2
8 | }
9 | }
--------------------------------------------------------------------------------
/.nuget/NuGet.Config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/MethodCache.Attributes/Members.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Attributes
2 | {
3 | using System;
4 |
5 | [Flags]
6 | public enum Members
7 | {
8 | Methods = 1,
9 |
10 | Properties = 2,
11 |
12 | All = 3
13 | }
14 | }
--------------------------------------------------------------------------------
/packages/repositories.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/MethodCache.Tests.TestAssembly/Cache/ICache.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests.TestAssembly.Cache
2 | {
3 | public interface ICache
4 | {
5 | bool Contains(string key);
6 |
7 | T Retrieve(string key);
8 |
9 | void Store(string key, object data);
10 | }
11 | }
--------------------------------------------------------------------------------
/MethodCache.Tests.TestAssembly/Cache/ComplexCacheAttribute/EnumType.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests.TestAssembly.Cache.ComplexCacheAttribute
2 | {
3 | using System;
4 |
5 | [Flags()]
6 | public enum EnumType
7 | {
8 | Entry1 = 1,
9 |
10 | Entry2 = 2,
11 |
12 | Entry3 = 4
13 | }
14 | }
--------------------------------------------------------------------------------
/MethodCache.Tests/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/MethodCache.Tests.TestAssembly/Cache/ICacheWithRemove.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests.TestAssembly.Cache
2 | {
3 | public interface ICacheWithRemove
4 | {
5 | bool Contains(string key);
6 |
7 | void Remove(string key);
8 |
9 | T Retrieve(string key);
10 |
11 | void Store(string key, object data);
12 | }
13 | }
--------------------------------------------------------------------------------
/MethodCache.Tests.TestAssembly/Cache/CacheAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests.TestAssembly.Cache
2 | {
3 | using System;
4 |
5 | public class CacheAttribute : Attribute
6 | {
7 | public string Parameter1 { get; set; }
8 |
9 | public int Parameter2 { get; set; }
10 |
11 | public bool Parameter3 { get; set; }
12 |
13 | public double parameter3;
14 | }
15 | }
--------------------------------------------------------------------------------
/MethodCache.Tests.TestAssembly/TestClass4.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests.TestAssembly
2 | {
3 | using MethodCache.Attributes;
4 |
5 | [Cache]
6 | public class TestClass4
7 | {
8 | // No CacheGetter - ModuleWeaver should check for Cache Getter and should skip weaving of this class
9 | public int MethodOne(int x)
10 | {
11 | return x * x;
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/MethodCache.Tests.TestAssembly/Cache/ICacheWithStoreParameters.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests.TestAssembly.Cache
2 | {
3 | using System.Collections.Generic;
4 |
5 | public interface ICacheWithStoreParameters
6 | {
7 | bool Contains(string key);
8 |
9 | T Retrieve(string key);
10 |
11 | void Store(string key, object data, IDictionary parameters);
12 | }
13 | }
--------------------------------------------------------------------------------
/MethodCache.Nuget/make.bat:
--------------------------------------------------------------------------------
1 | mkdir input\lib\
2 | del /Q input\lib\*.*
3 |
4 | msbuild ..\MethodCache.Attributes\MethodCache.Attributes.csproj /p:Configuration=Release;OutputPath=..\MethodCache.Nuget\input\lib
5 | msbuild ..\MethodCache.Fody\MethodCache.Fody.csproj /p:Configuration=Release;OutputPath=..\MethodCache.Nuget\input\lib
6 |
7 | mkdir output
8 | ..\.nuget\nuget.exe pack /o output .\MethodCache.nuspec
--------------------------------------------------------------------------------
/MethodCache.Tests.TestAssembly/TestClass5.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests.TestAssembly
2 | {
3 | using MethodCache.Attributes;
4 |
5 | [Cache]
6 | public class TestClass5
7 | {
8 | // Wrong CacheType - ModuleWeaver should check Getter Type and should skip weaving of this class
9 | public int Cache { get; set; }
10 |
11 | public int MethodOne(int x)
12 | {
13 | return x * x;
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/MethodCache.Attributes/NoCacheAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Attributes
2 | {
3 | using System;
4 |
5 | ///
6 | /// Prevents caching the output of this method when CacheAttribute is used on class level.
7 | ///
8 | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
9 | public class NoCacheAttribute : Attribute
10 | {
11 | }
12 | }
--------------------------------------------------------------------------------
/MethodCache.Tests.TestAssembly/TestClass6.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests.TestAssembly
2 | {
3 | using MethodCache.Attributes;
4 | using MethodCache.Tests.TestAssembly.Cache;
5 |
6 | public class TestClass6 : TestClass1
7 | {
8 | public TestClass6(ICache cache)
9 | : base(cache)
10 | {
11 | }
12 |
13 | [Attributes.Cache]
14 | public int MethodThree(int x)
15 | {
16 | return x * x * x;
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/MethodCache.Tests.TestAssembly/TestClass9.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests.TestAssembly
2 | {
3 | using MethodCache.Attributes;
4 | using MethodCache.Tests.TestAssembly.Cache;
5 |
6 | [Attributes.Cache]
7 | public class TestClass9
8 | {
9 | // MethodOne is static, Getter is not - ModuleWeaver should check this and should skip weaving of this method
10 | public ICache Cache { get; set; }
11 |
12 | public static int MethodOne(int x)
13 | {
14 | return x * x;
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/MethodCache.Tests.TestAssembly/TestClass8.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests.TestAssembly
2 | {
3 | using MethodCache.Attributes;
4 | using MethodCache.Tests.TestAssembly.Cache;
5 |
6 | [Attributes.Cache]
7 | public class TestClass8
8 | {
9 | public static ICache Cache { get; set; }
10 |
11 | public static int MethodOne(int x)
12 | {
13 | return x * x;
14 | }
15 |
16 | public static MethodResult MethodTwo(string x)
17 | {
18 | return new MethodResult();
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/MethodCache.Nuget/readme.txt:
--------------------------------------------------------------------------------
1 | Since 1.5.0.0 it is now possible to use custom CacheAttribute parameters for fine-grained cache control (see https://github.com/Dresel/MethodCache section "CacheAttribute Parameters Support" for details).
2 |
3 | Since 1.4.0.0 it is now possible to also cache properties (see https://github.com/Dresel/MethodCache for details).
4 |
5 | If you want this package to behave as previous packages (let class level [Cache] attribute only cache methods), modify ModuleWeavers.xml to:
6 |
7 |
--------------------------------------------------------------------------------
/MethodCache.Tests.TestAssembly/TestClass1.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests.TestAssembly
2 | {
3 | using MethodCache.Attributes;
4 | using MethodCache.Tests.TestAssembly.Cache;
5 |
6 | [Attributes.Cache]
7 | public class TestClass1
8 | {
9 | public TestClass1(ICache cache)
10 | {
11 | Cache = cache;
12 | }
13 |
14 | public ICache Cache { get; set; }
15 |
16 | public int MethodOne(int x)
17 | {
18 | return x * x;
19 | }
20 |
21 | public MethodResult MethodTwo(string x)
22 | {
23 | return new MethodResult();
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/MethodCache.Tests.TestAssembly/TestClass2.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests.TestAssembly
2 | {
3 | using MethodCache.Attributes;
4 | using MethodCache.Tests.TestAssembly.Cache;
5 |
6 | public class TestClass2
7 | {
8 | public TestClass2(ICache cache)
9 | {
10 | Cache = cache;
11 | }
12 |
13 | public ICache Cache { get; set; }
14 |
15 | [Attributes.Cache]
16 | public int MethodOne(int x)
17 | {
18 | return x * x;
19 | }
20 |
21 | public MethodResult MethodTwo(string x)
22 | {
23 | return new MethodResult();
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/MethodCache.Attributes/CacheAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Attributes
2 | {
3 | using System;
4 |
5 | ///
6 | /// Cache the output of this member or members of this class.
7 | ///
8 | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = false,
9 | Inherited = false)]
10 | public class CacheAttribute : Attribute
11 | {
12 | public CacheAttribute()
13 | : this(Members.All)
14 | {
15 | }
16 |
17 | public CacheAttribute(Members membersToCache)
18 | {
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/MethodCache.Tests.TestAssembly/TestClass7.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests.TestAssembly
2 | {
3 | using MethodCache.Attributes;
4 | using MethodCache.Tests.TestAssembly.Cache;
5 |
6 | [Attributes.Cache]
7 | public class TestClass7
8 | {
9 | public TestClass7(ICache cache)
10 | {
11 | Cache = cache;
12 | }
13 |
14 | public ICache Cache { get; set; }
15 |
16 | public int MethodOne(int x)
17 | {
18 | return x * x;
19 | }
20 |
21 | [NoCache]
22 | public MethodResult MethodTwo(string x)
23 | {
24 | return new MethodResult();
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/MethodCache.Tests.TestAssembly/TestClass3.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests.TestAssembly
2 | {
3 | using MethodCache.Attributes;
4 | using MethodCache.Tests.TestAssembly.Cache;
5 |
6 | [Attributes.Cache]
7 | public class TestClass3
8 | {
9 | public TestClass3(DictionaryCache cache)
10 | {
11 | Cache = cache;
12 | }
13 |
14 | public DictionaryCache Cache { get; set; }
15 |
16 | public int MethodOne(int x)
17 | {
18 | return x * x;
19 | }
20 |
21 | public MethodResult MethodTwo(string x)
22 | {
23 | return new MethodResult();
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/MethodCache.Tests.TestAssembly/TestClassAllExplicitlyIncluded.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests.TestAssembly
2 | {
3 | using MethodCache.Attributes;
4 | using MethodCache.Tests.TestAssembly.Cache;
5 |
6 | [Attributes.Cache(Members.All)]
7 | public class TestClassAllExplicitlyIncluded
8 | {
9 | public TestClassAllExplicitlyIncluded(ICache cache)
10 | {
11 | Cache = cache;
12 | }
13 |
14 | public ICache Cache { get; private set; }
15 |
16 | public string Property
17 | {
18 | get { return "some value"; }
19 | }
20 |
21 | public int Method(int x)
22 | {
23 | return x * x;
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/MethodCache.Tests.TestAssembly/TestClassStaticProperties.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests.TestAssembly
2 | {
3 | using MethodCache.Attributes;
4 | using MethodCache.Tests.TestAssembly.Cache;
5 |
6 | [Attributes.Cache]
7 | public class TestClassStaticProperties
8 | {
9 | private static int someValue;
10 |
11 | public static ICacheWithRemove Cache { get; set; }
12 |
13 | public static string ReadOnlyProperty
14 | {
15 | get { return "some value"; }
16 | }
17 |
18 | // ReSharper disable once ConvertToAutoProperty
19 | public static int ReadWriteProperty
20 | {
21 | get { return someValue; }
22 | set { someValue = value; }
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/MethodCache.Tests/ModuleWeaverTestsBase.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests
2 | {
3 | using System.Reflection;
4 | using System.Xml.Linq;
5 | using NUnit.Framework;
6 |
7 | [TestFixture]
8 | public abstract class ModuleWeaverTestsBase
9 | {
10 | private Assembly assembly;
11 |
12 | protected Assembly Assembly
13 | {
14 | get { return this.assembly; }
15 | }
16 |
17 | protected virtual XElement WeaverConfig
18 | {
19 | get { return null; }
20 | }
21 |
22 | [SetUp]
23 | public void ClassInitialize()
24 | {
25 | if (this.assembly == null)
26 | {
27 | this.assembly = WeaverHelper.WeaveAssembly(GetType().Name, WeaverConfig);
28 | }
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/MethodCache.Tests.TestAssembly/TestClassMethodsExcluded.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests.TestAssembly
2 | {
3 | using MethodCache.Attributes;
4 | using MethodCache.Tests.TestAssembly.Cache;
5 |
6 | [Attributes.Cache(Members.Properties)]
7 | public class TestClassMethodsExcluded
8 | {
9 | public TestClassMethodsExcluded(ICache cache)
10 | {
11 | Cache = cache;
12 | }
13 |
14 | public ICache Cache { get; private set; }
15 |
16 | public string Property
17 | {
18 | get { return "some value"; }
19 | }
20 |
21 | public int Method(int x)
22 | {
23 | return x * x;
24 | }
25 |
26 | [Attributes.Cache]
27 | public int MethodIncluded(int x)
28 | {
29 | return x * x;
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/MethodCache.Tests.TestAssembly/TestClassPropertiesExcluded.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests.TestAssembly
2 | {
3 | using MethodCache.Attributes;
4 | using MethodCache.Tests.TestAssembly.Cache;
5 |
6 | [Attributes.Cache(Members.Methods)]
7 | public class TestClassPropertiesExcluded
8 | {
9 | public TestClassPropertiesExcluded(ICache cache)
10 | {
11 | Cache = cache;
12 | }
13 |
14 | public ICache Cache { get; private set; }
15 |
16 | [Attributes.Cache]
17 | public string ExplicitlyCachedProperty
18 | {
19 | get { return "some value"; }
20 | }
21 |
22 | public string ReadOnlyProperty
23 | {
24 | get { return "some value"; }
25 | }
26 |
27 | public int Method(int x)
28 | {
29 | return x * x;
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/MethodCache.Tests.TestAssembly/TestClassGeneric.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests.TestAssembly
2 | {
3 | using MethodCache.Attributes;
4 | using MethodCache.Tests.TestAssembly.Cache;
5 |
6 | [Attributes.Cache]
7 | public class TestClassGeneric
8 | {
9 | private TClass property;
10 |
11 | public TestClassGeneric(ICacheWithRemove cache)
12 | {
13 | Cache = cache;
14 | }
15 |
16 | public static ICacheWithRemove Cache { get; private set; }
17 |
18 | // ReSharper disable once ConvertToAutoProperty
19 | public TClass Property
20 | {
21 | get { return this.property; }
22 | set { this.property = value; }
23 | }
24 |
25 | public TMethod Method1(TMethod parameter)
26 | {
27 | return parameter;
28 | }
29 |
30 | public TClass Method2(TClass parameter)
31 | {
32 | return parameter;
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/MethodCache.Nuget/tools/uninstall.ps1:
--------------------------------------------------------------------------------
1 | param($installPath, $toolsPath, $package, $project)
2 |
3 |
4 | function Update-FodyConfig($addinName, $project)
5 | {
6 | $fodyWeaversPath = [System.IO.Path]::Combine([System.IO.Path]::GetDirectoryName($project.FullName), "FodyWeavers.xml")
7 |
8 | if (!(Test-Path ($fodyWeaversPath)))
9 | {
10 | return
11 | }
12 |
13 | $env:FodyLastProjectPath = $project.FullName
14 | $env:FodyLastWeaverName = $addinName
15 | $env:FodyLastXmlContents = [IO.File]::ReadAllText($fodyWeaversPath)
16 |
17 | $xml = [xml](get-content $fodyWeaversPath)
18 |
19 | $weavers = $xml["Weavers"]
20 | $node = $weavers.SelectSingleNode($addinName)
21 |
22 | if ($node)
23 | {
24 | $weavers.RemoveChild($node)
25 | }
26 |
27 | $xml.Save($fodyWeaversPath)
28 | }
29 |
30 |
31 |
32 | Update-FodyConfig $package.Id.Replace(".Fody", "") $project
--------------------------------------------------------------------------------
/MethodCache.Tests/Verifier.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests
2 | {
3 | using System.Diagnostics;
4 | using System.Diagnostics.CodeAnalysis;
5 | using System.IO;
6 | using Microsoft.Build.Utilities;
7 |
8 | public static class Verifier
9 | {
10 | public static string Verify(string assemblyPath2)
11 | {
12 | string exePath = GetPathToPEVerify();
13 | Process process =
14 | Process.Start(new ProcessStartInfo(exePath, "\"" + assemblyPath2 + "\"")
15 | {
16 | RedirectStandardOutput = true,
17 | UseShellExecute = false,
18 | CreateNoWindow = true
19 | });
20 |
21 | process.WaitForExit(10000);
22 |
23 | return process.StandardOutput.ReadToEnd().Trim();
24 | }
25 |
26 | private static string GetPathToPEVerify()
27 | {
28 | return Path.Combine(ToolLocationHelper.GetPathToDotNetFrameworkSdk(TargetDotNetFrameworkVersion.VersionLatest),
29 | @"bin\NETFX 4.0 Tools\peverify.exe");
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/MethodCache.Tests.TestAssembly/TestClassIndividualProperties.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests.TestAssembly
2 | {
3 | using MethodCache.Attributes;
4 | using MethodCache.Tests.TestAssembly.Cache;
5 |
6 | public class TestClassIndividualProperties
7 | {
8 | private string field;
9 |
10 | private int someValue;
11 |
12 | public TestClassIndividualProperties(ICacheWithRemove cache)
13 | {
14 | Cache = cache;
15 | }
16 |
17 | [Attributes.Cache]
18 | public int AutoProperty { get; set; }
19 |
20 | public ICacheWithRemove Cache { get; private set; }
21 |
22 | [Attributes.Cache]
23 | public string ReadOnlyProperty
24 | {
25 | get { return "some value"; }
26 | }
27 |
28 | [Attributes.Cache]
29 |
30 | // ReSharper disable once ConvertToAutoProperty
31 | public int ReadWriteProperty
32 | {
33 | get { return this.someValue; }
34 | set { this.someValue = value; }
35 | }
36 |
37 | [Attributes.Cache]
38 | public string SetOnlyProperty
39 | {
40 | set { this.field = value; }
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/MethodCache.Tests.TestAssembly/TestClassWithPropertiesWithoutRemove.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests.TestAssembly
2 | {
3 | using MethodCache.Attributes;
4 | using MethodCache.Tests.TestAssembly.Cache;
5 |
6 | [Attributes.Cache]
7 | public class TestClassWithPropertiesWithoutRemove
8 | {
9 | private string field;
10 |
11 | private int someValue;
12 |
13 | public TestClassWithPropertiesWithoutRemove(ICache cache)
14 | {
15 | Cache = cache;
16 | }
17 |
18 | public int AutoProperty { get; set; }
19 |
20 | public ICache Cache { get; private set; }
21 |
22 | [NoCache]
23 | public string ReadOnlyNoCache
24 | {
25 | get { return "no cache value"; }
26 | }
27 |
28 | public string ReadOnlyProperty
29 | {
30 | get { return "some value"; }
31 | }
32 |
33 | // ReSharper disable once ConvertToAutoProperty
34 | public int ReadWriteProperty
35 | {
36 | get { return this.someValue; }
37 | set { this.someValue = value; }
38 | }
39 |
40 | public string SetOnlyProperty
41 | {
42 | set { this.field = value; }
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/MethodCache.Tests/AssemblyResolverMock.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests
2 | {
3 | using System;
4 | using System.Diagnostics;
5 | using Mono.Cecil;
6 |
7 | public class AssemblyResolverMock : IAssemblyResolver
8 | {
9 | public AssemblyDefinition Resolve(AssemblyNameReference name)
10 | {
11 | throw new NotImplementedException();
12 | }
13 |
14 | public AssemblyDefinition Resolve(AssemblyNameReference name, ReaderParameters parameters)
15 | {
16 | throw new NotImplementedException();
17 | }
18 |
19 | public AssemblyDefinition Resolve(string fullName)
20 | {
21 | if (fullName == "System")
22 | {
23 | string codeBase = typeof(Debug).Assembly.CodeBase.Replace("file:///", "");
24 | return AssemblyDefinition.ReadAssembly(codeBase);
25 | }
26 | else
27 | {
28 | string codeBase = typeof(string).Assembly.CodeBase.Replace("file:///", "");
29 | return AssemblyDefinition.ReadAssembly(codeBase);
30 | }
31 | }
32 |
33 | public AssemblyDefinition Resolve(string fullName, ReaderParameters parameters)
34 | {
35 | throw new NotImplementedException();
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/MethodCache.Tests.TestAssembly/TestClassWithProperties.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests.TestAssembly
2 | {
3 | using MethodCache.Attributes;
4 | using MethodCache.Tests.TestAssembly.Cache;
5 |
6 | [Attributes.Cache]
7 | public class TestClassWithProperties
8 | {
9 | private string field;
10 |
11 | private int someValue;
12 |
13 | public TestClassWithProperties(ICacheWithRemove cache)
14 | {
15 | Cache = cache;
16 | ReadOnlyAutoProperty = "some value";
17 | }
18 |
19 | public int AutoProperty { get; set; }
20 |
21 | public ICacheWithRemove Cache { get; private set; }
22 |
23 | [NoCache]
24 | public string ReadOnlyNoCache
25 | {
26 | get { return "no cache value"; }
27 | }
28 |
29 | public string ReadOnlyAutoProperty { get; }
30 |
31 | public string ReadOnlyProperty
32 | {
33 | get { return "some value"; }
34 | }
35 |
36 | // ReSharper disable once ConvertToAutoProperty
37 | public int ReadWriteProperty
38 | {
39 | get { return someValue; }
40 | set { someValue = value; }
41 | }
42 |
43 | public string SetOnlyProperty
44 | {
45 | set { field = value; }
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/MethodCache.Attributes/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Resources;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("MethodCache.Attributes")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("MethodCache.Attributes")]
12 | [assembly: AssemblyCopyright("Copyright © 2013")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 | [assembly: NeutralResourcesLanguage("en")]
16 |
17 | // Version information for an assembly consists of the following four values:
18 | // Major Version
19 | // Minor Version
20 | // Build Number
21 | // Revision
22 | // You can specify all the values or you can default the Build and Revision Numbers
23 | // by using the '*' as shown below:
24 | // [assembly: AssemblyVersion("1.0.*")]
25 | [assembly: AssemblyVersion("1.5.1.0")]
26 | [assembly: AssemblyFileVersion("1.5.1.0")]
--------------------------------------------------------------------------------
/MethodCache.Fody/MethodsForWeaving.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Fody
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using Mono.Cecil;
6 |
7 | internal class MethodsForWeaving
8 | {
9 | private readonly IList> methods;
10 |
11 | private readonly IList> properties;
12 |
13 | public MethodsForWeaving()
14 | {
15 | this.methods = new List>();
16 | this.properties = new List>();
17 | }
18 |
19 | public IEnumerable> Methods
20 | {
21 | get { return this.methods; }
22 | }
23 |
24 | public IEnumerable> Properties
25 | {
26 | get { return this.properties; }
27 | }
28 |
29 | public void Add(MethodDefinition method, CustomAttribute attribute)
30 | {
31 | this.methods.Add(new Tuple(method, attribute));
32 | }
33 |
34 | public void Add(PropertyDefinition property, CustomAttribute attribute)
35 | {
36 | if (property.GetMethod == null)
37 | {
38 | return;
39 | }
40 |
41 | this.properties.Add(new Tuple(property, attribute));
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/MethodCache.Fody/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("MethodCache.Fody")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("MethodCache.Fody")]
12 | [assembly: AssemblyCopyright("Copyright © 2013")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("dead2628-829b-4d50-82e3-2a33e8c5e15c")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | // Major Version
26 | // Minor Version
27 | // Build Number
28 | // Revision
29 | // You can specify all the values or you can default the Build and Revision Numbers
30 | // by using the '*' as shown below:
31 | // [assembly: AssemblyVersion("1.0.*")]
32 | [assembly: AssemblyVersion("1.5.1.0")]
33 | [assembly: AssemblyFileVersion("1.5.1.0")]
--------------------------------------------------------------------------------
/MethodCache.Tests/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("MethodCache.Tests")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("MethodCache.Tests")]
12 | [assembly: AssemblyCopyright("Copyright © 2013")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("da9101e3-3bc8-4f74-8d60-12482624b893")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | // Major Version
26 | // Minor Version
27 | // Build Number
28 | // Revision
29 | // You can specify all the values or you can default the Build and Revision Numbers
30 | // by using the '*' as shown below:
31 | // [assembly: AssemblyVersion("1.0.*")]
32 | [assembly: AssemblyVersion("1.5.1.0")]
33 | [assembly: AssemblyFileVersion("1.5.1.0")]
--------------------------------------------------------------------------------
/MethodCache.Tests.TestAssembly/Cache/DictionaryCache.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests.TestAssembly.Cache
2 | {
3 | using System.Collections.Generic;
4 |
5 | public class DictionaryCache : ICache, ICacheWithRemove, ICacheWithStoreParameters
6 | {
7 | public DictionaryCache()
8 | {
9 | Storage = new Dictionary();
10 | }
11 |
12 | public int NumRemoveCalls { get; private set; }
13 |
14 | public int NumRetrieveCalls { get; private set; }
15 |
16 | public int NumStoreCalls { get; private set; }
17 |
18 | public int NumStoreParameterCalls { get; private set; }
19 |
20 | public IDictionary ParametersPassedToLastStoreCall { get; set; }
21 |
22 | private Dictionary Storage { get; }
23 |
24 | public bool Contains(string key)
25 | {
26 | return Storage.ContainsKey(key);
27 | }
28 |
29 | public void Remove(string key)
30 | {
31 | NumRemoveCalls++;
32 |
33 | Storage.Remove(key);
34 | }
35 |
36 | public T Retrieve(string key)
37 | {
38 | NumRetrieveCalls++;
39 |
40 | return (T)Storage[key];
41 | }
42 |
43 | public void Store(string key, object data)
44 | {
45 | NumStoreCalls++;
46 |
47 | Storage[key] = data;
48 | }
49 |
50 | void ICacheWithStoreParameters.Store(string key, object data, IDictionary parameters)
51 | {
52 | NumStoreParameterCalls++;
53 |
54 | ParametersPassedToLastStoreCall = new Dictionary(parameters);
55 |
56 | Storage[key] = data;
57 | }
58 | }
59 | }
--------------------------------------------------------------------------------
/MethodCache.Tests.TestAssembly/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("MethodCache.Tests.TestAssembly")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("MethodCache.Tests.TestAssembly")]
12 | [assembly: AssemblyCopyright("Copyright © 2013")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("7f0009f5-0a9c-41fc-9025-72cf30f516d9")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | // Major Version
26 | // Minor Version
27 | // Build Number
28 | // Revision
29 | // You can specify all the values or you can default the Build and Revision Numbers
30 | // by using the '*' as shown below:
31 | // [assembly: AssemblyVersion("1.0.*")]
32 | [assembly: AssemblyVersion("1.5.1.0")]
33 | [assembly: AssemblyFileVersion("1.5.1.0")]
--------------------------------------------------------------------------------
/MethodCache.Nuget/MethodCache.nuspec:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | MethodCache.Fody
5 | 1.5.1
6 | MethodCache.Fody
7 | Christopher Dresel
8 | Christopher Dresel
9 | http://www.opensource.org/licenses/mit-license.php
10 | http://github.com/Dresel/MethodCache
11 | false
12 | A method and property caching Fody addin.
13 |
14 | en-US
15 | ILWeaving, Fody, Cecil, Caching
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/MethodCache.Nuget/tools/install.ps1:
--------------------------------------------------------------------------------
1 | param($installPath, $toolsPath, $package, $project)
2 |
3 |
4 | function RemoveForceProjectLevelHack($project)
5 | {
6 | Foreach ($item in $project.ProjectItems)
7 | {
8 | if ($item.Name -eq "Fody_ToBeDeleted.txt")
9 | {
10 | $item.Delete()
11 | }
12 | }
13 | }
14 |
15 | function Update-FodyConfig($addinName, $project)
16 | {
17 |
18 | $fodyWeaversPath = [System.IO.Path]::Combine([System.IO.Path]::GetDirectoryName($project.FullName), "FodyWeavers.xml")
19 |
20 | $FodyLastProjectPath = $env:FodyLastProjectPath
21 | $FodyLastWeaverName = $env:FodyLastWeaverName
22 | $FodyLastXmlContents = $env:FodyLastXmlContents
23 |
24 | if (
25 | ($FodyLastProjectPath -eq $project.FullName) -and
26 | ($FodyLastWeaverName -eq $addinName))
27 | {
28 | [System.IO.File]::WriteAllText($fodyWeaversPath, $FodyLastXmlContents)
29 | return
30 | }
31 |
32 | $xml = [xml](get-content $fodyWeaversPath)
33 |
34 | $weavers = $xml["Weavers"]
35 | $node = $weavers.SelectSingleNode($addinName)
36 |
37 | if (-not $node)
38 | {
39 | $newNode = $xml.CreateElement($addinName)
40 | $weavers.AppendChild($newNode)
41 | }
42 |
43 | $xml.Save($fodyWeaversPath)
44 | }
45 |
46 | function Fix-ReferencesCopyLocal($package, $project)
47 | {
48 | $asms = $package.AssemblyReferences | %{$_.Name}
49 |
50 | foreach ($reference in $project.Object.References)
51 | {
52 | if ($asms -contains $reference.Name + ".dll")
53 | {
54 | if($reference.CopyLocal -eq $true)
55 | {
56 | $reference.CopyLocal = $false;
57 | }
58 | }
59 | }
60 | }
61 |
62 | RemoveForceProjectLevelHack $project
63 |
64 | Update-FodyConfig $package.Id.Replace(".Fody", "") $project
65 |
66 | Fix-ReferencesCopyLocal $package $project
--------------------------------------------------------------------------------
/MethodCache.Tests.TestAssembly/Cache/ComplexCacheAttribute/CacheAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests.TestAssembly.Cache.ComplexCacheAttribute
2 | {
3 | using System;
4 |
5 | public class CacheAttribute : Attribute
6 | {
7 | public bool ParameterBool { get; set; }
8 |
9 | public bool[] ParameterBoolArray { get; set; }
10 |
11 | public byte ParameterByte { get; set; }
12 |
13 | public byte[] ParameterByteArray { get; set; }
14 |
15 | public char ParameterCharacter { get; set; }
16 |
17 | public char[] ParameterCharacterArray { get; set; }
18 |
19 | public double ParameterDouble { get; set; }
20 |
21 | public double[] ParameterDoubleArray { get; set; }
22 |
23 | public EnumType ParameterEnum { get; set; }
24 |
25 | public EnumType[] ParameterEnumArray { get; set; }
26 |
27 | public float ParameterFloat { get; set; }
28 |
29 | public float[] ParameterFloatArray { get; set; }
30 |
31 | public int ParameterInt { get; set; }
32 |
33 | public int[] ParameterIntArray { get; set; }
34 |
35 | public long ParameterLong { get; set; }
36 |
37 | public long[] ParameterLongArray { get; set; }
38 |
39 | public object ParameterObject { get; set; }
40 |
41 | public object[] ParameterObjectArray { get; set; }
42 |
43 | public sbyte ParameterSByte { get; set; }
44 |
45 | public sbyte[] ParameterSByteArray { get; set; }
46 |
47 | public short ParameterShort { get; set; }
48 |
49 | public short[] ParameterShortArray { get; set; }
50 |
51 | public string ParameterString { get; set; }
52 |
53 | public string[] ParameterStringArray { get; set; }
54 |
55 | public Type ParameterType { get; set; }
56 |
57 | public Type[] ParameterTypeArray { get; set; }
58 |
59 | public uint ParameterUInt { get; set; }
60 |
61 | public uint[] ParameterUIntArray { get; set; }
62 |
63 | public ulong ParameterULong { get; set; }
64 |
65 | public ulong[] ParameterULongArray { get; set; }
66 |
67 | public ushort ParameterUShort { get; set; }
68 |
69 | public ushort[] ParameterUShortArray { get; set; }
70 | }
71 | }
--------------------------------------------------------------------------------
/MethodCache.Tests/WeaverHelper.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests
2 | {
3 | using System;
4 | using System.Diagnostics;
5 | using System.IO;
6 | using System.Reflection;
7 | using System.Xml.Linq;
8 | using MethodCache.Fody;
9 | using Mono.Cecil;
10 |
11 | public class WeaverHelper
12 | {
13 | public static dynamic CreateInstance(Assembly assembly)
14 | {
15 | return Activator.CreateInstance(CreateType(assembly));
16 | }
17 |
18 | public static dynamic CreateInstance(Assembly assembly, object parameter)
19 | {
20 | return Activator.CreateInstance(CreateType(assembly), parameter);
21 | }
22 |
23 | public static dynamic CreateInstance(Assembly assembly, object[] parameters)
24 | {
25 | return Activator.CreateInstance(CreateType(assembly), parameters);
26 | }
27 |
28 | public static Type CreateType(Assembly assembly)
29 | {
30 | return assembly.GetType(typeof(T).FullName);
31 | }
32 |
33 | public static Assembly WeaveAssembly(string suffix, XElement config)
34 | {
35 | string projectPath =
36 | Path.GetFullPath(Path.Combine(Environment.CurrentDirectory,
37 | @"..\..\..\MethodCache.Tests.TestAssembly\MethodCache.Tests.TestAssembly.csproj"));
38 | string assemblyPath = Path.Combine(Path.GetDirectoryName(projectPath),
39 | @"bin\Debug\MethodCache.Tests.TestAssembly.dll");
40 |
41 | #if (!DEBUG)
42 | assemblyPath = assemblyPath.Replace("Debug", "Release");
43 | #endif
44 |
45 | string newAssembly = assemblyPath.Replace(".dll", string.Format("{0}.dll", suffix));
46 | File.Copy(assemblyPath, newAssembly, true);
47 |
48 | ModuleDefinition moduleDefinition = ModuleDefinition.ReadModule(newAssembly);
49 | ModuleWeaver weavingTask = new ModuleWeaver { ModuleDefinition = moduleDefinition };
50 |
51 | weavingTask.AssemblyResolver = new AssemblyResolverMock();
52 |
53 | weavingTask.LogInfo = (message) => Debug.WriteLine(message);
54 | weavingTask.LogWarning = (message) => Debug.WriteLine(message);
55 | weavingTask.LogError = (message) => new Exception(message);
56 |
57 | if (config != null)
58 | {
59 | weavingTask.Config = config;
60 | }
61 |
62 | #if (DEBUG)
63 | weavingTask.DefineConstants.Add("DEBUG");
64 | #endif
65 |
66 | weavingTask.Execute();
67 | moduleDefinition.Write(newAssembly);
68 |
69 | return Assembly.LoadFile(newAssembly);
70 | }
71 | }
72 | }
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/MethodCache.Attributes/MethodCache.Attributes.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 10.0
6 | Debug
7 | AnyCPU
8 | {4F4D865D-8ABB-4554-84BB-2B7BC40E37F9}
9 | Library
10 | Properties
11 | MethodCache.Attributes
12 | MethodCache.Attributes
13 | v4.0
14 | Profile328
15 | 512
16 | {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
17 |
18 |
19 | true
20 | full
21 | false
22 | bin\Debug\
23 | DEBUG;TRACE
24 | prompt
25 | 4
26 |
27 |
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
52 |
--------------------------------------------------------------------------------
/MethodCache.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2010
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MethodCache.Fody", "MethodCache.Fody\MethodCache.Fody.csproj", "{1A9A6C8E-6B88-4FF1-9447-E37CB39AAFDE}"
5 | EndProject
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{725AD6BA-EA0A-4096-8688-A295844613E7}"
7 | ProjectSection(SolutionItems) = preProject
8 | .nuget\NuGet.Config = .nuget\NuGet.Config
9 | .nuget\NuGet.exe = .nuget\NuGet.exe
10 | .nuget\NuGet.targets = .nuget\NuGet.targets
11 | EndProjectSection
12 | EndProject
13 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MethodCache.Attributes", "MethodCache.Attributes\MethodCache.Attributes.csproj", "{4F4D865D-8ABB-4554-84BB-2B7BC40E37F9}"
14 | EndProject
15 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MethodCache.Tests.TestAssembly", "MethodCache.Tests.TestAssembly\MethodCache.Tests.TestAssembly.csproj", "{CBF07605-D646-45F7-B2B0-54DC33538644}"
16 | EndProject
17 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MethodCache.Tests", "MethodCache.Tests\MethodCache.Tests.csproj", "{CF7379B0-EC94-4427-871E-DBD537959C09}"
18 | EndProject
19 | Global
20 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
21 | Debug|Any CPU = Debug|Any CPU
22 | Release|Any CPU = Release|Any CPU
23 | EndGlobalSection
24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
25 | {1A9A6C8E-6B88-4FF1-9447-E37CB39AAFDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
26 | {1A9A6C8E-6B88-4FF1-9447-E37CB39AAFDE}.Debug|Any CPU.Build.0 = Debug|Any CPU
27 | {1A9A6C8E-6B88-4FF1-9447-E37CB39AAFDE}.Release|Any CPU.ActiveCfg = Release|Any CPU
28 | {1A9A6C8E-6B88-4FF1-9447-E37CB39AAFDE}.Release|Any CPU.Build.0 = Release|Any CPU
29 | {4F4D865D-8ABB-4554-84BB-2B7BC40E37F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
30 | {4F4D865D-8ABB-4554-84BB-2B7BC40E37F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
31 | {4F4D865D-8ABB-4554-84BB-2B7BC40E37F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
32 | {4F4D865D-8ABB-4554-84BB-2B7BC40E37F9}.Release|Any CPU.Build.0 = Release|Any CPU
33 | {CBF07605-D646-45F7-B2B0-54DC33538644}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
34 | {CBF07605-D646-45F7-B2B0-54DC33538644}.Debug|Any CPU.Build.0 = Debug|Any CPU
35 | {CBF07605-D646-45F7-B2B0-54DC33538644}.Release|Any CPU.ActiveCfg = Release|Any CPU
36 | {CBF07605-D646-45F7-B2B0-54DC33538644}.Release|Any CPU.Build.0 = Release|Any CPU
37 | {CF7379B0-EC94-4427-871E-DBD537959C09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
38 | {CF7379B0-EC94-4427-871E-DBD537959C09}.Debug|Any CPU.Build.0 = Debug|Any CPU
39 | {CF7379B0-EC94-4427-871E-DBD537959C09}.Release|Any CPU.ActiveCfg = Release|Any CPU
40 | {CF7379B0-EC94-4427-871E-DBD537959C09}.Release|Any CPU.Build.0 = Release|Any CPU
41 | EndGlobalSection
42 | GlobalSection(SolutionProperties) = preSolution
43 | HideSolutionNode = FALSE
44 | EndGlobalSection
45 | EndGlobal
46 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.sln.docstates
8 |
9 | # Build results
10 |
11 | [Dd]ebug/
12 | [Rr]elease/
13 | x64/
14 | build/
15 | [Bb]in/
16 | [Oo]bj/
17 |
18 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
19 | !packages/*/build/
20 |
21 | # MSTest test Results
22 | [Tt]est[Rr]esult*/
23 | [Bb]uild[Ll]og.*
24 |
25 | *_i.c
26 | *_p.c
27 | *.ilk
28 | *.meta
29 | *.obj
30 | *.pch
31 | *.pdb
32 | *.pgc
33 | *.pgd
34 | *.rsp
35 | *.sbr
36 | *.tlb
37 | *.tli
38 | *.tlh
39 | *.tmp
40 | *.tmp_proj
41 | *.log
42 | *.vspscc
43 | *.vssscc
44 | .builds
45 | *.pidb
46 | *.log
47 | *.scc
48 |
49 | # Visual C++ cache files
50 | ipch/
51 | *.aps
52 | *.ncb
53 | *.opensdf
54 | *.sdf
55 | *.cachefile
56 |
57 | # Visual Studio profiler
58 | *.psess
59 | *.vsp
60 | *.vspx
61 |
62 | # Guidance Automation Toolkit
63 | *.gpState
64 |
65 | # ReSharper is a .NET coding add-in
66 | _ReSharper*/
67 | *.[Rr]e[Ss]harper
68 |
69 | # TeamCity is a build add-in
70 | _TeamCity*
71 |
72 | # DotCover is a Code Coverage Tool
73 | *.dotCover
74 |
75 | # NCrunch
76 | *.ncrunch*
77 | .*crunch*.local.xml
78 |
79 | # Installshield output folder
80 | [Ee]xpress/
81 |
82 | # DocProject is a documentation generator add-in
83 | DocProject/buildhelp/
84 | DocProject/Help/*.HxT
85 | DocProject/Help/*.HxC
86 | DocProject/Help/*.hhc
87 | DocProject/Help/*.hhk
88 | DocProject/Help/*.hhp
89 | DocProject/Help/Html2
90 | DocProject/Help/html
91 |
92 | # Click-Once directory
93 | publish/
94 |
95 | # Publish Web Output
96 | *.Publish.xml
97 |
98 | # NuGet Packages Directory
99 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
100 | packages/
101 |
102 | # Windows Azure Build Output
103 | csx
104 | *.build.csdef
105 |
106 | # Windows Store app package directory
107 | AppPackages/
108 |
109 | # Others
110 | sql/
111 | *.Cache
112 | ClientBin/
113 | [Ss]tyle[Cc]op.*
114 | ~$*
115 | *~
116 | *.dbmdl
117 | *.[Pp]ublish.xml
118 | *.pfx
119 | *.publishsettings
120 |
121 | # RIA/Silverlight projects
122 | Generated_Code/
123 |
124 | # Backup & report files from converting an old project file to a newer
125 | # Visual Studio version. Backup files are not needed, because we have git ;-)
126 | _UpgradeReport_Files/
127 | Backup*/
128 | UpgradeLog*.XML
129 | UpgradeLog*.htm
130 |
131 | # SQL Server files
132 | App_Data/*.mdf
133 | App_Data/*.ldf
134 |
135 |
136 | #LightSwitch generated files
137 | GeneratedArtifacts/
138 | _Pvt_Extensions/
139 | ModelManifest.xml
140 |
141 | # =========================
142 | # Windows detritus
143 | # =========================
144 |
145 | # Windows image file caches
146 | Thumbs.db
147 | ehthumbs.db
148 |
149 | # Folder config file
150 | Desktop.ini
151 |
152 | # Recycle Bin used on file shares
153 | $RECYCLE.BIN/
154 |
155 | # Mac desktop service store files
156 | .DS_Store
157 |
--------------------------------------------------------------------------------
/MethodCache.Tests/GenericTests.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests
2 | {
3 | using System;
4 | using MethodCache.Tests.TestAssembly;
5 | using MethodCache.Tests.TestAssembly.Cache;
6 | using NUnit.Framework;
7 |
8 | public class GenericTests : ModuleWeaverTestsBase
9 | {
10 | [Test]
11 | public void GenericClassReferenceTypeExecutesCorrectly()
12 | {
13 | // Arrange
14 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
15 | dynamic instance = WeaverHelper.CreateInstance>>(Assembly, cache);
16 |
17 | // Act
18 | dynamic value1 = instance.Method1>(new Tuple(1, 2.0));
19 | value1 = instance.Method1>(new Tuple(1, 2.0));
20 | dynamic value2 = instance.Method2(new Tuple(1, 2.0));
21 | value2 = instance.Method2(new Tuple(1, 2.0));
22 |
23 | // Assert
24 | Assert.IsTrue(cache.NumStoreCalls == 2);
25 | Assert.IsTrue(cache.NumRetrieveCalls == 2);
26 | }
27 |
28 | [Test]
29 | public void GenericClassValueTypeExecutesCorrectly()
30 | {
31 | // Arrange
32 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
33 | dynamic instance = WeaverHelper.CreateInstance>(Assembly, cache);
34 |
35 | // Act
36 | dynamic value1 = instance.Method1(10);
37 | value1 = instance.Method1(10);
38 | dynamic value2 = instance.Method2(10);
39 | value2 = instance.Method2(10);
40 |
41 | // Assert
42 | Assert.IsTrue(cache.NumStoreCalls == 2);
43 | Assert.IsTrue(cache.NumRetrieveCalls == 2);
44 | }
45 |
46 | [Test]
47 | public void GenericClassValueTypePropertyExecutesCorrectly()
48 | {
49 | // Arrange
50 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
51 | dynamic instance = WeaverHelper.CreateInstance>(Assembly, cache);
52 |
53 | // Act
54 | dynamic value = instance.Property;
55 | value = instance.Property;
56 | instance.Property = value;
57 |
58 | // Assert
59 | Assert.IsTrue(cache.NumStoreCalls == 1);
60 | Assert.IsTrue(cache.NumRetrieveCalls == 1);
61 | Assert.IsTrue(cache.NumRemoveCalls == 1);
62 | }
63 |
64 | [Test]
65 | public void GenericClassReferenceTypePropertyExecutesCorrectly()
66 | {
67 | // Arrange
68 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
69 | dynamic instance = WeaverHelper.CreateInstance>>(Assembly, cache);
70 |
71 | // Act
72 | instance.Property = new Tuple(1, 2.0);
73 | dynamic value = instance.Property;
74 | value = instance.Property;
75 | instance.Property = value;
76 |
77 | // Assert
78 | Assert.IsTrue(cache.NumStoreCalls == 1);
79 | Assert.IsTrue(cache.NumRetrieveCalls == 1);
80 | Assert.IsTrue(cache.NumRemoveCalls == 2);
81 | }
82 |
83 | [Test]
84 | public void GenericMethod()
85 | {
86 | // Arrange
87 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
88 | dynamic instance = WeaverHelper.CreateInstance>>(Assembly, cache);
89 |
90 | // Act
91 | dynamic value1 = instance.Method1(0);
92 |
93 | // Assert
94 | Assert.IsTrue(cache.Contains(string.Format("MethodCache.Tests.TestAssembly.TestClassGeneric`1.Method1_{0}_{1}", typeof(int), 0)));
95 | }
96 | }
97 | }
--------------------------------------------------------------------------------
/MethodCache.Tests/MethodCacheDisabledAtConfigTests.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests
2 | {
3 | using System;
4 | using System.Reflection;
5 | using System.Xml.Linq;
6 | using MethodCache.Tests.TestAssembly;
7 | using MethodCache.Tests.TestAssembly.Cache;
8 | using NUnit.Framework;
9 |
10 | public class MethodCacheDisabledAtConfigTests : ModuleWeaverTestsBase
11 | {
12 | protected override XElement WeaverConfig
13 | {
14 | get { return new XElement("MethodCache", new XAttribute("CacheMethods", false)); }
15 | }
16 |
17 | [Test]
18 | public void ClassLevelCache_ShouldCacheMethodWhenAllMembersSelectedExplicitlyOnClassAttribute()
19 | {
20 | // Arrange
21 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
22 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
23 |
24 | // Act
25 | dynamic value = instance.Method(10);
26 | value = instance.Method(10);
27 |
28 | // Assert
29 | Assert.That(cache.NumStoreCalls, Is.EqualTo(1));
30 | Assert.That(cache.NumRetrieveCalls, Is.EqualTo(1));
31 | }
32 |
33 | [Test]
34 | public void ClassLevelCache_ShouldCacheMethodWhenSelectedExplicitlyOnClassAttribute()
35 | {
36 | // Arrange
37 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
38 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
39 |
40 | // Act
41 | dynamic value = instance.Method(10);
42 | value = instance.Method(10);
43 |
44 | // Assert
45 | Assert.That(cache.NumStoreCalls, Is.EqualTo(1));
46 | Assert.That(cache.NumRetrieveCalls, Is.EqualTo(1));
47 | }
48 |
49 | [Test]
50 | public void ClassLevelCache_ShouldNotCacheMethods()
51 | {
52 | // Arrange
53 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
54 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
55 |
56 | // Act
57 | dynamic value = instance.MethodOne(10);
58 | value = instance.MethodOne(10);
59 |
60 | // Assert
61 | Assert.That(cache.NumStoreCalls, Is.EqualTo(0));
62 | Assert.That(cache.NumRetrieveCalls, Is.EqualTo(0));
63 | }
64 |
65 | [Test]
66 | public void ClassLevelCache_ShouldNotCacheStaticMethods()
67 | {
68 | // Arrange
69 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
70 |
71 | Type testClassType = Assembly.GetType(typeof(TestClass8).FullName);
72 | Type cacheType = Assembly.GetType(typeof(ICache).FullName);
73 |
74 | PropertyInfo cacheProperty = testClassType.GetProperty("Cache", cacheType);
75 | cacheProperty.SetValue(null, cache, null);
76 |
77 | MethodInfo method = testClassType.GetMethod("MethodTwo", new[] { typeof(string) });
78 |
79 | // Act
80 | method.Invoke(null, new object[] { "1337" });
81 | method.Invoke(null, new object[] { "1337" });
82 |
83 | // Assert
84 | Assert.That(cache.NumStoreCalls, Is.EqualTo(0));
85 | Assert.That(cache.NumRetrieveCalls, Is.EqualTo(0));
86 | }
87 |
88 | [Test]
89 | public void IndividualCache_ShouldCacheMethods()
90 | {
91 | // Arrange
92 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
93 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
94 |
95 | // Act
96 | dynamic value = instance.MethodThree(10);
97 | value = instance.MethodThree(10);
98 |
99 | // Assert
100 | Assert.That(cache.NumStoreCalls, Is.EqualTo(1));
101 | Assert.That(cache.NumRetrieveCalls, Is.EqualTo(1));
102 | }
103 | }
104 | }
--------------------------------------------------------------------------------
/MethodCache.Fody/MethodCache.Fody.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {1A9A6C8E-6B88-4FF1-9447-E37CB39AAFDE}
8 | Library
9 | Properties
10 | MethodCache.Fody
11 | MethodCache.Fody
12 | v4.0
13 | 512
14 | ..\
15 | true
16 |
17 |
18 | true
19 | full
20 | false
21 | bin\Debug\
22 | DEBUG;TRACE
23 | prompt
24 | 4
25 |
26 |
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 |
34 |
35 |
36 | ..\packages\Mono.Cecil.0.9.5.4\lib\net40\Mono.Cecil.dll
37 |
38 |
39 | ..\packages\Mono.Cecil.0.9.5.4\lib\net40\Mono.Cecil.Mdb.dll
40 |
41 |
42 | ..\packages\Mono.Cecil.0.9.5.4\lib\net40\Mono.Cecil.Pdb.dll
43 |
44 |
45 | ..\packages\Mono.Cecil.0.9.5.4\lib\net40\Mono.Cecil.Rocks.dll
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | Members.cs
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
77 |
--------------------------------------------------------------------------------
/MethodCache.Tests.TestAssembly/MethodCache.Tests.TestAssembly.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | 8.0.30703
7 | 2.0
8 | {CBF07605-D646-45F7-B2B0-54DC33538644}
9 | Library
10 | Properties
11 | MethodCache.Tests.TestAssembly
12 | MethodCache.Tests.TestAssembly
13 | v4.0
14 | 512
15 |
16 |
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | pdbonly
27 | true
28 | bin\Release\
29 | TRACE
30 | prompt
31 | 4
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | {4F4D865D-8ABB-4554-84BB-2B7BC40E37F9}
75 | MethodCache.Attributes
76 |
77 |
78 |
79 |
86 |
--------------------------------------------------------------------------------
/MethodCache.Tests/PropertyCacheDisabledAtConfigTests.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests
2 | {
3 | using System;
4 | using System.Reflection;
5 | using System.Xml.Linq;
6 | using MethodCache.Tests.TestAssembly;
7 | using MethodCache.Tests.TestAssembly.Cache;
8 | using NUnit.Framework;
9 |
10 | public class PropertyCacheDisabledAtConfigTests : ModuleWeaverTestsBase
11 | {
12 | protected override XElement WeaverConfig
13 | {
14 | get { return new XElement("MethodCache", new XAttribute("CacheProperties", false)); }
15 | }
16 |
17 | [Test]
18 | public void ClassLevelCache_ShouldCachePropertiesWhenAllMembersSelectedExplicitlyOnClassAttribute()
19 | {
20 | // Arrange
21 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
22 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
23 |
24 | // Act
25 | dynamic value = instance.Property;
26 | value = instance.Property;
27 |
28 | // Assert
29 | Assert.That(cache.NumStoreCalls, Is.EqualTo(1));
30 | Assert.That(cache.NumRetrieveCalls, Is.EqualTo(1));
31 | }
32 |
33 | [Test]
34 | public void ClassLevelCache_ShouldCachePropertiesWhenSelectedExplicitlyOnClassAttribute()
35 | {
36 | // Arrange
37 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
38 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
39 |
40 | // Act
41 | dynamic value = instance.Property;
42 | value = instance.Property;
43 |
44 | // Assert
45 | Assert.That(cache.NumStoreCalls, Is.EqualTo(1));
46 | Assert.That(cache.NumRetrieveCalls, Is.EqualTo(1));
47 | }
48 |
49 | [Test]
50 | public void ClassLevelCache_ShouldNotCacheProperties()
51 | {
52 | // Arrange
53 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
54 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
55 |
56 | // Act
57 | dynamic value = instance.ReadOnlyProperty;
58 | value = instance.ReadOnlyProperty;
59 | value = instance.ReadOnlyNoCache;
60 | value = instance.ReadOnlyNoCache;
61 | value = instance.AutoProperty;
62 | value = instance.AutoProperty;
63 | value = instance.ReadWriteProperty;
64 | value = instance.ReadWriteProperty;
65 |
66 | // Assert
67 | Assert.That(cache.NumStoreCalls, Is.EqualTo(0));
68 | Assert.That(cache.NumRetrieveCalls, Is.EqualTo(0));
69 | }
70 |
71 | [Test]
72 | public void ClassLevelCache_ShouldNotCacheStaticReadOnlyProperties()
73 | {
74 | // Arrange
75 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
76 |
77 | Type testClassType = Assembly.GetType(typeof(TestClassStaticProperties).FullName);
78 | Type cacheType = Assembly.GetType(typeof(ICacheWithRemove).FullName);
79 |
80 | PropertyInfo cacheProperty = testClassType.GetProperty("Cache", cacheType);
81 | cacheProperty.SetValue(null, cache, null);
82 |
83 | PropertyInfo property = testClassType.GetProperty("ReadOnlyProperty");
84 |
85 | // Act
86 | object value = property.GetValue(null, null);
87 | value = property.GetValue(null, null);
88 |
89 | // Assert
90 | Assert.That(cache.NumStoreCalls, Is.EqualTo(0));
91 | Assert.That(cache.NumRetrieveCalls, Is.EqualTo(0));
92 | }
93 |
94 | [Test]
95 | public void ClassLevelCache_ShouldNotCacheStaticReadWriteProperties()
96 | {
97 | // Arrange
98 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
99 |
100 | Type testClassType = Assembly.GetType(typeof(TestClassStaticProperties).FullName);
101 | Type cacheType = Assembly.GetType(typeof(ICacheWithRemove).FullName);
102 |
103 | PropertyInfo cacheProperty = testClassType.GetProperty("Cache", cacheType);
104 | cacheProperty.SetValue(null, cache, null);
105 |
106 | PropertyInfo property = testClassType.GetProperty("ReadWriteProperty");
107 |
108 | // Act
109 | object value = property.GetValue(null, null);
110 | value = property.GetValue(null, null);
111 |
112 | // Assert
113 | Assert.That(cache.NumStoreCalls, Is.EqualTo(0));
114 | Assert.That(cache.NumRetrieveCalls, Is.EqualTo(0));
115 | }
116 |
117 | [Test]
118 | public void IndividualCache_ShouldCacheEligibleProperties()
119 | {
120 | // Arrange
121 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
122 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
123 |
124 | // Act
125 | dynamic value = instance.ReadOnlyProperty;
126 | value = instance.ReadOnlyProperty;
127 | value = instance.ReadWriteProperty;
128 | value = instance.ReadWriteProperty;
129 |
130 | // Assert
131 | Assert.That(cache.NumStoreCalls, Is.EqualTo(2));
132 | Assert.That(cache.NumRetrieveCalls, Is.EqualTo(2));
133 | }
134 | }
135 | }
--------------------------------------------------------------------------------
/MethodCache.Tests.TestAssembly/TestClassWithParameterizedCacheAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests.TestAssembly
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using MethodCache.Tests.TestAssembly.Cache;
6 | using MethodCache.Tests.TestAssembly.Cache.ComplexCacheAttribute;
7 |
8 | [Cache.Cache(Parameter1 = "CacheParameter", Parameter2 = 1)]
9 | public class TestClassWithParameterizedCacheAttribute
10 | {
11 | public TestClassWithParameterizedCacheAttribute(ICacheWithStoreParameters cache)
12 | {
13 | Cache = cache;
14 | }
15 |
16 | public ICacheWithStoreParameters Cache { get; set; }
17 |
18 | public string CacheParameterMethod(string argument1, int argument2)
19 | {
20 | return argument1 + argument2;
21 | }
22 |
23 | [Cache.Cache(Parameter3 = true, parameter3 = 0)]
24 | public string CacheParameterMethodWithField(string argument1, int argument2)
25 | {
26 | return argument1 + argument2;
27 | }
28 |
29 | [Cache.ComplexCacheAttribute.Cache(ParameterBoolArray = new bool[] { false, true },
30 | ParameterByteArray = new byte[] { Byte.MinValue, Byte.MaxValue },
31 | ParameterCharacterArray = new char[] { Char.MinValue, Char.MaxValue },
32 | ParameterDoubleArray = new double[] { Double.MinValue, Double.MaxValue },
33 | ParameterFloatArray = new float[] { Single.MinValue, Single.MaxValue },
34 | ParameterIntArray = new int[] { Int32.MinValue, Int32.MaxValue },
35 | ParameterLongArray = new long[] { Int64.MinValue, Int64.MaxValue },
36 | ParameterSByteArray = new sbyte[] { SByte.MinValue, SByte.MaxValue },
37 | ParameterShortArray = new short[] { Int16.MinValue, Int16.MaxValue },
38 | ParameterUIntArray = new uint[] { UInt32.MinValue, UInt32.MaxValue },
39 | ParameterULongArray = new ulong[] { UInt64.MinValue, UInt64.MaxValue },
40 | ParameterUShortArray = new ushort[] { UInt16.MinValue, UInt16.MaxValue })]
41 | public int ComplexCacheParameterMethodArrays()
42 | {
43 | return 0;
44 | }
45 |
46 | [Cache.ComplexCacheAttribute.Cache(ParameterEnum = EnumType.Entry1 | EnumType.Entry3)]
47 | public int ComplexCacheParameterMethodEnum()
48 | {
49 | return 0;
50 | }
51 |
52 | [Cache.ComplexCacheAttribute.Cache(
53 | ParameterEnumArray = new EnumType[] { EnumType.Entry1 | EnumType.Entry3, EnumType.Entry2 })]
54 | public int ComplexCacheParameterMethodEnumArray()
55 | {
56 | return 0;
57 | }
58 |
59 | [Cache.ComplexCacheAttribute.Cache(ParameterBool = false, ParameterByte = Byte.MaxValue,
60 | ParameterCharacter = Char.MaxValue, ParameterDouble = Double.MaxValue, ParameterFloat = Single.MaxValue,
61 | ParameterInt = Int32.MaxValue, ParameterLong = Int64.MaxValue, ParameterSByte = SByte.MaxValue,
62 | ParameterShort = Int16.MaxValue, ParameterUInt = UInt32.MaxValue, ParameterULong = UInt64.MaxValue,
63 | ParameterUShort = UInt16.MaxValue)]
64 | public int ComplexCacheParameterMethodMax()
65 | {
66 | return 0;
67 | }
68 |
69 | [Cache.ComplexCacheAttribute.Cache(ParameterBool = false, ParameterByte = Byte.MinValue,
70 | ParameterCharacter = Char.MinValue, ParameterDouble = Double.MinValue, ParameterFloat = Single.MinValue,
71 | ParameterInt = Int32.MinValue, ParameterLong = Int64.MinValue, ParameterSByte = SByte.MinValue,
72 | ParameterShort = Int16.MinValue, ParameterUInt = UInt32.MinValue, ParameterULong = UInt64.MinValue,
73 | ParameterUShort = UInt16.MinValue)]
74 | public int ComplexCacheParameterMethodMin()
75 | {
76 | return 0;
77 | }
78 |
79 | [Cache.ComplexCacheAttribute.Cache(
80 | ParameterObjectArray = new object[] { 1, "2", EnumType.Entry3, typeof(List), new object[] { 1, "2" } })]
81 | public int ComplexCacheParameterMethodObjectArray()
82 | {
83 | return 0;
84 | }
85 |
86 | [Cache.ComplexCacheAttribute.Cache(ParameterObject = new string[] { "1", "2" })]
87 | public int ComplexCacheParameterMethodObjectArrayString()
88 | {
89 | return 0;
90 | }
91 |
92 | [Cache.ComplexCacheAttribute.Cache(ParameterObject = EnumType.Entry1 | EnumType.Entry3)]
93 | public int ComplexCacheParameterMethodObjectEnum()
94 | {
95 | return 0;
96 | }
97 |
98 | [Cache.ComplexCacheAttribute.Cache(ParameterObject = typeof(List))]
99 | public int ComplexCacheParameterMethodObjectType()
100 | {
101 | return 0;
102 | }
103 |
104 | [Cache.ComplexCacheAttribute.Cache(ParameterObject = true)]
105 | public int ComplexCacheParameterMethodObjectValueType()
106 | {
107 | return 0;
108 | }
109 |
110 | [Cache.ComplexCacheAttribute.Cache(ParameterStringArray = new[] { "1", "2" })]
111 | public int ComplexCacheParameterMethodStringArray()
112 | {
113 | return 0;
114 | }
115 |
116 | [Cache.ComplexCacheAttribute.Cache(ParameterType = typeof(List))]
117 | public int ComplexCacheParameterMethodType()
118 | {
119 | return 0;
120 | }
121 |
122 | [Cache.ComplexCacheAttribute.Cache(ParameterTypeArray = new[] { typeof(List), typeof(List) })]
123 | public int ComplexCacheParameterMethodTypeArray()
124 | {
125 | return 0;
126 | }
127 |
128 | [Cache.Cache(Parameter2 = 2, Parameter3 = true)]
129 | public string OverridenCacheParameterMethod(string argument1, int argument2)
130 | {
131 | return argument1 + argument2;
132 | }
133 | }
134 | }
--------------------------------------------------------------------------------
/MethodCache.Tests/MethodCache.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | 8.0.30703
7 | 2.0
8 | {CF7379B0-EC94-4427-871E-DBD537959C09}
9 | Library
10 | Properties
11 | MethodCache.Tests
12 | MethodCache.Tests
13 | v4.0
14 | 512
15 | ..\
16 | true
17 |
18 |
19 | true
20 | full
21 | false
22 | bin\Debug\
23 | DEBUG;TRACE
24 | prompt
25 | 4
26 |
27 |
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 |
38 | ..\packages\Mono.Cecil.0.9.5.4\lib\net40\Mono.Cecil.dll
39 |
40 |
41 | ..\packages\Mono.Cecil.0.9.5.4\lib\net40\Mono.Cecil.Mdb.dll
42 |
43 |
44 | ..\packages\Mono.Cecil.0.9.5.4\lib\net40\Mono.Cecil.Pdb.dll
45 |
46 |
47 | ..\packages\Mono.Cecil.0.9.5.4\lib\net40\Mono.Cecil.Rocks.dll
48 |
49 |
50 | ..\packages\NUnitTestAdapter.1.0\lib\nunit.core.dll
51 | False
52 |
53 |
54 | ..\packages\NUnitTestAdapter.1.0\lib\nunit.core.interfaces.dll
55 | False
56 |
57 |
58 | ..\packages\NUnit.2.6.3\lib\nunit.framework.dll
59 |
60 |
61 | ..\packages\NUnitTestAdapter.1.0\lib\nunit.util.dll
62 | False
63 |
64 |
65 | ..\packages\NUnitTestAdapter.1.0\lib\NUnit.VisualStudio.TestAdapter.dll
66 | False
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 | {1A9A6C8E-6B88-4FF1-9447-E37CB39AAFDE}
95 | MethodCache.Fody
96 |
97 |
98 | {CBF07605-D646-45F7-B2B0-54DC33538644}
99 | MethodCache.Tests.TestAssembly
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
114 |
--------------------------------------------------------------------------------
/MethodCache.Fody/ReferenceFinder.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Fody
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using Mono.Cecil;
7 | using Mono.Cecil.Rocks;
8 |
9 | public class References
10 | {
11 | public IAssemblyResolver AssemblyResolver { get; set; }
12 |
13 | public TypeDefinition CompilerGeneratedAttribute { get; set; }
14 |
15 | public MethodDefinition DebugWriteLineMethod { get; set; }
16 |
17 | public MethodReference DictionaryAddMethod { get; set; }
18 |
19 | public MethodReference DictionaryConstructor { get; set; }
20 |
21 | public GenericInstanceType DictionaryGenericInstanceType { get; set; }
22 |
23 | public TypeDefinition DictionaryInterface { get; set; }
24 |
25 | public TypeDefinition DictionaryType { get; set; }
26 |
27 | public ModuleDefinition ModuleDefinition { get; set; }
28 |
29 | public MethodDefinition StringFormatMethod { get; set; }
30 |
31 | public MethodDefinition SystemTypeGetTypeFromHandleMethod { get; set; }
32 |
33 | public TypeDefinition SystemTypeType { get; set; }
34 |
35 | public void LoadReferences()
36 | {
37 | List coreTypes = new List();
38 |
39 | AppendTypes("System.Runtime.Extensions", coreTypes);
40 | AppendTypes("System", coreTypes);
41 | AppendTypes("mscorlib", coreTypes);
42 | AppendTypes("System.Runtime", coreTypes);
43 | AppendTypes("System.Reflection", coreTypes);
44 |
45 | TypeDefinition debugType = GetDebugType(coreTypes);
46 |
47 | DebugWriteLineMethod =
48 | debugType.Methods.First(
49 | method =>
50 | method.Matches("WriteLine", ModuleDefinition.TypeSystem.Void, new[] { ModuleDefinition.TypeSystem.String }));
51 |
52 | StringFormatMethod =
53 | ModuleDefinition.TypeSystem.String.Resolve()
54 | .Methods.First(
55 | method =>
56 | method.Matches("Format", ModuleDefinition.TypeSystem.String,
57 | new[] { ModuleDefinition.TypeSystem.String, ModuleDefinition.TypeSystem.Object.MakeArrayType() }));
58 |
59 | CompilerGeneratedAttribute = coreTypes.First(t => t.Name == "CompilerGeneratedAttribute");
60 |
61 | DictionaryInterface = GetDictionaryInterface(coreTypes);
62 |
63 | DictionaryType = GetDictionaryType(coreTypes);
64 | DictionaryGenericInstanceType = DictionaryType.MakeGenericInstanceType(ModuleDefinition.TypeSystem.String,
65 | ModuleDefinition.TypeSystem.Object);
66 |
67 | DictionaryConstructor =
68 | DictionaryGenericInstanceType.Resolve()
69 | .GetConstructors()
70 | .First(x => !x.Parameters.Any())
71 | .MakeHostInstanceGeneric(ModuleDefinition.TypeSystem.String, ModuleDefinition.TypeSystem.Object);
72 |
73 | DictionaryAddMethod =
74 | DictionaryGenericInstanceType.Resolve()
75 | .Methods.First(
76 | method =>
77 | method.Matches("Add", ModuleDefinition.TypeSystem.Void,
78 | new TypeReference[] { DictionaryType.GenericParameters[0], DictionaryType.GenericParameters[1] }))
79 | .MakeHostInstanceGeneric(ModuleDefinition.TypeSystem.String, ModuleDefinition.TypeSystem.Object);
80 |
81 | SystemTypeType = GetSystemTypeType(coreTypes);
82 |
83 | SystemTypeGetTypeFromHandleMethod =
84 | SystemTypeType.Resolve()
85 | .Methods.First(
86 | method =>
87 | method.Matches("GetTypeFromHandle", SystemTypeType,
88 | new TypeReference[] { GetSystemRuntimeTypeHandleType(coreTypes) }));
89 | }
90 |
91 | private void AppendTypes(string name, List coreTypes)
92 | {
93 | AssemblyDefinition definition = AssemblyResolver.Resolve(name);
94 | if (definition != null)
95 | {
96 | coreTypes.AddRange(definition.MainModule.Types);
97 | }
98 | }
99 |
100 | private TypeDefinition GetDebugType(List coreTypes)
101 | {
102 | TypeDefinition debugType = coreTypes.FirstOrDefault(x => x.Name == "Debug");
103 |
104 | if (debugType != null)
105 | {
106 | return debugType;
107 | }
108 |
109 | AssemblyDefinition systemDiagnosticsDebug = AssemblyResolver.Resolve("System.Diagnostics.Debug");
110 |
111 | if (systemDiagnosticsDebug != null)
112 | {
113 | debugType = systemDiagnosticsDebug.MainModule.Types.FirstOrDefault(x => x.Name == "Debug");
114 |
115 | if (debugType != null)
116 | {
117 | return debugType;
118 | }
119 | }
120 |
121 | throw new Exception("Could not find the 'Debug' type.");
122 | }
123 |
124 | private TypeDefinition GetDictionaryInterface(List coreTypes)
125 | {
126 | TypeDefinition dictionaryType = coreTypes.FirstOrDefault(x => x.Name == "IDictionary`2");
127 |
128 | if (dictionaryType != null)
129 | {
130 | return dictionaryType;
131 | }
132 |
133 | throw new Exception("Could not find the 'IDictionary' interface.");
134 | }
135 |
136 | private TypeDefinition GetDictionaryType(List coreTypes)
137 | {
138 | TypeDefinition dictionaryType = coreTypes.FirstOrDefault(x => x.Name == "Dictionary`2");
139 |
140 | if (dictionaryType != null)
141 | {
142 | return dictionaryType;
143 | }
144 |
145 | throw new Exception("Could not find the 'Dictionary' type.");
146 | }
147 |
148 | private TypeDefinition GetSystemRuntimeTypeHandleType(List coreTypes)
149 | {
150 | TypeDefinition runtimeTypeHandle = coreTypes.FirstOrDefault(x => x.Name == "RuntimeTypeHandle");
151 |
152 | if (runtimeTypeHandle != null)
153 | {
154 | return runtimeTypeHandle;
155 | }
156 |
157 | throw new Exception("Could not find the 'RuntimeHandle' type.");
158 | }
159 |
160 | private TypeDefinition GetSystemTypeType(List coreTypes)
161 | {
162 | TypeDefinition systemType = coreTypes.FirstOrDefault(x => x.Name == "Type");
163 |
164 | if (systemType != null)
165 | {
166 | return systemType;
167 | }
168 |
169 | throw new Exception("Could not find the 'SystemType' type.");
170 | }
171 | }
172 | }
--------------------------------------------------------------------------------
/.nuget/NuGet.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(MSBuildProjectDirectory)\..\
5 |
6 |
7 | false
8 |
9 |
10 | false
11 |
12 |
13 | true
14 |
15 |
16 | false
17 |
18 |
19 |
20 |
21 |
22 |
26 |
27 |
28 |
29 |
30 | $([System.IO.Path]::Combine($(SolutionDir), ".nuget"))
31 | $([System.IO.Path]::Combine($(ProjectDir), "packages.config"))
32 |
33 |
34 |
35 |
36 | $(SolutionDir).nuget
37 | packages.config
38 |
39 |
40 |
41 |
42 | $(NuGetToolsPath)\NuGet.exe
43 | @(PackageSource)
44 |
45 | "$(NuGetExePath)"
46 | mono --runtime=v4.0.30319 $(NuGetExePath)
47 |
48 | $(TargetDir.Trim('\\'))
49 |
50 | -RequireConsent
51 | -NonInteractive
52 |
53 | "$(SolutionDir) "
54 | "$(SolutionDir)"
55 |
56 |
57 | $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)
58 | $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols
59 |
60 |
61 |
62 | RestorePackages;
63 | $(BuildDependsOn);
64 |
65 |
66 |
67 |
68 | $(BuildDependsOn);
69 | BuildPackage;
70 |
71 |
72 |
73 |
74 |
75 |
76 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
91 |
92 |
95 |
96 |
97 |
98 |
100 |
101 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
133 |
134 |
135 |
136 |
--------------------------------------------------------------------------------
/Settings.StyleCop:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | False
8 |
9 |
10 |
11 |
12 | False
13 |
14 |
15 |
16 |
17 | False
18 |
19 |
20 |
21 |
22 | False
23 |
24 |
25 |
26 |
27 | False
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | False
38 |
39 |
40 |
41 |
42 | False
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | True
53 |
54 |
55 |
56 |
57 | False
58 |
59 |
60 |
61 |
62 | False
63 |
64 |
65 |
66 |
67 | False
68 |
69 |
70 |
71 |
72 | False
73 |
74 |
75 |
76 |
77 | False
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | False
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 | False
98 |
99 |
100 |
101 |
102 | $(AaBb)
103 | $(AaBb)
104 | $(aaBb)
105 | $(aaBb)
106 | $(aaBb)
107 | $(aaBb)
108 | $(AaBb)
109 | $(AaBb)
110 | $(AaBb)
111 | NotEmpty
112 | $(AaBb):$(AaBb)_$(AaBb)
113 | 3D A B C FxCop ID IDs IL OK PE StyleCop X
114 |
115 |
116 |
117 |
118 |
119 |
120 | False
121 |
122 |
123 |
124 |
125 | False
126 |
127 |
128 |
129 |
130 | False
131 |
132 |
133 |
134 |
135 | False
136 |
137 |
138 |
139 |
140 | False
141 |
142 |
143 |
144 |
145 | False
146 |
147 |
148 |
149 |
150 | False
151 |
152 |
153 |
154 |
155 | False
156 |
157 |
158 |
159 |
160 | False
161 |
162 |
163 |
164 |
165 | False
166 |
167 |
168 |
169 |
170 | False
171 |
172 |
173 |
174 |
175 | False
176 |
177 |
178 |
179 |
180 |
181 |
182 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Migrated to SpatialFocus.MethodCache
2 |
3 | We have made a complete rewrite of MethodCache that supports latest version of Fody . Please find the new version [here](https://github.com/SpatialFocus/MethodCache.Fody/).
4 |
5 | ----
6 |
7 | ## This is an add-in for [Fody](https://github.com/Fody/Fody/)
8 |
9 | Caches return values of methods and properties decorated with a `[Cache]` Attribute.
10 |
11 | [Introduction to Fody](http://github.com/Fody/Fody/wiki/SampleUsage).
12 |
13 | # Nuget package
14 |
15 | There is a nuget package avaliable here http://nuget.org/packages/MethodCache.Fody.
16 |
17 | ## Your Code
18 |
19 | [Cache]
20 | public int Add(int a, int b)
21 | {
22 | return a + b;
23 | }
24 |
25 | [Cache]
26 | public string AlsoWorksForProperties
27 | {
28 | get
29 | {
30 | return DoSomeCalculations(this.parameterField);
31 | }
32 | set
33 | {
34 | this.parameterField = value;
35 | }
36 | }
37 |
38 | ## What gets compiled
39 |
40 | public int Add(int a, int b)
41 | {
42 | string cacheKey = string.Format("Namespace.Class.Add_{0}_{1}", new object[] { a, b });
43 |
44 | if(Cache.Contains(cacheKey))
45 | {
46 | return Cache.Retrieve(cacheKey);
47 | }
48 |
49 | int result = a + b;
50 |
51 | Cache.Store(cacheKey, result);
52 |
53 | return result;
54 | }
55 |
56 | public object AlsoWorksForProperties
57 | {
58 | get
59 | {
60 | string cacheKey = "Namespace.Class.AlsoWorksForProperties";
61 |
62 | if(Cache.Contains(cacheKey))
63 | {
64 | return Cache.Retrieve(cacheKey);
65 | }
66 |
67 | object result = DoSomeCalculations(this.parameterField);
68 |
69 | Cache.Store(cacheKey, result);
70 |
71 | return result;
72 | }
73 | set
74 | {
75 | string cacheKey = "Namespace.Class.AlsoWorksForProperties";
76 | Cache.Remove(cacheKey);
77 | this.parameterField = value;
78 | }
79 | }
80 |
81 | # How to use
82 |
83 | * Install MethodCache.Fody via Nuget
84 | * Create an Cache Implementation (MemCache, FileCache, DBCache, ...) which implements Contains, Retrieve, Store and Remove methods.
85 |
86 | Optional
87 |
88 | * Add your own CacheAttribute and NoCacheAttribute to your Solution to decorate your methods or classes (you can use the existing attributes defined in MethodCache.Attributes).
89 |
90 | ## Example
91 |
92 | DictionaryCache (in-memory implementation):
93 |
94 | public class DictionaryCache
95 | {
96 | public DictionaryCache()
97 | {
98 | Storage = new Dictionary();
99 | }
100 |
101 | private Dictionary Storage { get; set; }
102 |
103 | // Note: The methods Contains, Retrieve, Store (and Remove) must exactly look like the following:
104 |
105 | public bool Contains(string key)
106 | {
107 | return Storage.ContainsKey(key);
108 | }
109 |
110 | public T Retrieve(string key)
111 | {
112 | return (T)Storage[key];
113 | }
114 |
115 | public void Store(string key, object data)
116 | {
117 | Storage[key] = data;
118 | }
119 |
120 | // Remove is needed for writeable properties which must invalidate the Cache
121 | // You can skip this method but then only readonly properties are supported
122 | public void Remove(string key)
123 | {
124 | Storage.Remove(key);
125 | }
126 | }
127 |
128 | Now all the preparation is done and you can start with the real work. The classes you want to cache must contain an Cache Getter (can also be inherited from a baseclass). Let's start decorating ...
129 |
130 | // Mark the class to enable caching of every method ...
131 |
132 | [Cache]
133 | public class ClassToCache
134 | {
135 | public ClassToCache()
136 | {
137 | // Consider using constructor or property injection instead
138 | Cache = new DictionaryCache();
139 | }
140 |
141 | // Consider using ICache Interface
142 | private DictionaryCache Cache { get; set; }
143 |
144 | // This method will be cached
145 | public int Add(int a, int b)
146 | {
147 | return a + b;
148 | }
149 |
150 | // This method will be cached too
151 | public string Concat(string a, string b)
152 | {
153 | return a + b;
154 | }
155 |
156 | // This method will not be cached
157 | [NoCache]
158 | public int ThirdMethod(int x)
159 | {
160 | return x * x;
161 | }
162 |
163 | public string AlsoWorksForProperties
164 | {
165 | get
166 | {
167 | return DoSomeCalculations();
168 | }
169 | }
170 |
171 | [NoCache]
172 | public string AlsoPropertiesCanBeIgnored
173 | {
174 | get
175 | {
176 | return 10;
177 | }
178 | }
179 | }
180 |
181 | // or mark the methods you want to cache explicitly.
182 |
183 | public class ClassToCache
184 | {
185 | public ClassToCache(ICache cache)
186 | {
187 | // Consider using constructor or property injection instead
188 | Cache = new DictionaryCache();
189 | }
190 |
191 | private ICache Cache { get; set; }
192 |
193 | [Cache] // Only this method ...
194 | public int Add(int a, int b)
195 | {
196 | return a + b;
197 | }
198 |
199 | public string Concat(string a, string b)
200 | {
201 | return a + b;
202 | }
203 |
204 | public int ThirdMethod(int x)
205 | {
206 | return x * x;
207 | }
208 |
209 | [Cache] // ... and this properties will be cached
210 | public string AlsoWorksForProperties
211 | {
212 | get
213 | {
214 | return DoSomeCalculations();
215 | }
216 | }
217 | }
218 |
219 | ... and let MethodCache do the rest.
220 |
221 | ## CacheAttribute Parameters Support (since 1.5)
222 |
223 | For fine-grained cache control you can use a custom `CacheAttribute` and add properties or fields which you can set on class or method level. This parameters will be passed to the Store function.
224 |
225 | See the following example which uses the `System.Runtime.Caching.ObjectCache` in combination with `System.Runtime.Caching.CacheItemPolicy` and a custom `CacheAttribute` with a property named Duration to control the cache duration.
226 |
227 | // Use a custom CacheAttribute
228 |
229 | public class CacheAttribute : Attribute
230 | {
231 | // You can use all types that are valid Attribute parameter types
232 | public int Duration { get; set; }
233 | }
234 |
235 | // Decorate you classes
236 |
237 | public class ClassToCache
238 | {
239 | public ClassToCache(ICache cache)
240 | {
241 | Cache = cache;
242 | }
243 |
244 | protected ICache Cache { get; set; }
245 |
246 | // Note: Only works when set as named parameter
247 | [Cache(Duration = 5000)]
248 | public int ShortTermFunction()
249 | {
250 | return 0;
251 | }
252 |
253 | [Cache]
254 | public int LongTermFunction()
255 | {
256 | return 0;
257 | }
258 | }
259 |
260 | // Use the new void Store(string key, object data, IDictionary parameters) signature
261 |
262 | public class ObjectCacheAdapter
263 | {
264 | public ObjectCacheAdapter(ObjectCache objectCache)
265 | {
266 | ObjectCache = objectCache;
267 | }
268 |
269 | protected ObjectCache ObjectCache { get; set; }
270 |
271 | // Note: The methods Contains, Retrieve, Store (and Remove) must exactly look like the following:
272 | public bool Contains(string key)
273 | {
274 | return ObjectCache.Contains(key);
275 | }
276 |
277 | public T Retrieve(string key)
278 | {
279 | return (T) ObjectCache.GetCacheItem(key).Value;
280 | }
281 |
282 | public void Store(string key, object data, IDictionary parameters)
283 | {
284 | CacheItemPolicy cacheItemPolicy = null;
285 |
286 | // Create a CacheItemPolicy instance if Duration is set
287 | if (parameters.ContainsKey("Duration"))
288 | {
289 | cacheItemPolicy = new CacheItemPolicy()
290 | {
291 | AbsoluteExpiration = DateTimeOffset.Now.AddMilliseconds((int)parameters["Duration"])
292 | };
293 | }
294 |
295 | ObjectCache.Add(key, data, cacheItemPolicy);
296 | }
297 |
298 | // Remove is needed for writeable properties which must invalidate the Cache
299 | // You can skip this method but then only readonly properties are supported
300 | public void Remove(string key)
301 | {
302 | ObjectCache.Remove(key);
303 | }
304 | }
305 |
306 | Class level named attribute parameters are overriden by method level attribute parameters (even if not set):
307 |
308 | [Cache.Cache(Parameter1 = "CacheParameter", Parameter2 = 1)]
309 | public class TestClassWithParameterizedCacheAttribute
310 | {
311 | // Store calls for this method will contain Parameter1 and Parameter2
312 | public int SomeMethod()
313 | {
314 | return 0;
315 | }
316 |
317 | // Store calls for this method will contain Parameter2 and Parameter3
318 | [Cache.Cache(Parameter2 = 2, Parameter3 = true)]
319 | public int SomeOverriddenMethod()
320 | {
321 | return 0;
322 | }
323 | }
324 |
325 | ## Miscellaneous
326 |
327 | ### Choosing what to cache
328 |
329 | Class' methods and properties are selected for caching using the following algorithm:
330 |
331 | 1. For each member
332 | 1. If it is marked with `[Cache]` attribute, **cache it**
333 | 2. If it is marked with `[NoCache]` attribute, **don't cache it**
334 | 2. If it is a method
335 | 2. If it the class is marked with `[Cache(Members.Methods)]` or `[Cache(Members.All)]`, **cache it**
336 | 3. It it the class is marked with `[Cache]` attribute and methods are not disabled in XML config, **cache it**
337 | 3. If it is a property
338 | 1. If it is an auto-property, **don't cache it**
339 | 2. If it is a write-only property, **don't cache it**
340 | 3. If it is the `Cache` property, **don't cache it**
341 | 2. If it the class is marked with `[Cache(Members.Properties)]` or `[Cache(Members.All)]`, **cache it**
342 | 3. It it the class is marked with `[Cache]` attribute and properties are not disabled in XML config, **cache it**
343 | 4. Otherwise, **don't cache it**
344 |
345 | For disabling method or property caching per default (say you only want to cache methods when using `[Cache]` on class level) edit the XML settings in ModuleWeavers.xml:
346 |
347 | ``
348 |
349 | Notes
350 | * `[Cache(Members.Methods | Members.Properties)]` can be used in place of `[Cache(Members.All)]`.
351 | * Arguments passed to `[Cache]` attribute will be ignored on individual members
352 |
353 | ### Improvements
354 |
355 | For production I would suggest using some DI framework and creating an ICache interface:
356 |
357 | public interface ICache
358 | {
359 | bool Contains(string key);
360 |
361 | T Retrieve(string key);
362 |
363 | void Store(string key, object data);
364 |
365 | // For CacheAttribute parameter support use the following
366 | // void Store(string key, object data, IDictionary parameters)
367 |
368 | void Remove(string key);
369 | }
370 |
371 | public class MyService
372 | {
373 | // Constructor injection
374 | public MyService(ICache cache)
375 | {
376 | Cache = cache;
377 | }
378 |
379 | protected ICache Cache { get; set; }
380 | }
381 |
382 | ### Runtime Debug messages
383 |
384 | When compiled in Debug mode, MethodCache outputs Cache information with Debug.WriteLine:
385 |
386 | CacheKey created: MethodCache.TestAssembly.TestClass1.MethodOne_1337
387 | Storing to cache.
388 | CacheKey created: MethodCache.TestAssembly.TestClass1.MethodOne_1337
389 | Loading from cache.
390 | ...
391 |
392 | If you do not like this behaviour (e.g. if lots of messages slow down the application), add SkipDebugOutput="false" to ModuleWeavers.xml.
393 |
394 | ### Enable Weaving Build Messages
395 |
396 | Be default, only warnings like a missing Cache Getter are shown in the build log. To enable detailed information, modify the following line in Fody.targets
397 |
398 | From
399 |
400 | Low
401 |
402 | To
403 |
404 | High
405 |
406 | You will now see detailed weaving information in the build log:
407 |
408 | Searching for Methods in assembly (MethodCache.TestAssembly.dll).
409 | Weaving method TestClassOne::MethodOne.
410 | Checking CacheType methods (Contains, Store, Retrieve).
411 | CacheType methods found.
412 | ...
413 |
414 | ### Credits
415 |
416 | Thanks to [Tomasz Pluskiewicz](https://github.com/tpluscode) for his contribution to support caching of properties.
417 |
--------------------------------------------------------------------------------
/MethodCache.Tests/ModuleWeaverTests.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests
2 | {
3 | using System;
4 | using System.Linq;
5 | using System.Reflection;
6 | using MethodCache.Fody;
7 | using MethodCache.Tests.TestAssembly;
8 | using MethodCache.Tests.TestAssembly.Cache;
9 | using NUnit.Framework;
10 |
11 | public class ModuleWeaverTests : ModuleWeaverTestsBase
12 | {
13 | [Test]
14 | public void ClassLevelCacheMethodsExcluded()
15 | {
16 | // Arrange
17 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
18 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
19 |
20 | // Act
21 | dynamic value = instance.Method(10);
22 | value = instance.Method(10);
23 |
24 | // Assert
25 | Assert.IsTrue(cache.NumStoreCalls == 0);
26 | Assert.IsTrue(cache.NumRetrieveCalls == 0);
27 | }
28 |
29 | [Test]
30 | public void ClassLevelCacheMethodsExcludedChooseSelectively()
31 | {
32 | // Arrange
33 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
34 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
35 |
36 | // Act
37 | dynamic value = instance.MethodIncluded(10);
38 | value = instance.MethodIncluded(10);
39 |
40 | // Assert
41 | Assert.IsTrue(cache.NumStoreCalls == 1);
42 | Assert.IsTrue(cache.NumRetrieveCalls == 1);
43 | }
44 |
45 | [Test]
46 | public void ClassLevelCacheMethodsWhenPropertiesExcluded()
47 | {
48 | // Arrange
49 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
50 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
51 |
52 | // Act
53 | dynamic value = instance.Method(10);
54 | value = instance.Method(10);
55 |
56 | // Assert
57 | Assert.IsTrue(cache.NumStoreCalls == 1);
58 | Assert.IsTrue(cache.NumRetrieveCalls == 1);
59 | }
60 |
61 | [Test]
62 | public void ModuleWeaverExecuteWeavesCorrectIL()
63 | {
64 | string assemblyPath = Assembly.CodeBase.Remove(0, 8);
65 | string result = Verifier.Verify(assemblyPath);
66 |
67 | Assert.IsTrue(result.Contains(string.Format("All Classes and Methods in {0} Verified.", assemblyPath)));
68 | }
69 |
70 | [Test]
71 | public void ModuleWeaverRemovesMethodCacheAttributeReference()
72 | {
73 | Assert.IsFalse(Assembly.GetReferencedAssemblies().Any(x => x.Name == "MethodCache.Attributes"));
74 | }
75 |
76 | [Test]
77 | public void TestClass1ClassLevelCacheAttributeCachedMethodTwoReturnsSameInstance()
78 | {
79 | // Arrange
80 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
81 | dynamic testClass1 = WeaverHelper.CreateInstance(Assembly, cache);
82 |
83 | // Act
84 | dynamic instance1 = testClass1.MethodTwo("1337");
85 | dynamic instance2 = testClass1.MethodTwo("1337");
86 |
87 | // Assert
88 | Assert.IsTrue(ReferenceEquals(instance1, instance2));
89 | }
90 |
91 | [Test]
92 | public void TestClass1ClassLevelCacheAttributeCachesMethodOne()
93 | {
94 | // Arrange
95 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
96 | dynamic testClass1 = WeaverHelper.CreateInstance(Assembly, cache);
97 |
98 | // Act
99 | testClass1.MethodOne(1337);
100 | testClass1.MethodOne(1337);
101 |
102 | // Assert
103 | Assert.IsTrue(cache.NumStoreCalls == 1);
104 | Assert.IsTrue(cache.NumRetrieveCalls == 1);
105 | }
106 |
107 | [Test]
108 | public void TestClass1ClassLevelCacheAttributeCachesMethodTwo()
109 | {
110 | // Arrange
111 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
112 | dynamic testClass1 = WeaverHelper.CreateInstance(Assembly, cache);
113 |
114 | // Act
115 | testClass1.MethodTwo("1337");
116 | testClass1.MethodTwo("1337");
117 |
118 | // Assert
119 | Assert.IsTrue(cache.NumStoreCalls == 1);
120 | Assert.IsTrue(cache.NumRetrieveCalls == 1);
121 | }
122 |
123 | [Test]
124 | public void TestClass1ClassLevelCacheAttributeSuccessfullyRemoved()
125 | {
126 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
127 | dynamic testClass1 = WeaverHelper.CreateInstance(Assembly, cache);
128 |
129 | Type classType = testClass1.GetType();
130 |
131 | Assert.IsFalse(classType.GetCustomAttributes(true).Any(x => x.GetType().Name == ModuleWeaver.CacheAttributeName));
132 | }
133 |
134 | [Test]
135 | public void TestClass2MethodLevelCacheAttributeCachesMethodOne()
136 | {
137 | // Arrange
138 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
139 | dynamic testClass2 = WeaverHelper.CreateInstance(Assembly, cache);
140 |
141 | // Act
142 | testClass2.MethodOne(1337);
143 | testClass2.MethodOne(1337);
144 |
145 | // Assert
146 | Assert.IsTrue(cache.NumStoreCalls == 1);
147 | Assert.IsTrue(cache.NumRetrieveCalls == 1);
148 | }
149 |
150 | [Test]
151 | public void TestClass2MethodLevelCacheAttributeDoesNotCachesMethodTwo()
152 | {
153 | // Arrange
154 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
155 | dynamic testClass2 = WeaverHelper.CreateInstance(Assembly, cache);
156 |
157 | // Act
158 | testClass2.MethodTwo("1337");
159 | testClass2.MethodTwo("1337");
160 |
161 | // Assert
162 | Assert.IsTrue(cache.NumStoreCalls == 0);
163 | Assert.IsTrue(cache.NumRetrieveCalls == 0);
164 | }
165 |
166 | [Test]
167 | public void TestClass2MethodLevelCacheAttributeNotCachedMethodTwoReturnsDifferentInstances()
168 | {
169 | // Arrange
170 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
171 | dynamic testClass2 = WeaverHelper.CreateInstance(Assembly, cache);
172 |
173 | // Act
174 | dynamic instance1 = testClass2.MethodTwo("1337");
175 | dynamic instance2 = testClass2.MethodTwo("1337");
176 |
177 | // Assert
178 | Assert.IsTrue(!ReferenceEquals(instance1, instance2));
179 | }
180 |
181 | [Test]
182 | public void TestClass2MethodLevelCacheAttributeSuccessfullyRemoved()
183 | {
184 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
185 | dynamic testClass2 = WeaverHelper.CreateInstance(Assembly, cache);
186 |
187 | Type classType = testClass2.GetType();
188 | MethodInfo methodInfo = classType.GetMethod("MethodOne");
189 |
190 | Assert.IsFalse(methodInfo.GetCustomAttributes(true).Any(x => x.GetType().Name == ModuleWeaver.CacheAttributeName));
191 | }
192 |
193 | [Test]
194 | public void TestClass3ConcreteCacheGetterCachesMethodOne()
195 | {
196 | // Arrange
197 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
198 | dynamic testClass2 = WeaverHelper.CreateInstance(Assembly, cache);
199 |
200 | // Act
201 | testClass2.MethodOne(1337);
202 | testClass2.MethodOne(1337);
203 |
204 | // Assert
205 | Assert.IsTrue(cache.NumStoreCalls == 1);
206 | Assert.IsTrue(cache.NumRetrieveCalls == 1);
207 | }
208 |
209 | [Test]
210 | public void TestClass3ConcreteCacheGetterCachesMethodTwo()
211 | {
212 | // Arrange
213 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
214 | dynamic testClass2 = WeaverHelper.CreateInstance(Assembly, cache);
215 |
216 | // Act
217 | testClass2.MethodTwo("1337");
218 | testClass2.MethodTwo("1337");
219 |
220 | // Assert
221 | Assert.IsTrue(cache.NumStoreCalls == 1);
222 | Assert.IsTrue(cache.NumRetrieveCalls == 1);
223 | }
224 |
225 | [Test]
226 | public void TestClass4MissingCacheGetterSuccessfullyIgnored()
227 | {
228 | // Arrange
229 | dynamic testClass4 = WeaverHelper.CreateInstance(Assembly);
230 |
231 | // Act
232 | testClass4.MethodOne(1337);
233 |
234 | // Assert (test should not throw any exceptions)
235 | }
236 |
237 | [Test]
238 | public void TestClass5WrongCacheGetterTypeSuccessfullyIgnored()
239 | {
240 | // Arrange
241 | dynamic testClass5 = WeaverHelper.CreateInstance(Assembly);
242 |
243 | // Act
244 | testClass5.MethodOne(1337);
245 |
246 | // Assert (test should not throw any exceptions)
247 | }
248 |
249 | [Test]
250 | public void TestClass6InheritedCacheGetterCanBeUsed()
251 | {
252 | // Arrange
253 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
254 | dynamic testClass6 = WeaverHelper.CreateInstance(Assembly, cache);
255 |
256 | // Act
257 | testClass6.MethodThree(1337);
258 | testClass6.MethodThree(1337);
259 |
260 | // Assert
261 | Assert.IsTrue(cache.NumStoreCalls == 1);
262 | Assert.IsTrue(cache.NumRetrieveCalls == 1);
263 | }
264 |
265 | [Test]
266 | public void TestClass7MethodLevelNoCacheAttributeDoesNotCachesMethodTwo()
267 | {
268 | // Arrange
269 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
270 | dynamic testClass7 = WeaverHelper.CreateInstance(Assembly, cache);
271 |
272 | // Act
273 | testClass7.MethodTwo("1337");
274 | testClass7.MethodTwo("1337");
275 |
276 | // Assert
277 | Assert.IsTrue(cache.NumStoreCalls == 0);
278 | Assert.IsTrue(cache.NumRetrieveCalls == 0);
279 | }
280 |
281 | [Test]
282 | public void TestClass7MethodLevelNoCacheAttributeNotCachedMethodTwoReturnsDifferentInstances()
283 | {
284 | // Arrange
285 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
286 | dynamic testClass7 = WeaverHelper.CreateInstance(Assembly, cache);
287 |
288 | // Act
289 | dynamic instance1 = testClass7.MethodTwo("1337");
290 | dynamic instance2 = testClass7.MethodTwo("1337");
291 |
292 | // Assert
293 | Assert.IsTrue(!ReferenceEquals(instance1, instance2));
294 | }
295 |
296 | [Test]
297 | public void TestClass7MethodLevelNoCacheAttributeSuccessfullyRemoved()
298 | {
299 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
300 | dynamic testClass7 = WeaverHelper.CreateInstance(Assembly, cache);
301 |
302 | Type classType = testClass7.GetType();
303 | MethodInfo methodInfo = classType.GetMethod("MethodTwo");
304 |
305 | Assert.IsFalse(methodInfo.GetCustomAttributes(true).Any(x => x.GetType().Name == ModuleWeaver.NoCacheAttributeName));
306 | }
307 |
308 | [Test]
309 | public void TestClass8ClassLevelCacheAttributeCachedMethodTwoReturnsSameInstance()
310 | {
311 | // Arrange
312 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
313 |
314 | Type testClassType = Assembly.GetType(typeof(TestClass8).FullName);
315 | Type cacheType = Assembly.GetType(typeof(ICache).FullName);
316 |
317 | PropertyInfo cacheProperty = testClassType.GetProperty("Cache", cacheType);
318 | cacheProperty.SetValue(null, cache, null);
319 |
320 | MethodInfo method = testClassType.GetMethod("MethodTwo", new[] { typeof(string) });
321 |
322 | // Act
323 | dynamic instance1 = method.Invoke(null, new object[] { "1337" });
324 | dynamic instance2 = method.Invoke(null, new object[] { "1337" });
325 |
326 | // Assert
327 | Assert.IsTrue(ReferenceEquals(instance1, instance2));
328 | }
329 |
330 | [Test]
331 | public void TestClass8ClassLevelCacheAttributeCachesMethodOne()
332 | {
333 | // Arrange
334 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
335 |
336 | Type testClassType = Assembly.GetType(typeof(TestClass8).FullName);
337 | Type cacheType = Assembly.GetType(typeof(ICache).FullName);
338 |
339 | PropertyInfo cacheProperty = testClassType.GetProperty("Cache", cacheType);
340 | cacheProperty.SetValue(null, cache, null);
341 |
342 | MethodInfo method = testClassType.GetMethod("MethodOne", new[] { typeof(int) });
343 |
344 | // Act
345 | method.Invoke(null, new object[] { 1337 });
346 | method.Invoke(null, new object[] { 1337 });
347 |
348 | // Assert
349 | Assert.IsTrue(cache.NumStoreCalls == 1);
350 | Assert.IsTrue(cache.NumRetrieveCalls == 1);
351 | }
352 |
353 | [Test]
354 | public void TestClass8ClassLevelCacheAttributeCachesMethodTwo()
355 | {
356 | // Arrange
357 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
358 |
359 | Type testClassType = Assembly.GetType(typeof(TestClass8).FullName);
360 | Type cacheType = Assembly.GetType(typeof(ICache).FullName);
361 |
362 | PropertyInfo cacheProperty = testClassType.GetProperty("Cache", cacheType);
363 | cacheProperty.SetValue(null, cache, null);
364 |
365 | MethodInfo method = testClassType.GetMethod("MethodTwo", new[] { typeof(string) });
366 |
367 | // Act
368 | method.Invoke(null, new object[] { "1337" });
369 | method.Invoke(null, new object[] { "1337" });
370 |
371 | // Assert
372 | Assert.IsTrue(cache.NumStoreCalls == 1);
373 | Assert.IsTrue(cache.NumRetrieveCalls == 1);
374 | }
375 | }
376 | }
--------------------------------------------------------------------------------
/MethodCache.Tests/PropertyCachingTests.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests
2 | {
3 | using System;
4 | using System.Reflection;
5 | using MethodCache.Tests.TestAssembly;
6 | using MethodCache.Tests.TestAssembly.Cache;
7 | using NUnit.Framework;
8 |
9 | public class PropertyCachingTests : ModuleWeaverTestsBase
10 | {
11 | [Test]
12 | public void ClassLevelCacheWithoutRemove_ShouldCacheReadOnlyProperties()
13 | {
14 | // Arrange
15 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
16 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
17 |
18 | // Act
19 | dynamic value = instance.ReadOnlyProperty;
20 | value = instance.ReadOnlyProperty;
21 |
22 | // Assert
23 | Assert.IsTrue(cache.NumStoreCalls == 1);
24 | Assert.IsTrue(cache.NumRetrieveCalls == 1);
25 | }
26 |
27 | [Test]
28 | public void ClassLevelCacheWithoutRemove_ShouldNotCacheReadWriteProperty()
29 | {
30 | // Arrange
31 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
32 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
33 |
34 | // Act
35 | dynamic value = instance.ReadWriteProperty;
36 | value = instance.ReadWriteProperty;
37 |
38 | // Assert
39 | Assert.IsTrue(cache.NumStoreCalls == 0);
40 | Assert.IsTrue(cache.NumRetrieveCalls == 0);
41 | }
42 |
43 | [Test]
44 | public void ClassLevelCache_SettingPropertyShouldInvalidateCache()
45 | {
46 | // Arrange
47 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
48 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
49 |
50 | // Act
51 | dynamic value = instance.ReadWriteProperty;
52 | instance.ReadWriteProperty = 10;
53 |
54 | // Assert
55 | Assert.That(cache.Contains("MethodCache.Tests.TestAssembly.TestClassWithProperties.ReadWriteProperty"), Is.False);
56 | }
57 |
58 | [Test]
59 | public void ClassLevelCache_ShouldBePossibleToDisablePropertyWeaving()
60 | {
61 | // Arrange
62 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
63 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
64 |
65 | // Act
66 | dynamic value = instance.ReadOnlyProperty;
67 | value = instance.ReadOnlyProperty;
68 |
69 | // Assert
70 | Assert.IsTrue(cache.NumStoreCalls == 0);
71 | Assert.IsTrue(cache.NumRetrieveCalls == 0);
72 | }
73 |
74 | [Test]
75 | public void ClassLevelCache_ShouldBePossibleToSelectivelyEnablePropertyWeaving()
76 | {
77 | // Arrange
78 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
79 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
80 |
81 | // Act
82 | dynamic value = instance.ExplicitlyCachedProperty;
83 | value = instance.ExplicitlyCachedProperty;
84 |
85 | // Assert
86 | Assert.IsTrue(cache.NumStoreCalls == 1);
87 | Assert.IsTrue(cache.NumRetrieveCalls == 1);
88 | }
89 |
90 | [Test]
91 | public void ClassLevelCache_ShouldCachePropertyWhenMethodsAreExcluded()
92 | {
93 | // Arrange
94 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
95 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
96 |
97 | // Act
98 | dynamic value = instance.Property;
99 | value = instance.Property;
100 |
101 | // Assert
102 | Assert.IsTrue(cache.NumStoreCalls == 1);
103 | Assert.IsTrue(cache.NumRetrieveCalls == 1);
104 | }
105 |
106 | [Test]
107 | public void ClassLevelCache_ShouldCacheReadOnlyProperties()
108 | {
109 | // Arrange
110 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
111 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
112 |
113 | // Act
114 | dynamic value = instance.ReadOnlyProperty;
115 | value = instance.ReadOnlyProperty;
116 |
117 | // Assert
118 | Assert.IsTrue(cache.NumStoreCalls == 1);
119 | Assert.IsTrue(cache.NumRetrieveCalls == 1);
120 | }
121 |
122 | [Test]
123 | public void ClassLevelCache_ShouldCacheReadWriteProperty()
124 | {
125 | // Arrange
126 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
127 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
128 |
129 | // Act
130 | dynamic value = instance.ReadWriteProperty;
131 | value = instance.ReadWriteProperty;
132 |
133 | // Assert
134 | Assert.IsTrue(cache.NumStoreCalls == 1);
135 | Assert.IsTrue(cache.NumRetrieveCalls == 1);
136 | }
137 |
138 | [Test]
139 | public void ClassLevelCache_ShouldCacheStaticReadOnlyProperties()
140 | {
141 | // Arrange
142 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
143 |
144 | Type testClassType = Assembly.GetType(typeof(TestClassStaticProperties).FullName);
145 | Type cacheType = Assembly.GetType(typeof(ICacheWithRemove).FullName);
146 |
147 | PropertyInfo cacheProperty = testClassType.GetProperty("Cache", cacheType);
148 | cacheProperty.SetValue(null, cache, null);
149 |
150 | PropertyInfo property = testClassType.GetProperty("ReadOnlyProperty");
151 |
152 | // Act
153 | object value = property.GetValue(null, null);
154 | value = property.GetValue(null, null);
155 |
156 | // Assert
157 | Assert.IsTrue(cache.NumStoreCalls == 1);
158 | Assert.IsTrue(cache.NumRetrieveCalls == 1);
159 | }
160 |
161 | [Test]
162 | public void ClassLevelCache_ShouldCacheStaticReadWriteProperties()
163 | {
164 | // Arrange
165 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
166 |
167 | Type testClassType = Assembly.GetType(typeof(TestClassStaticProperties).FullName);
168 | Type cacheType = Assembly.GetType(typeof(ICacheWithRemove).FullName);
169 |
170 | PropertyInfo cacheProperty = testClassType.GetProperty("Cache", cacheType);
171 | cacheProperty.SetValue(null, cache, null);
172 |
173 | PropertyInfo property = testClassType.GetProperty("ReadWriteProperty");
174 |
175 | // Act
176 | object value = property.GetValue(null, null);
177 | value = property.GetValue(null, null);
178 |
179 | // Assert
180 | Assert.IsTrue(cache.NumStoreCalls == 1);
181 | Assert.IsTrue(cache.NumRetrieveCalls == 1);
182 | }
183 |
184 | [Test]
185 | public void ClassLevelCache_ShouldInvalidateCacheWhenSettingReadWriteProperties()
186 | {
187 | // Arrange
188 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
189 |
190 | Type testClassType = Assembly.GetType(typeof(TestClassStaticProperties).FullName);
191 | Type cacheType = Assembly.GetType(typeof(ICacheWithRemove).FullName);
192 |
193 | PropertyInfo cacheProperty = testClassType.GetProperty("Cache", cacheType);
194 | cacheProperty.SetValue(null, cache, null);
195 |
196 | PropertyInfo property = testClassType.GetProperty("ReadWriteProperty");
197 |
198 | // Act
199 | property.GetValue(null, null);
200 | property.SetValue(null, 5, null);
201 |
202 | // Assert
203 | Assert.That(cache.Contains("MethodCache.Tests.TestAssembly.TestClassStaticProperties.ReadWriteProperty"), Is.False);
204 | }
205 |
206 | [Test]
207 | public void ClassLevelCache_ShouldNotCacheAutoProperties()
208 | {
209 | // Arrange
210 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
211 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
212 |
213 | // Act
214 | dynamic value = instance.AutoProperty;
215 | value = instance.AutoProperty;
216 |
217 | // Assert
218 | Assert.IsTrue(cache.NumStoreCalls == 0);
219 | Assert.IsTrue(cache.NumRetrieveCalls == 0);
220 | }
221 |
222 | [Test]
223 | public void ClassLevelCache_ShouldNotCacheReadOnlyAutoProperties()
224 | {
225 | // Arrange
226 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
227 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
228 |
229 | // Act
230 | dynamic value = instance.ReadOnlyAutoProperty;
231 | value = instance.ReadOnlyAutoProperty;
232 |
233 | // Assert
234 | Assert.IsTrue(cache.NumStoreCalls == 0);
235 | Assert.IsTrue(cache.NumRetrieveCalls == 0);
236 | }
237 |
238 | [Test]
239 | public void ClassLevelCache_ShouldNotWeaveWriteOnlyProperties()
240 | {
241 | // Arrange
242 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
243 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
244 |
245 | // Act
246 | instance.SetOnlyProperty = "some text";
247 |
248 | // Assert
249 | Assert.That(cache.NumRemoveCalls, Is.EqualTo(0));
250 | }
251 |
252 | [Test]
253 | public void ClassLevelCache_ShouldRemoveCacheAttributesFromAutoproperties()
254 | {
255 | // Arrange
256 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
257 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
258 | Type type = instance.GetType();
259 | PropertyInfo property = type.GetProperty("AutoProperty");
260 |
261 | // Act
262 | object[] customAttributes = property.GetCustomAttributes(true);
263 |
264 | // Assert
265 | Assert.IsEmpty(customAttributes);
266 | }
267 |
268 | [Test]
269 | public void ClassLevelCache_ShouldRemoveClassCacheAttribute()
270 | {
271 | // Arrange
272 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
273 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
274 | Type type = instance.GetType();
275 |
276 | // Act
277 | object[] customAttributes = type.GetCustomAttributes(true);
278 |
279 | // Assert
280 | Assert.IsEmpty(customAttributes);
281 | }
282 |
283 | [Test]
284 | public void ClassLevelCache_ShouldRemoveNoCacheAttribute()
285 | {
286 | // Arrange
287 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
288 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
289 | Type type = instance.GetType();
290 | PropertyInfo property = type.GetProperty("ReadOnlyNoCache");
291 |
292 | // Act
293 | object[] customAttributes = property.GetCustomAttributes(true);
294 |
295 | // Assert
296 | Assert.IsEmpty(customAttributes);
297 | }
298 |
299 | [Test]
300 | public void ClassLevelCache_ShouldRespectNoCacheAttribute()
301 | {
302 | // Arrange
303 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
304 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
305 |
306 | // Act
307 | dynamic value = instance.ReadOnlyNoCache;
308 | value = instance.ReadOnlyNoCache;
309 |
310 | // Assert
311 | Assert.IsTrue(cache.NumStoreCalls == 0);
312 | Assert.IsTrue(cache.NumRetrieveCalls == 0);
313 | }
314 |
315 | [Test]
316 | public void IndividualCache_SettingPropertyShouldInvalidateCache()
317 | {
318 | // Arrange
319 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
320 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
321 |
322 | // Act
323 | dynamic value = instance.ReadWriteProperty;
324 | instance.ReadWriteProperty = 10;
325 |
326 | // Assert
327 | Assert.That(cache.Contains("MethodCache.Tests.TestAssembly.TestClassIndividualProperties.ReadWriteProperty"),
328 | Is.False);
329 | }
330 |
331 | [Test]
332 | public void IndividualCache_ShouldCacheReadOnlyProperty()
333 | {
334 | // Arrange
335 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
336 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
337 |
338 | // Act
339 | dynamic value = instance.ReadOnlyProperty;
340 | value = instance.ReadOnlyProperty;
341 |
342 | // Assert
343 | Assert.IsTrue(cache.NumStoreCalls == 1);
344 | Assert.IsTrue(cache.NumRetrieveCalls == 1);
345 | }
346 |
347 | [Test]
348 | public void IndividualCache_ShouldCacheReadWriteProperty()
349 | {
350 | // Arrange
351 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
352 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
353 |
354 | // Act
355 | dynamic value = instance.ReadWriteProperty;
356 | value = instance.ReadWriteProperty;
357 |
358 | // Assert
359 | Assert.IsTrue(cache.NumStoreCalls == 1);
360 | Assert.IsTrue(cache.NumRetrieveCalls == 1);
361 | }
362 |
363 | [Test]
364 | public void IndividualCache_ShouldNeverWeaveWriteOnlyProperties()
365 | {
366 | // Arrange
367 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
368 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
369 |
370 | // Act
371 | instance.SetOnlyProperty = "some text";
372 |
373 | // Assert
374 | Assert.That(cache.NumRemoveCalls, Is.EqualTo(0));
375 | }
376 |
377 | [Test]
378 | public void IndividualCache_ShouldNotCacheAutoproperties()
379 | {
380 | // Arrange
381 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
382 | dynamic instance = WeaverHelper.CreateInstance(Assembly, cache);
383 |
384 | // Act
385 | dynamic value = instance.AutoProperty;
386 | value = instance.AutoProperty;
387 |
388 | // Assert
389 | Assert.IsTrue(cache.NumStoreCalls == 0);
390 | Assert.IsTrue(cache.NumRetrieveCalls == 0);
391 | }
392 | }
393 | }
--------------------------------------------------------------------------------
/MethodCache.Fody/CecilHelper.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Fody
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using Mono.Cecil;
7 | using Mono.Cecil.Cil;
8 | using Mono.Cecil.Rocks;
9 |
10 | public static class CecilHelper
11 | {
12 | public static int AddVariable(this MethodDefinition method, TypeReference typeReference)
13 | {
14 | method.Body.Variables.Add(method.Module.ImportVariable(typeReference));
15 |
16 | return method.Body.Variables.Count - 1;
17 | }
18 |
19 | public static Instruction Append(this Instruction instruction, Instruction instructionAfter, ILProcessor processor)
20 | {
21 | processor.InsertAfter(instruction, instructionAfter);
22 |
23 | return instructionAfter;
24 | }
25 |
26 | public static Instruction AppendBoxIfNecessary(this Instruction instruction, ILProcessor processor,
27 | TypeReference typeReference)
28 | {
29 | if (typeReference.IsValueType || typeReference.IsGenericParameter)
30 | {
31 | return instruction.Append(processor.Create(OpCodes.Box, typeReference), processor);
32 | }
33 |
34 | return instruction;
35 | }
36 |
37 | public static Instruction AppendDup(this Instruction instruction, ILProcessor processor)
38 | {
39 | return instruction.Append(processor.Create(OpCodes.Dup), processor);
40 | }
41 |
42 | public static Instruction AppendLd(this Instruction instruction, ILProcessor processor,
43 | CustomAttributeArgument argument, References references)
44 | {
45 | // Tested with the following types (see ECMA-334, 24.1.3 attribute parameter types)
46 | // bool, byte, char, double, float, int, long, sbyte, short, string, uint, ulong, ushort.
47 | // object
48 | // System.Type
49 | // An enum type, provided it has public accessibility and the types in which it is nested (if any) also have public accessibility (§17.2)
50 | // Single-dimensional arrays of the above types
51 | TypeReference argumentType = argument.Type;
52 |
53 | switch (argumentType.MetadataType)
54 | {
55 | case MetadataType.ValueType:
56 | if (argumentType.Resolve().IsEnum == false)
57 | {
58 | throw new ArgumentException("Type Enum expected.", "argument");
59 | }
60 |
61 | // Get underlying Enum type
62 | argumentType = argument.Type.Resolve().Fields.First(field => field.Name == "value__").FieldType;
63 | break;
64 |
65 | case MetadataType.Object:
66 | return instruction.AppendLd(processor, (CustomAttributeArgument)argument.Value, references);
67 |
68 | case MetadataType.Array:
69 | CustomAttributeArgument[] values = (CustomAttributeArgument[])argument.Value;
70 |
71 | instruction = instruction
72 | .AppendLdcI4(processor, values.Length)
73 | .Append(processor.Create(OpCodes.Newarr, argument.Type.GetElementType()), processor);
74 |
75 | TypeReference arrayType = argument.Type.GetElementType();
76 |
77 | for (int i = 0; i < values.Length; i++)
78 | {
79 | instruction = instruction
80 | .AppendDup(processor)
81 | .AppendLdcI4(processor, i)
82 | .AppendLd(processor, values[i], references);
83 |
84 | if (arrayType == references.ModuleDefinition.TypeSystem.Object)
85 | {
86 | instruction = instruction.AppendBoxIfNecessary(processor, ((CustomAttributeArgument)values[i].Value).Type);
87 | }
88 | else if (argumentType.Resolve().IsEnum)
89 | {
90 | // Get underlying Enum type
91 | arrayType = argument.Type.Resolve().Fields.First(field => field.Name == "value__").FieldType;
92 | }
93 |
94 | if (arrayType.IsValueType)
95 | {
96 | switch (arrayType.MetadataType)
97 | {
98 | case MetadataType.Boolean:
99 | case MetadataType.SByte:
100 | case MetadataType.Byte:
101 | instruction = instruction.Append(processor.Create(OpCodes.Stelem_I1), processor);
102 | break;
103 |
104 | case MetadataType.Char:
105 | case MetadataType.Int16:
106 | case MetadataType.UInt16:
107 | instruction = instruction.Append(processor.Create(OpCodes.Stelem_I2), processor);
108 | break;
109 |
110 | case MetadataType.Int32:
111 | case MetadataType.UInt32:
112 | instruction = instruction.Append(processor.Create(OpCodes.Stelem_I4), processor);
113 | break;
114 |
115 | case MetadataType.Int64:
116 | case MetadataType.UInt64:
117 | instruction = instruction.Append(processor.Create(OpCodes.Stelem_I8), processor);
118 | break;
119 |
120 | case MetadataType.Single:
121 | instruction = instruction.Append(processor.Create(OpCodes.Stelem_R4), processor);
122 | break;
123 |
124 | case MetadataType.Double:
125 | instruction = instruction.Append(processor.Create(OpCodes.Stelem_R8), processor);
126 | break;
127 |
128 | default:
129 | throw new ArgumentException("Unrecognized array value type.", "argument");
130 | }
131 | }
132 | else
133 | {
134 | instruction = instruction.Append(processor.Create(OpCodes.Stelem_Ref), processor);
135 | }
136 | }
137 |
138 | return instruction;
139 | }
140 |
141 | switch (argumentType.MetadataType)
142 | {
143 | case MetadataType.Boolean:
144 | case MetadataType.SByte:
145 | return instruction.Append(processor.Create(OpCodes.Ldc_I4_S, Convert.ToSByte(argument.Value)), processor);
146 |
147 | case MetadataType.Int16:
148 | return instruction.Append(processor.Create(OpCodes.Ldc_I4, Convert.ToInt16(argument.Value)), processor);
149 |
150 | case MetadataType.Int32:
151 | return instruction.Append(processor.Create(OpCodes.Ldc_I4, Convert.ToInt32(argument.Value)), processor);
152 |
153 | case MetadataType.Int64:
154 | return instruction.Append(processor.Create(OpCodes.Ldc_I8, Convert.ToInt64(argument.Value)), processor);
155 |
156 | case MetadataType.Byte:
157 | return instruction.Append(processor.Create(OpCodes.Ldc_I4, Convert.ToInt32(argument.Value)), processor);
158 |
159 | case MetadataType.UInt16:
160 | unchecked
161 | {
162 | return instruction.Append(processor.Create(OpCodes.Ldc_I4, (Int16)(UInt16)argument.Value), processor);
163 | }
164 |
165 | case MetadataType.Char:
166 | return instruction.Append(processor.Create(OpCodes.Ldc_I4, Convert.ToChar(argument.Value)), processor);
167 |
168 | case MetadataType.UInt32:
169 | unchecked
170 | {
171 | return instruction.Append(processor.Create(OpCodes.Ldc_I4, (Int32)(UInt32)argument.Value), processor);
172 | }
173 |
174 | case MetadataType.UInt64:
175 | unchecked
176 | {
177 | return instruction.Append(processor.Create(OpCodes.Ldc_I8, (Int64)(UInt64)argument.Value), processor);
178 | }
179 |
180 | case MetadataType.Single:
181 | return instruction.Append(processor.Create(OpCodes.Ldc_R4, Convert.ToSingle(argument.Value)), processor);
182 |
183 | case MetadataType.Double:
184 | return instruction.Append(processor.Create(OpCodes.Ldc_R8, Convert.ToDouble(argument.Value)), processor);
185 |
186 | case MetadataType.String:
187 | return instruction.Append(processor.Create(OpCodes.Ldstr, (string)argument.Value), processor);
188 |
189 | case MetadataType.Class:
190 | if (argumentType.Resolve().IsDefinition == false)
191 | {
192 | throw new ArgumentException("Type Type expected.", "argument");
193 | }
194 |
195 | return
196 | instruction
197 | .Append(processor.Create(OpCodes.Ldtoken, (TypeReference)argument.Value), processor)
198 | .Append(processor.Create(OpCodes.Call, references.ModuleDefinition.Import(
199 | references.SystemTypeGetTypeFromHandleMethod)), processor);
200 |
201 | default:
202 | throw new ArgumentException("Unrecognized attribute parameter type.", "argument");
203 | }
204 | }
205 |
206 | public static Instruction AppendLdarg(this Instruction instruction, ILProcessor processor, int index)
207 | {
208 | return instruction.Append(processor.Create(OpCodes.Ldarg, index), processor);
209 | }
210 |
211 | public static Instruction AppendLdcI4(this Instruction instruction, ILProcessor processor, int value)
212 | {
213 | return instruction.Append(processor.Create(OpCodes.Ldc_I4, value), processor);
214 | }
215 |
216 | public static Instruction AppendLdloc(this Instruction instruction, ILProcessor processor, int index)
217 | {
218 | return instruction.Append(processor.Create(OpCodes.Ldloc, index), processor);
219 | }
220 |
221 | public static Instruction AppendLdstr(this Instruction instruction, ILProcessor processor, string str)
222 | {
223 | return instruction.Append(processor.Create(OpCodes.Ldstr, str), processor);
224 | }
225 |
226 | public static Instruction AppendStloc(this Instruction instruction, ILProcessor processor, int index)
227 | {
228 | return instruction.Append(processor.Create(OpCodes.Stloc, index), processor);
229 | }
230 |
231 | public static bool ContainsAttribute(this ICustomAttributeProvider methodDefinition, TypeDefinition attributeType)
232 | {
233 | return methodDefinition.CustomAttributes.Any(x => x.Constructor.DeclaringType.FullName == attributeType.FullName);
234 | }
235 |
236 | public static bool ContainsAttribute(this ICustomAttributeProvider methodDefinition, string attributeTypeName)
237 | {
238 | return methodDefinition.CustomAttributes.Any(x => x.Constructor.DeclaringType.Name == attributeTypeName);
239 | }
240 |
241 | public static MethodDefinition GetInheritedPropertyGet(this TypeDefinition baseType, string propertyName)
242 | {
243 | MethodDefinition methodDefinition = baseType.GetPropertyGet(propertyName);
244 |
245 | if (methodDefinition == null && baseType.BaseType != null)
246 | {
247 | return baseType.BaseType.Resolve().GetInheritedPropertyGet(propertyName);
248 | }
249 |
250 | if (methodDefinition == null && baseType.BaseType == null)
251 | {
252 | return null;
253 | }
254 |
255 | if (methodDefinition.IsPrivate)
256 | {
257 | return null;
258 | }
259 |
260 | return methodDefinition;
261 | }
262 |
263 | public static MethodDefinition GetMethod(this TypeDefinition typeDefinition, string methodName,
264 | TypeReference returnType, TypeReference[] parameterTypes)
265 | {
266 | return typeDefinition.Methods.SingleOrDefault(x => x.Matches(methodName, returnType, parameterTypes));
267 | }
268 |
269 | public static MethodDefinition GetPropertyGet(this TypeDefinition typeDefinition, string propertyName)
270 | {
271 | return typeDefinition.Properties.Where(x => x.Name == propertyName).Select(x => x.GetMethod).SingleOrDefault();
272 | }
273 |
274 | public static MethodReference ImportMethod(this ModuleDefinition module, MethodDefinition methodDefinition)
275 | {
276 | return module.Import(methodDefinition);
277 | }
278 |
279 | public static VariableDefinition ImportVariable(this ModuleDefinition module, TypeReference typeReference)
280 | {
281 | return new VariableDefinition(module.Import(typeReference));
282 | }
283 |
284 | public static bool IsEqualTo(this IEnumerable collection, IEnumerable collectionToCompare)
285 | {
286 | int index = 0;
287 |
288 | IEnumerable list = collection as IList ?? collection.ToList();
289 | IEnumerable listToCompare = collectionToCompare as IList ?? collectionToCompare.ToList();
290 |
291 | if (list.Count() != listToCompare.Count())
292 | {
293 | return false;
294 | }
295 |
296 | foreach (T item in list)
297 | {
298 | if (!EqualityComparer.Default.Equals(item, listToCompare.ElementAt(index)))
299 | {
300 | return false;
301 | }
302 |
303 | index++;
304 | }
305 |
306 | return true;
307 | }
308 |
309 | public static MethodReference MakeGeneric(this MethodReference method, params TypeReference[] arguments)
310 | {
311 | if (method.GenericParameters.Count != arguments.Length)
312 | {
313 | throw new ArgumentException("Invalid number of generic type arguments supplied");
314 | }
315 |
316 | if (arguments.Length == 0)
317 | {
318 | return method;
319 | }
320 |
321 | GenericInstanceMethod genericTypeReference = new GenericInstanceMethod(method);
322 |
323 | foreach (TypeReference argument in arguments)
324 | {
325 | genericTypeReference.GenericArguments.Add(argument);
326 | }
327 |
328 | return genericTypeReference;
329 | }
330 |
331 | public static MethodReference MakeHostInstanceGeneric(this MethodReference self, params TypeReference[] args)
332 | {
333 | MethodReference reference = new MethodReference(self.Name, self.ReturnType, self.DeclaringType.MakeGenericInstanceType(args))
334 | {
335 | HasThis = self.HasThis,
336 | ExplicitThis = self.ExplicitThis,
337 | CallingConvention = self.CallingConvention
338 | };
339 |
340 | foreach (ParameterDefinition parameter in self.Parameters)
341 | {
342 | reference.Parameters.Add(new ParameterDefinition(parameter.ParameterType));
343 | }
344 |
345 | foreach (GenericParameter genericParam in self.GenericParameters)
346 | {
347 | reference.GenericParameters.Add(new GenericParameter(genericParam.Name, reference));
348 | }
349 |
350 | return reference;
351 | }
352 |
353 | public static bool Matches(this MethodDefinition methodDefinition, string methodName, TypeReference returnType,
354 | TypeReference[] parameters)
355 | {
356 | return methodDefinition.Name == methodName && methodDefinition.ReturnType.FullName == returnType.FullName &&
357 | methodDefinition.Parameters.ToList()
358 | .Select(parameter => parameter.ParameterType.FullName)
359 | .IsEqualTo(parameters.Select(parameter => parameter.FullName));
360 | }
361 |
362 | public static Instruction Prepend(this Instruction instruction, Instruction instructionBefore, ILProcessor processor)
363 | {
364 | processor.InsertBefore(instruction, instructionBefore);
365 |
366 | return instructionBefore;
367 | }
368 |
369 | public static void RemoveAttribute(this ICustomAttributeProvider methodDefinition, string attributeTypeName)
370 | {
371 | methodDefinition.CustomAttributes.Where(x => x.Constructor.DeclaringType.Name == attributeTypeName)
372 | .ToList()
373 | .ForEach(x => methodDefinition.CustomAttributes.Remove(x));
374 | }
375 | }
376 | }
--------------------------------------------------------------------------------
/MethodCache.sln.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | DO_NOT_SHOW
3 | DO_NOT_SHOW
4 | DO_NOT_SHOW
5 | DO_NOT_SHOW
6 | DO_NOT_SHOW
7 | DO_NOT_SHOW
8 | DO_NOT_SHOW
9 | DO_NOT_SHOW
10 | DO_NOT_SHOW
11 | DO_NOT_SHOW
12 | DO_NOT_SHOW
13 | DO_NOT_SHOW
14 | DO_NOT_SHOW
15 | DO_NOT_SHOW
16 | DO_NOT_SHOW
17 | DO_NOT_SHOW
18 | DO_NOT_SHOW
19 | DO_NOT_SHOW
20 | DO_NOT_SHOW
21 | DO_NOT_SHOW
22 | DO_NOT_SHOW
23 | DO_NOT_SHOW
24 | DO_NOT_SHOW
25 | DO_NOT_SHOW
26 | DO_NOT_SHOW
27 | DO_NOT_SHOW
28 | DO_NOT_SHOW
29 | DO_NOT_SHOW
30 | DO_NOT_SHOW
31 | DO_NOT_SHOW
32 |
33 |
34 | DO_NOT_SHOW
35 | DO_NOT_SHOW
36 | <?xml version="1.0" encoding="utf-16"?><Profile name="Default"><AspOptimizeRegisterDirectives>True</AspOptimizeRegisterDirectives><HtmlReformatCode>True</HtmlReformatCode><CSArrangeThisQualifier>True</CSArrangeThisQualifier><CSUseAutoProperty>True</CSUseAutoProperty><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSUseVar><BehavourStyle>CAN_CHANGE_BOTH</BehavourStyle><LocalVariableStyle>ALWAYS_EXPLICIT</LocalVariableStyle><ForeachVariableStyle>ALWAYS_EXPLICIT</ForeachVariableStyle></CSUseVar><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSReformatCode>True</CSReformatCode><CSharpFormatDocComments>True</CSharpFormatDocComments><CSReorderTypeMembers>True</CSReorderTypeMembers><JsInsertSemicolon>True</JsInsertSemicolon><JsReformatCode>True</JsReformatCode><CssAlphabetizeProperties>True</CssAlphabetizeProperties><CssReformatCode>True</CssReformatCode><XMLReformatCode>True</XMLReformatCode><StyleCop.Documentation><SA1600ElementsMustBeDocumented>False</SA1600ElementsMustBeDocumented><SA1604ElementDocumentationMustHaveSummary>True</SA1604ElementDocumentationMustHaveSummary><SA1609PropertyDocumentationMustHaveValueDocumented>True</SA1609PropertyDocumentationMustHaveValueDocumented><SA1611ElementParametersMustBeDocumented>True</SA1611ElementParametersMustBeDocumented><SA1615ElementReturnValueMustBeDocumented>True</SA1615ElementReturnValueMustBeDocumented><SA1617VoidReturnValueMustNotBeDocumented>True</SA1617VoidReturnValueMustNotBeDocumented><SA1618GenericTypeParametersMustBeDocumented>True</SA1618GenericTypeParametersMustBeDocumented><SA1626SingleLineCommentsMustNotUseDocumentationStyleSlashes>True</SA1626SingleLineCommentsMustNotUseDocumentationStyleSlashes><SA1628DocumentationTextMustBeginWithACapitalLetter>True</SA1628DocumentationTextMustBeginWithACapitalLetter><SA1629DocumentationTextMustEndWithAPeriod>True</SA1629DocumentationTextMustEndWithAPeriod><SA1633SA1641UpdateFileHeader>Ignore</SA1633SA1641UpdateFileHeader><SA1639FileHeaderMustHaveSummary>True</SA1639FileHeaderMustHaveSummary><SA1642ConstructorSummaryDocumentationMustBeginWithStandardText>True</SA1642ConstructorSummaryDocumentationMustBeginWithStandardText><SA1643DestructorSummaryDocumentationMustBeginWithStandardText>True</SA1643DestructorSummaryDocumentationMustBeginWithStandardText><SA1644DocumentationHeadersMustNotContainBlankLines>True</SA1644DocumentationHeadersMustNotContainBlankLines></StyleCop.Documentation><StyleCop.Spacing><SA1001CommasMustBeSpacedCorrectly>False</SA1001CommasMustBeSpacedCorrectly><SA1005SingleLineCommentsMustBeginWithSingleSpace>True</SA1005SingleLineCommentsMustBeginWithSingleSpace><SA1006PreprocessorKeywordsMustNotBePrecededBySpace>True</SA1006PreprocessorKeywordsMustNotBePrecededBySpace><SA1021NegativeSignsMustBeSpacedCorrectly>True</SA1021NegativeSignsMustBeSpacedCorrectly><SA1022PositiveSignsMustBeSpacedCorrectly>True</SA1022PositiveSignsMustBeSpacedCorrectly><SA1025CodeMustNotContainMultipleWhitespaceInARow>True</SA1025CodeMustNotContainMultipleWhitespaceInARow></StyleCop.Spacing></Profile>
37 | Default
38 | USE_TABS_ONLY
39 | False
40 | SEPARATE
41 | False
42 | USE_FOR_FIELD
43 | False
44 | True
45 | False
46 | False
47 | True
48 | False
49 | OneStep
50 | OneStep
51 | 1
52 | OneStep
53 | OneStep
54 | <?xml version="1.0" encoding="utf-8" ?>
55 | <Patterns xmlns="urn:shemas-jetbrains-com:member-reordering-patterns">
56 |
57 | <!--Do not reorder COM interfaces-->
58 | <Pattern>
59 | <Match>
60 | <And Weight="100">
61 | <Kind Is="interface"/>
62 | <HasAttribute CLRName="System.Runtime.InteropServices.InterfaceTypeAttribute"/>
63 | </And>
64 | </Match>
65 | </Pattern>
66 |
67 | <!--Default pattern-->
68 | <Pattern RemoveAllRegions="true">
69 | <Entry>
70 | <Sort>
71 | <Kind Order="constant field constructor destructor delegate event enum interface property indexer operator method struct class" />
72 | <Access Order="public protected internal private" />
73 | <Static />
74 | <Readonly />
75 | <Name />
76 | </Sort>
77 | </Entry>
78 |
79 | <!--all other members-->
80 | <Entry />
81 | </Pattern>
82 | </Patterns>
83 | CustomLayout
84 | True
85 | False
86 | True
87 | IL
88 | PE
89 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
90 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
91 | ALWAYS_ADD
92 | ALWAYS_ADD
93 | ALWAYS_ADD
94 | ALWAYS_ADD
95 | ALWAYS_ADD
96 | ALWAYS_ADD
97 | 1
98 | 1
99 | 0
100 | 1
101 | 1
102 | 1
103 | 1
104 | False
105 | LINE_BREAK
106 | True
107 |
108 | WRAP_IF_LONG
109 | WRAP_IF_LONG
110 | WRAP_IF_LONG
111 | False
112 | False
113 | True
114 |
--------------------------------------------------------------------------------
/MethodCache.Tests/ParameterizedCacheAttributeWithParameterTests.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Tests
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using MethodCache.Tests.TestAssembly;
6 | using MethodCache.Tests.TestAssembly.Cache;
7 | using MethodCache.Tests.TestAssembly.Cache.ComplexCacheAttribute;
8 | using NUnit.Framework;
9 |
10 | public class ParameterizedCacheAttributeWithParameterTests : ModuleWeaverTestsBase
11 | {
12 | [Test]
13 | public void TestClassWithParameterizedCacheAttributeClassLevelCacheAttributeAddsParameters()
14 | {
15 | // Arrange
16 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
17 | dynamic testClass1 = WeaverHelper.CreateInstance(Assembly, cache);
18 |
19 | // Act
20 | testClass1.CacheParameterMethod("1", 2);
21 | testClass1.CacheParameterMethod("1", 2);
22 |
23 | // Assert
24 | Assert.IsTrue(cache.NumStoreParameterCalls == 1);
25 | Assert.IsTrue(cache.NumRetrieveCalls == 1);
26 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall.Count == 2);
27 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall["Parameter1"] == "CacheParameter");
28 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall["Parameter2"] == 1);
29 | }
30 |
31 | [Test]
32 | public void TestClassWithParameterizedCacheAttributeComplexCacheAttributePassesParameterArrays()
33 | {
34 | // Arrange
35 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
36 | dynamic testClass1 = WeaverHelper.CreateInstance(Assembly, cache);
37 |
38 | // Act
39 | testClass1.ComplexCacheParameterMethodArrays();
40 |
41 | // Assert
42 | Assert.IsTrue(cache.NumStoreParameterCalls == 1);
43 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall.Count == 12);
44 |
45 | bool[] boolArray = (bool[])cache.ParametersPassedToLastStoreCall["ParameterBoolArray"];
46 | Assert.IsTrue(boolArray[0] == false);
47 | Assert.IsTrue(boolArray[1] == true);
48 |
49 | byte[] byteArray = (byte[])cache.ParametersPassedToLastStoreCall["ParameterByteArray"];
50 | Assert.IsTrue(byteArray[0] == Byte.MinValue);
51 | Assert.IsTrue(byteArray[1] == Byte.MaxValue);
52 |
53 | char[] charArray = (char[])cache.ParametersPassedToLastStoreCall["ParameterCharacterArray"];
54 | Assert.IsTrue(charArray[0] == Char.MinValue);
55 | Assert.IsTrue(charArray[1] == Char.MaxValue);
56 |
57 | double[] doubleArray = (double[])cache.ParametersPassedToLastStoreCall["ParameterDoubleArray"];
58 | Assert.IsTrue(doubleArray[0] == Double.MinValue);
59 | Assert.IsTrue(doubleArray[1] == Double.MaxValue);
60 |
61 | float[] floatArray = (float[])cache.ParametersPassedToLastStoreCall["ParameterFloatArray"];
62 | Assert.IsTrue(floatArray[0] == Single.MinValue);
63 | Assert.IsTrue(floatArray[1] == Single.MaxValue);
64 |
65 | int[] intArray = (int[])cache.ParametersPassedToLastStoreCall["ParameterIntArray"];
66 | Assert.IsTrue(intArray[0] == Int32.MinValue);
67 | Assert.IsTrue(intArray[1] == Int32.MaxValue);
68 |
69 | long[] longArray = (long[])cache.ParametersPassedToLastStoreCall["ParameterLongArray"];
70 | Assert.IsTrue(longArray[0] == Int64.MinValue);
71 | Assert.IsTrue(longArray[1] == Int64.MaxValue);
72 |
73 | sbyte[] sByteArray = (sbyte[])cache.ParametersPassedToLastStoreCall["ParameterSByteArray"];
74 | Assert.IsTrue(sByteArray[0] == SByte.MinValue);
75 | Assert.IsTrue(sByteArray[1] == SByte.MaxValue);
76 |
77 | short[] shortArray = (short[])cache.ParametersPassedToLastStoreCall["ParameterShortArray"];
78 | Assert.IsTrue(shortArray[0] == Int16.MinValue);
79 | Assert.IsTrue(shortArray[1] == Int16.MaxValue);
80 |
81 | uint[] uIntArray = (uint[])cache.ParametersPassedToLastStoreCall["ParameterUIntArray"];
82 | Assert.IsTrue(uIntArray[0] == UInt32.MinValue);
83 | Assert.IsTrue(uIntArray[1] == UInt32.MaxValue);
84 |
85 | ulong[] uLongArray = (ulong[])cache.ParametersPassedToLastStoreCall["ParameterULongArray"];
86 | Assert.IsTrue(uLongArray[0] == UInt64.MinValue);
87 | Assert.IsTrue(uLongArray[1] == UInt64.MaxValue);
88 |
89 | ushort[] uShortArray = (ushort[])cache.ParametersPassedToLastStoreCall["ParameterUShortArray"];
90 | Assert.IsTrue(uShortArray[0] == UInt16.MinValue);
91 | Assert.IsTrue(uShortArray[1] == UInt16.MaxValue);
92 | }
93 |
94 | [Test]
95 | public void TestClassWithParameterizedCacheAttributeComplexCacheAttributePassesParametersEnum()
96 | {
97 | // Arrange
98 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
99 | dynamic testClass1 = WeaverHelper.CreateInstance(Assembly, cache);
100 |
101 | // Act
102 | testClass1.ComplexCacheParameterMethodEnum();
103 |
104 | // Assert
105 | Assert.IsTrue(cache.NumStoreParameterCalls == 1);
106 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall.Count == 1);
107 |
108 | dynamic type = WeaverHelper.CreateType(Assembly);
109 |
110 | Assert.IsInstanceOfType(type, cache.ParametersPassedToLastStoreCall["ParameterEnum"]);
111 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall["ParameterEnum"] ==
112 | (Enum.GetValues(type)[0] | Enum.GetValues(type)[2]));
113 | }
114 |
115 | [Test]
116 | public void TestClassWithParameterizedCacheAttributeComplexCacheAttributePassesParametersEnumArray()
117 | {
118 | // Arrange
119 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
120 | dynamic testClass1 = WeaverHelper.CreateInstance(Assembly, cache);
121 |
122 | // Act
123 | testClass1.ComplexCacheParameterMethodEnumArray();
124 |
125 | // Assert
126 | Assert.IsTrue(cache.NumStoreParameterCalls == 1);
127 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall.Count == 1);
128 |
129 | dynamic type = WeaverHelper.CreateType(Assembly);
130 | dynamic typeArray = WeaverHelper.CreateType(Assembly);
131 |
132 | Assert.IsInstanceOfType(typeArray, cache.ParametersPassedToLastStoreCall["ParameterEnumArray"]);
133 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall["ParameterEnumArray"][0] ==
134 | (Enum.GetValues(type)[0] | Enum.GetValues(type)[2]));
135 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall["ParameterEnumArray"][1] == Enum.GetValues(type)[1]);
136 | }
137 |
138 | [Test]
139 | public void TestClassWithParameterizedCacheAttributeComplexCacheAttributePassesParametersMax()
140 | {
141 | // Arrange
142 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
143 | dynamic testClass1 = WeaverHelper.CreateInstance(Assembly, cache);
144 |
145 | // Act
146 | testClass1.ComplexCacheParameterMethodMax();
147 |
148 | // Assert
149 | Assert.IsTrue(cache.NumStoreParameterCalls == 1);
150 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall.Count == 12);
151 | Assert.IsTrue((bool)cache.ParametersPassedToLastStoreCall["ParameterBool"] == false);
152 | Assert.IsTrue((byte)cache.ParametersPassedToLastStoreCall["ParameterByte"] == Byte.MaxValue);
153 | Assert.IsTrue((char)cache.ParametersPassedToLastStoreCall["ParameterCharacter"] == Char.MaxValue);
154 | Assert.IsTrue((double)cache.ParametersPassedToLastStoreCall["ParameterDouble"] == Double.MaxValue);
155 | Assert.IsTrue((float)cache.ParametersPassedToLastStoreCall["ParameterFloat"] == Single.MaxValue);
156 | Assert.IsTrue((int)cache.ParametersPassedToLastStoreCall["ParameterInt"] == Int32.MaxValue);
157 | Assert.IsTrue((long)cache.ParametersPassedToLastStoreCall["ParameterLong"] == Int64.MaxValue);
158 | Assert.IsTrue((sbyte)cache.ParametersPassedToLastStoreCall["ParameterSByte"] == SByte.MaxValue);
159 | Assert.IsTrue((short)cache.ParametersPassedToLastStoreCall["ParameterShort"] == Int16.MaxValue);
160 | Assert.IsTrue((uint)cache.ParametersPassedToLastStoreCall["ParameterUInt"] == UInt32.MaxValue);
161 | Assert.IsTrue((ulong)cache.ParametersPassedToLastStoreCall["ParameterULong"] == UInt64.MaxValue);
162 | Assert.IsTrue((ushort)cache.ParametersPassedToLastStoreCall["ParameterUShort"] == UInt16.MaxValue);
163 | }
164 |
165 | [Test]
166 | public void TestClassWithParameterizedCacheAttributeComplexCacheAttributePassesParametersMin()
167 | {
168 | // Arrange
169 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
170 | dynamic testClass1 = WeaverHelper.CreateInstance(Assembly, cache);
171 |
172 | // Act
173 | testClass1.ComplexCacheParameterMethodMin();
174 |
175 | // Assert
176 | Assert.IsTrue(cache.NumStoreParameterCalls == 1);
177 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall.Count == 12);
178 | Assert.IsTrue((bool)cache.ParametersPassedToLastStoreCall["ParameterBool"] == false);
179 | Assert.IsTrue((byte)cache.ParametersPassedToLastStoreCall["ParameterByte"] == Byte.MinValue);
180 | Assert.IsTrue((char)cache.ParametersPassedToLastStoreCall["ParameterCharacter"] == Char.MinValue);
181 | Assert.IsTrue((double)cache.ParametersPassedToLastStoreCall["ParameterDouble"] == Double.MinValue);
182 | Assert.IsTrue((float)cache.ParametersPassedToLastStoreCall["ParameterFloat"] == Single.MinValue);
183 | Assert.IsTrue((int)cache.ParametersPassedToLastStoreCall["ParameterInt"] == Int32.MinValue);
184 | Assert.IsTrue((long)cache.ParametersPassedToLastStoreCall["ParameterLong"] == Int64.MinValue);
185 | Assert.IsTrue((sbyte)cache.ParametersPassedToLastStoreCall["ParameterSByte"] == SByte.MinValue);
186 | Assert.IsTrue((short)cache.ParametersPassedToLastStoreCall["ParameterShort"] == Int16.MinValue);
187 | Assert.IsTrue((uint)cache.ParametersPassedToLastStoreCall["ParameterUInt"] == UInt32.MinValue);
188 | Assert.IsTrue((ulong)cache.ParametersPassedToLastStoreCall["ParameterULong"] == UInt64.MinValue);
189 | Assert.IsTrue((ushort)cache.ParametersPassedToLastStoreCall["ParameterUShort"] == UInt16.MinValue);
190 | }
191 |
192 | [Test]
193 | public void TestClassWithParameterizedCacheAttributeComplexCacheAttributePassesParametersObjectArray()
194 | {
195 | // Arrange
196 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
197 | dynamic testClass1 = WeaverHelper.CreateInstance(Assembly, cache);
198 |
199 | // Act
200 | testClass1.ComplexCacheParameterMethodObjectArray();
201 |
202 | // Assert
203 | dynamic type = WeaverHelper.CreateType(Assembly);
204 |
205 | Assert.IsTrue(cache.NumStoreParameterCalls == 1);
206 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall.Count == 1);
207 | Assert.IsInstanceOfType(typeof(object[]), cache.ParametersPassedToLastStoreCall["ParameterObjectArray"]);
208 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall["ParameterObjectArray"].Length == 5);
209 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall["ParameterObjectArray"][0] == 1);
210 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall["ParameterObjectArray"][1] == "2");
211 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall["ParameterObjectArray"][2] == Enum.GetValues(type)[2]);
212 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall["ParameterObjectArray"][3] == typeof(List));
213 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall["ParameterObjectArray"][4][0] == 1);
214 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall["ParameterObjectArray"][4][1] == "2");
215 | }
216 |
217 | [Test]
218 | public void TestClassWithParameterizedCacheAttributeComplexCacheAttributePassesParametersObjectArrayString()
219 | {
220 | // Arrange
221 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
222 | dynamic testClass1 = WeaverHelper.CreateInstance(Assembly, cache);
223 |
224 | // Act
225 | testClass1.ComplexCacheParameterMethodObjectArrayString();
226 |
227 | // Assert
228 | Assert.IsTrue(cache.NumStoreParameterCalls == 1);
229 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall.Count == 1);
230 | Assert.IsInstanceOfType(typeof(string[]), cache.ParametersPassedToLastStoreCall["ParameterObject"]);
231 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall["ParameterObject"].Length == 2);
232 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall["ParameterObject"][0] == "1");
233 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall["ParameterObject"][1] == "2");
234 | }
235 |
236 | [Test]
237 | public void TestClassWithParameterizedCacheAttributeComplexCacheAttributePassesParametersObjectEnum()
238 | {
239 | // Arrange
240 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
241 | dynamic testClass1 = WeaverHelper.CreateInstance(Assembly, cache);
242 |
243 | // Act
244 | testClass1.ComplexCacheParameterMethodObjectEnum();
245 |
246 | // Assert
247 | Assert.IsTrue(cache.NumStoreParameterCalls == 1);
248 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall.Count == 1);
249 |
250 | dynamic type = WeaverHelper.CreateType(Assembly);
251 |
252 | Assert.IsInstanceOfType(type, cache.ParametersPassedToLastStoreCall["ParameterObject"]);
253 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall["ParameterObject"] ==
254 | (Enum.GetValues(type)[0] | Enum.GetValues(type)[2]));
255 | }
256 |
257 | [Test]
258 | public void TestClassWithParameterizedCacheAttributeComplexCacheAttributePassesParametersObjectType()
259 | {
260 | // Arrange
261 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
262 | dynamic testClass1 = WeaverHelper.CreateInstance(Assembly, cache);
263 |
264 | // Act
265 | testClass1.ComplexCacheParameterMethodObjectType();
266 |
267 | // Assert
268 | Assert.IsTrue(cache.NumStoreParameterCalls == 1);
269 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall.Count == 1);
270 | Assert.IsInstanceOfType(typeof(Type), cache.ParametersPassedToLastStoreCall["ParameterObject"]);
271 | Assert.IsTrue((Type)cache.ParametersPassedToLastStoreCall["ParameterObject"] == typeof(List));
272 | }
273 |
274 | [Test]
275 | public void TestClassWithParameterizedCacheAttributeComplexCacheAttributePassesParametersObjectValueType()
276 | {
277 | // Arrange
278 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
279 | dynamic testClass1 = WeaverHelper.CreateInstance(Assembly, cache);
280 |
281 | // Act
282 | testClass1.ComplexCacheParameterMethodObjectValueType();
283 |
284 | // Assert
285 | Assert.IsTrue(cache.NumStoreParameterCalls == 1);
286 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall.Count == 1);
287 | Assert.IsInstanceOfType(typeof(bool), cache.ParametersPassedToLastStoreCall["ParameterObject"]);
288 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall["ParameterObject"] == true);
289 | }
290 |
291 | [Test]
292 | public void TestClassWithParameterizedCacheAttributeComplexCacheAttributePassesParametersStringArray()
293 | {
294 | // Arrange
295 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
296 | dynamic testClass1 = WeaverHelper.CreateInstance(Assembly, cache);
297 |
298 | // Act
299 | testClass1.ComplexCacheParameterMethodStringArray();
300 |
301 | // Assert
302 | Assert.IsTrue(cache.NumStoreParameterCalls == 1);
303 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall.Count == 1);
304 | Assert.IsInstanceOfType(typeof(string[]), cache.ParametersPassedToLastStoreCall["ParameterStringArray"]);
305 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall["ParameterStringArray"].Length == 2);
306 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall["ParameterStringArray"][0] == "1");
307 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall["ParameterStringArray"][1] == "2");
308 | }
309 |
310 | [Test]
311 | public void TestClassWithParameterizedCacheAttributeComplexCacheAttributePassesParametersType()
312 | {
313 | // Arrange
314 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
315 | dynamic testClass1 = WeaverHelper.CreateInstance(Assembly, cache);
316 |
317 | // Act
318 | testClass1.ComplexCacheParameterMethodType();
319 |
320 | // Assert
321 | Assert.IsTrue(cache.NumStoreParameterCalls == 1);
322 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall.Count == 1);
323 | Assert.IsInstanceOfType(typeof(Type), cache.ParametersPassedToLastStoreCall["ParameterType"]);
324 | Assert.IsTrue((Type)cache.ParametersPassedToLastStoreCall["ParameterType"] == typeof(List));
325 | }
326 |
327 | [Test]
328 | public void TestClassWithParameterizedCacheAttributeComplexCacheAttributePassesParametersTypeArray()
329 | {
330 | // Arrange
331 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
332 | dynamic testClass1 = WeaverHelper.CreateInstance(Assembly, cache);
333 |
334 | // Act
335 | testClass1.ComplexCacheParameterMethodTypeArray();
336 |
337 | // Assert
338 | Assert.IsTrue(cache.NumStoreParameterCalls == 1);
339 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall.Count == 1);
340 | Assert.IsInstanceOfType(typeof(Type[]), cache.ParametersPassedToLastStoreCall["ParameterTypeArray"]);
341 | Assert.IsTrue((Type)cache.ParametersPassedToLastStoreCall["ParameterTypeArray"][0] == typeof(List));
342 | Assert.IsTrue((Type)cache.ParametersPassedToLastStoreCall["ParameterTypeArray"][1] == typeof(List));
343 | }
344 |
345 | [Test]
346 | public void TestClassWithParameterizedCacheAttributeMethodLevelCacheAttributeAlsoWorksForFields()
347 | {
348 | // Arrange
349 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
350 | dynamic testClass1 = WeaverHelper.CreateInstance(Assembly, cache);
351 |
352 | // Act
353 | testClass1.CacheParameterMethodWithField("1", 2);
354 | testClass1.CacheParameterMethodWithField("1", 2);
355 |
356 | // Assert
357 | Assert.IsTrue(cache.NumStoreParameterCalls == 1);
358 | Assert.IsTrue(cache.NumRetrieveCalls == 1);
359 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall.Count == 2);
360 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall["Parameter3"] == true);
361 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall["parameter3"] == 0);
362 | }
363 |
364 | [Test]
365 | public void TestClassWithParameterizedCacheAttributeMethodLevelCacheAttributeOverridesParameters()
366 | {
367 | // Arrange
368 | dynamic cache = WeaverHelper.CreateInstance(Assembly);
369 | dynamic testClass1 = WeaverHelper.CreateInstance(Assembly, cache);
370 |
371 | // Act
372 | testClass1.OverridenCacheParameterMethod("1", 2);
373 | testClass1.OverridenCacheParameterMethod("1", 2);
374 |
375 | // Assert
376 | Assert.IsTrue(cache.NumStoreParameterCalls == 1);
377 | Assert.IsTrue(cache.NumRetrieveCalls == 1);
378 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall.Count == 2);
379 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall["Parameter2"] == 2);
380 | Assert.IsTrue(cache.ParametersPassedToLastStoreCall["Parameter3"] == true);
381 | }
382 | }
383 | }
--------------------------------------------------------------------------------
/MethodCache.Fody/ModuleWeaver.cs:
--------------------------------------------------------------------------------
1 | namespace MethodCache.Fody
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Text.RegularExpressions;
8 | using System.Xml.Linq;
9 | using MethodCache.Attributes;
10 | using Mono.Cecil;
11 | using Mono.Cecil.Cil;
12 | using Mono.Cecil.Rocks;
13 |
14 | public class ModuleWeaver
15 | {
16 | public const string CacheAttributeName = "CacheAttribute";
17 |
18 | public const string CacheGetterName = "Cache";
19 |
20 | public const string CacheTypeContainsMethodName = "Contains";
21 |
22 | public const string CacheTypeRemoveMethodName = "Remove";
23 |
24 | public const string CacheTypeRetrieveMethodName = "Retrieve";
25 |
26 | public const string CacheTypeStoreMethodName = "Store";
27 |
28 | public const string NoCacheAttributeName = "NoCacheAttribute";
29 |
30 | public ModuleWeaver()
31 | {
32 | LogInfo = m => { };
33 | LogWarning = m => { };
34 | LogWarningPoint = (m, p) => { };
35 | LogError = m => { };
36 | LogErrorPoint = (m, p) => { };
37 |
38 | Config = new XElement("MethodCache");
39 | DefineConstants = new List();
40 | }
41 |
42 | public IAssemblyResolver AssemblyResolver { get; set; }
43 |
44 | public XElement Config { get; set; }
45 |
46 | public List DefineConstants { get; set; }
47 |
48 | public Action LogError { get; set; }
49 |
50 | public Action LogErrorPoint { get; set; }
51 |
52 | public Action LogInfo { get; set; }
53 |
54 | public Action LogWarning { get; set; }
55 |
56 | public Action LogWarningPoint { get; set; }
57 |
58 | public ModuleDefinition ModuleDefinition { get; set; }
59 |
60 | public References References { get; set; }
61 |
62 | private bool LogDebugOutput { get; set; }
63 |
64 | private bool MethodCacheEnabledByDefault { get; set; }
65 |
66 | private bool PropertyCacheEnabledByDefault { get; set; }
67 |
68 | public void Execute()
69 | {
70 | LogDebugOutput = DefineConstants.Any(x => x.ToLower() == "debug");
71 |
72 | References = new References() { AssemblyResolver = AssemblyResolver, ModuleDefinition = ModuleDefinition };
73 | References.LoadReferences();
74 |
75 | MethodCacheEnabledByDefault = true;
76 | PropertyCacheEnabledByDefault = true;
77 |
78 | ReadConfiguration();
79 |
80 | MethodsForWeaving methodToWeave = SelectMethods();
81 |
82 | WeaveMethods(methodToWeave.Methods);
83 | WeaveProperties(methodToWeave.Properties);
84 |
85 | RemoveReference();
86 | }
87 |
88 | private static string CreateCacheKeyString(MethodDefinition methodDefinition)
89 | {
90 | StringBuilder builder = new StringBuilder();
91 |
92 | builder.Append(methodDefinition.DeclaringType.FullName);
93 | builder.Append(".");
94 |
95 | if (methodDefinition.IsSpecialName && (methodDefinition.IsSetter || methodDefinition.IsGetter))
96 | {
97 | builder.Append(Regex.Replace(methodDefinition.Name, "[gs]et_", string.Empty));
98 | }
99 | else
100 | {
101 | builder.Append(methodDefinition.Name);
102 |
103 | for (int i = 0; i < methodDefinition.Parameters.Count + methodDefinition.GenericParameters.Count; i++)
104 | {
105 | builder.Append(string.Format("_{{{0}}}", i));
106 | }
107 | }
108 |
109 | return builder.ToString();
110 | }
111 |
112 | private static MethodDefinition GetCacheGetter(MethodDefinition methodDefinition)
113 | {
114 | MethodDefinition propertyGet = methodDefinition.DeclaringType.GetPropertyGet(CacheGetterName);
115 |
116 | propertyGet = propertyGet ??
117 | methodDefinition.DeclaringType.BaseType.Resolve().GetInheritedPropertyGet(CacheGetterName);
118 |
119 | return propertyGet;
120 | }
121 |
122 | private Instruction AppendDebugWrite(Instruction instruction, ILProcessor processor, string message)
123 | {
124 | return instruction
125 | .AppendLdstr(processor, message)
126 | .Append(processor.Create(OpCodes.Call, ModuleDefinition.ImportMethod(References.DebugWriteLineMethod)), processor);
127 | }
128 |
129 | private bool CacheAttributeConstructedWithParam(CustomAttribute attribute, Members cachedMembers)
130 | {
131 | if (attribute == null || attribute.ConstructorArguments.Count == 0)
132 | {
133 | return false;
134 | }
135 |
136 | return Equals(attribute.ConstructorArguments.Single().Value, (int)cachedMembers);
137 | }
138 |
139 | private bool CacheAttributeExcludesMethods(CustomAttribute attribute)
140 | {
141 | return CacheAttributeConstructedWithParam(attribute, Members.Properties);
142 | }
143 |
144 | private bool CacheAttributeExcludesProperties(CustomAttribute attribute)
145 | {
146 | return CacheAttributeConstructedWithParam(attribute, Members.Methods);
147 | }
148 |
149 | private bool CacheAttributeMembersExplicitly(CustomAttribute attribute, Members requiredFlag)
150 | {
151 | if (attribute == null || attribute.ConstructorArguments.Count == 0)
152 | {
153 | return false;
154 | }
155 |
156 | Members constructorArgument = (Members)attribute.ConstructorArguments.Single().Value;
157 |
158 | return constructorArgument.HasFlag(requiredFlag);
159 | }
160 |
161 | private MethodDefinition CacheTypeGetContainsMethod(TypeDefinition cacheType, string cacheTypeContainsMethodName)
162 | {
163 | return cacheType.GetMethod(cacheTypeContainsMethodName, ModuleDefinition.TypeSystem.Boolean,
164 | new[] { ModuleDefinition.TypeSystem.String });
165 | }
166 |
167 | private MethodDefinition CacheTypeGetRemoveMethod(TypeDefinition cacheType, string cacheTypeRemoveMethodName)
168 | {
169 | return cacheType.GetMethod(cacheTypeRemoveMethodName, ModuleDefinition.TypeSystem.Void,
170 | new[] { ModuleDefinition.TypeSystem.String });
171 | }
172 |
173 | private MethodDefinition CacheTypeGetRetrieveMethod(TypeDefinition cacheType, string cacheTypeRetrieveMethodName)
174 | {
175 | return cacheType.GetMethod(cacheTypeRetrieveMethodName, new GenericParameter("T", cacheType),
176 | new[] { ModuleDefinition.TypeSystem.String });
177 | }
178 |
179 | private MethodDefinition CacheTypeGetStoreMethod(TypeDefinition cacheInterface, string cacheTypeStoreMethodName)
180 | {
181 | // Prioritize Store methods with parameters Dictionary
182 | MethodDefinition methodDefinition = cacheInterface.GetMethod(cacheTypeStoreMethodName, ModuleDefinition.TypeSystem.Void,
183 | new[]
184 | {
185 | ModuleDefinition.TypeSystem.String, ModuleDefinition.TypeSystem.Object,
186 | References.DictionaryInterface.MakeGenericInstanceType(ModuleDefinition.TypeSystem.String,
187 | ModuleDefinition.TypeSystem.Object)
188 | });
189 |
190 | if (methodDefinition != null)
191 | {
192 | return methodDefinition;
193 | }
194 |
195 | return cacheInterface.GetMethod(cacheTypeStoreMethodName, ModuleDefinition.TypeSystem.Void,
196 | new[] { ModuleDefinition.TypeSystem.String, ModuleDefinition.TypeSystem.Object });
197 | }
198 |
199 | private bool CheckCacheTypeMethods(TypeDefinition cacheType)
200 | {
201 | LogInfo(string.Format("Checking CacheType methods ({0}, {1}, {2}).", CacheTypeContainsMethodName,
202 | CacheTypeStoreMethodName, CacheTypeRetrieveMethodName));
203 |
204 | if (CacheTypeGetContainsMethod(cacheType, CacheTypeContainsMethodName) == null)
205 | {
206 | LogWarning(string.Format("Method {0} missing in {1}.", CacheTypeContainsMethodName, cacheType.FullName));
207 |
208 | return false;
209 | }
210 |
211 | if (CacheTypeGetStoreMethod(cacheType, CacheTypeStoreMethodName) == null)
212 | {
213 | LogWarning(string.Format("Method {0} missing in {1}.", CacheTypeStoreMethodName, cacheType.FullName));
214 |
215 | return false;
216 | }
217 |
218 | if (CacheTypeGetRetrieveMethod(cacheType, CacheTypeRetrieveMethodName) == null)
219 | {
220 | LogWarning(string.Format("Method {0} missing in {1}.", CacheTypeRetrieveMethodName, cacheType.FullName));
221 |
222 | return false;
223 | }
224 |
225 | LogInfo(string.Format("CacheInterface methods found."));
226 |
227 | return true;
228 | }
229 |
230 | private bool ConfigHasAttribute(string name, string value)
231 | {
232 | return
233 | Config.Attributes().Any(x => x.Name.ToString().ToLower() == name.ToLower() && x.Value.ToLower() == value.ToLower());
234 | }
235 |
236 | private IDictionary GetCacheAttributeConstructorFields(CustomAttribute attribute)
237 | {
238 | return attribute.Fields.ToDictionary(field => field.Name, field => field.Argument);
239 | }
240 |
241 | private IDictionary GetCacheAttributeConstructorParameters(CustomAttribute attribute)
242 | {
243 | return attribute.Properties.ToDictionary(property => property.Name, property => property.Argument);
244 | }
245 |
246 | private Instruction InjectCacheKeyCreatedCode(MethodDefinition methodDefinition, Instruction current,
247 | ILProcessor processor, int cacheKeyIndex)
248 | {
249 | if (LogDebugOutput)
250 | {
251 | // Call Debug.WriteLine with CacheKey
252 | current = current
253 | .AppendLdstr(processor, "CacheKey created: {0}")
254 | .AppendLdcI4(processor, 1)
255 | .Append(processor.Create(OpCodes.Newarr, ModuleDefinition.TypeSystem.Object), processor)
256 | .AppendDup(processor)
257 | .AppendLdcI4(processor, 0)
258 | .AppendLdloc(processor, cacheKeyIndex)
259 | .Append(processor.Create(OpCodes.Stelem_Ref), processor)
260 | .Append(processor.Create(OpCodes.Call, methodDefinition.Module.ImportMethod(References.StringFormatMethod)),
261 | processor)
262 | .Append(processor.Create(OpCodes.Call, methodDefinition.Module.ImportMethod(References.DebugWriteLineMethod)),
263 | processor);
264 | }
265 |
266 | return current;
267 | }
268 |
269 | private bool IsMethodValidForWeaving(MethodDefinition propertyGet, MethodDefinition methodDefinition)
270 | {
271 | if (propertyGet == null)
272 | {
273 | LogWarning(string.Format("Class {0} does not contain or inherit Getter {1}. Skip weaving of method {2}.",
274 | methodDefinition.DeclaringType.Name, CacheGetterName, methodDefinition.Name));
275 |
276 | return false;
277 | }
278 |
279 | if (methodDefinition.IsStatic && !propertyGet.IsStatic)
280 | {
281 | LogWarning(string.Format("Method {2} of Class {0} is static, Getter {1} is not. Skip weaving of method {2}.",
282 | methodDefinition.DeclaringType.Name, CacheGetterName, methodDefinition.Name));
283 |
284 | return false;
285 | }
286 |
287 | if (!CheckCacheTypeMethods(propertyGet.ReturnType.Resolve()))
288 | {
289 | LogWarning(
290 | string.Format(
291 | "ReturnType {0} of Getter {1} of Class {2} does not implement all methods. Skip weaving of method {3}.",
292 | propertyGet.ReturnType.Name, CacheGetterName, methodDefinition.DeclaringType.Name, methodDefinition.Name));
293 |
294 | return false;
295 | }
296 |
297 | return true;
298 | }
299 |
300 | private bool IsPropertySetterValidForWeaving(MethodDefinition propertyGet, MethodDefinition methodDefinition)
301 | {
302 | if (!IsMethodValidForWeaving(propertyGet, methodDefinition))
303 | {
304 | return false;
305 | }
306 |
307 | if (CacheTypeGetRemoveMethod(propertyGet.ReturnType.Resolve(), CacheTypeRemoveMethodName) == null)
308 | {
309 | LogWarning(string.Format("Method {0} missing in {1}.", CacheTypeRemoveMethodName,
310 | propertyGet.ReturnType.Resolve().FullName));
311 |
312 | LogWarning(
313 | string.Format(
314 | "ReturnType {0} of Getter {1} of Class {2} does not implement all methods. Skip weaving of method {3}.",
315 | propertyGet.ReturnType.Name, CacheGetterName, methodDefinition.DeclaringType.Name, methodDefinition.Name));
316 |
317 | return false;
318 | }
319 |
320 | return true;
321 | }
322 |
323 | private void ReadConfiguration()
324 | {
325 | if (ConfigHasAttribute("SkipDebugOutput", "true"))
326 | {
327 | LogWarning("Skipping Debug Output.");
328 | LogDebugOutput = false;
329 | }
330 |
331 | if (ConfigHasAttribute("CacheProperties", "false"))
332 | {
333 | LogWarning("Disabling property weaving.");
334 | PropertyCacheEnabledByDefault = false;
335 | }
336 |
337 | if (ConfigHasAttribute("CacheMethods", "false"))
338 | {
339 | LogWarning("Disabling method weaving.");
340 | MethodCacheEnabledByDefault = false;
341 | }
342 | }
343 |
344 | private void RemoveReference()
345 | {
346 | AssemblyNameReference referenceToRemove =
347 | ModuleDefinition.AssemblyReferences.FirstOrDefault(x => x.Name == "MethodCache.Attributes");
348 |
349 | if (referenceToRemove == null)
350 | {
351 | LogInfo("No reference to 'MethodCache.Attributes.dll' found. References not modified.");
352 |
353 | return;
354 | }
355 |
356 | ModuleDefinition.AssemblyReferences.Remove(referenceToRemove);
357 |
358 | LogInfo("Removing reference to 'MethodCache.Attributes.dll'.");
359 | }
360 |
361 | private MethodsForWeaving SelectMethods()
362 | {
363 | LogInfo(string.Format("Searching for Methods and Properties in assembly ({0}).", ModuleDefinition.Name));
364 |
365 | MethodsForWeaving result = new MethodsForWeaving();
366 |
367 | foreach (TypeDefinition type in ModuleDefinition.Types)
368 | {
369 | foreach (MethodDefinition method in type.Methods)
370 | {
371 | if (ShouldWeaveMethod(method))
372 | {
373 | // Store Cache attribute, method attribute takes precedence over class attributes
374 | CustomAttribute attribute =
375 | method.CustomAttributes.SingleOrDefault(x => x.Constructor.DeclaringType.Name == CacheAttributeName) ??
376 | method.DeclaringType.CustomAttributes.SingleOrDefault(
377 | x => x.Constructor.DeclaringType.Name == CacheAttributeName);
378 |
379 | result.Add(method, attribute);
380 | }
381 |
382 | method.RemoveAttribute(CacheAttributeName);
383 | method.RemoveAttribute(NoCacheAttributeName);
384 | }
385 |
386 | foreach (PropertyDefinition property in type.Properties)
387 | {
388 | if (ShouldWeaveProperty(property))
389 | {
390 | // Store Cache attribute, property attribute takes precedence over class attributes
391 | CustomAttribute attribute =
392 | property.CustomAttributes.SingleOrDefault(x => x.Constructor.DeclaringType.Name == CacheAttributeName) ??
393 | property.DeclaringType.CustomAttributes.SingleOrDefault(
394 | x => x.Constructor.DeclaringType.Name == CacheAttributeName);
395 |
396 | result.Add(property, attribute);
397 | }
398 |
399 | property.RemoveAttribute(CacheAttributeName);
400 | property.RemoveAttribute(NoCacheAttributeName);
401 | }
402 |
403 | type.RemoveAttribute(CacheAttributeName);
404 | }
405 |
406 | return result;
407 | }
408 |
409 | private Instruction SetCacheKeyLocalVariable(Instruction current, MethodDefinition methodDefinition,
410 | ILProcessor processor, int cacheKeyIndex, int objectArrayIndex)
411 | {
412 | if (methodDefinition.IsSetter || methodDefinition.IsGetter)
413 | {
414 | return current.AppendStloc(processor, cacheKeyIndex);
415 | }
416 | else
417 | {
418 | // Create object[] for string.format
419 | int parameterCount = methodDefinition.Parameters.Count + methodDefinition.GenericParameters.Count;
420 |
421 | current = current
422 | .AppendLdcI4(processor, parameterCount)
423 | .Append(processor.Create(OpCodes.Newarr, ModuleDefinition.TypeSystem.Object), processor)
424 | .AppendStloc(processor, objectArrayIndex);
425 |
426 |
427 | // Set object[] values
428 | for (int i = 0; i < methodDefinition.GenericParameters.Count; i++)
429 | {
430 | current = current
431 | .AppendLdloc(processor, objectArrayIndex)
432 | .AppendLdcI4(processor, i)
433 | .Append(processor.Create(OpCodes.Ldtoken, methodDefinition.GenericParameters[i]), processor)
434 | .Append(processor.Create(OpCodes.Call, methodDefinition.Module.ImportMethod(References.SystemTypeGetTypeFromHandleMethod)),
435 | processor)
436 | .Append(processor.Create(OpCodes.Stelem_Ref), processor);
437 | }
438 |
439 | for (int i = 0; i < methodDefinition.Parameters.Count; i++)
440 | {
441 | current = current
442 | .AppendLdloc(processor, objectArrayIndex)
443 | .AppendLdcI4(processor, methodDefinition.GenericParameters.Count + i)
444 | .AppendLdarg(processor, !methodDefinition.IsStatic ? i + 1 : i)
445 | .AppendBoxIfNecessary(processor, methodDefinition.Parameters[i].ParameterType)
446 | .Append(processor.Create(OpCodes.Stelem_Ref), processor);
447 | }
448 |
449 | // Call string.format
450 | return current
451 | .AppendLdloc(processor, objectArrayIndex)
452 | .Append(processor.Create(OpCodes.Call, methodDefinition.Module.ImportMethod(References.StringFormatMethod)),
453 | processor)
454 | .AppendStloc(processor, cacheKeyIndex);
455 | }
456 | }
457 |
458 | private bool ShouldWeaveMethod(MethodDefinition method)
459 | {
460 | CustomAttribute classLevelCacheAttribute =
461 | method.DeclaringType.CustomAttributes.SingleOrDefault(x => x.Constructor.DeclaringType.Name == CacheAttributeName);
462 |
463 | bool hasClassLevelCache = classLevelCacheAttribute != null &&
464 | !CacheAttributeExcludesMethods(classLevelCacheAttribute);
465 | bool hasMethodLevelCache = method.ContainsAttribute(CacheAttributeName);
466 | bool hasNoCacheAttribute = method.ContainsAttribute(NoCacheAttributeName);
467 | bool isSpecialName = method.IsSpecialName || method.IsGetter || method.IsSetter || method.IsConstructor;
468 | bool isCompilerGenerated = method.ContainsAttribute(References.CompilerGeneratedAttribute);
469 |
470 | if (hasNoCacheAttribute || isSpecialName || isCompilerGenerated)
471 | {
472 | // Never weave property accessors, special methods and compiler generated methods
473 | return false;
474 | }
475 |
476 | if (hasMethodLevelCache)
477 | {
478 | // Always weave methods explicitly marked for cache
479 | return true;
480 | }
481 |
482 | if (hasClassLevelCache && !CacheAttributeExcludesMethods(classLevelCacheAttribute))
483 | {
484 | // Otherwise weave if marked at class level
485 | return MethodCacheEnabledByDefault || CacheAttributeMembersExplicitly(classLevelCacheAttribute, Members.Methods);
486 | }
487 |
488 | return false;
489 | }
490 |
491 | private bool ShouldWeaveProperty(PropertyDefinition property)
492 | {
493 | CustomAttribute classLevelCacheAttribute =
494 | property.DeclaringType.CustomAttributes.SingleOrDefault(x => x.Constructor.DeclaringType.Name == CacheAttributeName);
495 |
496 | bool hasClassLevelCache = classLevelCacheAttribute != null;
497 | bool hasPropertyLevelCache = property.ContainsAttribute(CacheAttributeName);
498 | bool hasNoCacheAttribute = property.ContainsAttribute(NoCacheAttributeName);
499 | bool isCacheGetter = property.Name == CacheGetterName;
500 | bool hasGetAccessor = property.GetMethod != null;
501 | bool isAutoProperty = hasGetAccessor && property.GetMethod.ContainsAttribute(References.CompilerGeneratedAttribute);
502 |
503 | if (hasNoCacheAttribute || isCacheGetter || isAutoProperty || !hasGetAccessor)
504 | {
505 | // Never weave Cache property, auto-properties, write-only properties and properties explicitly excluded
506 | return false;
507 | }
508 |
509 | if (hasPropertyLevelCache)
510 | {
511 | // Always weave properties explicitly marked for cache
512 | return true;
513 | }
514 |
515 | if (hasClassLevelCache && !CacheAttributeExcludesProperties(classLevelCacheAttribute))
516 | {
517 | // Otherwise weave if marked at class level
518 | return PropertyCacheEnabledByDefault ||
519 | CacheAttributeMembersExplicitly(classLevelCacheAttribute, Members.Properties);
520 | }
521 |
522 | return false;
523 | }
524 |
525 | private void WeaveMethod(MethodDefinition methodDefinition, CustomAttribute attribute, MethodReference propertyGet)
526 | {
527 | methodDefinition.Body.InitLocals = true;
528 |
529 | methodDefinition.Body.SimplifyMacros();
530 |
531 | if (propertyGet.DeclaringType.HasGenericParameters)
532 | {
533 | propertyGet = propertyGet.MakeHostInstanceGeneric(propertyGet.DeclaringType.GenericParameters.Cast().ToArray());
534 | }
535 |
536 | Instruction firstInstruction = methodDefinition.Body.Instructions.First();
537 |
538 | ICollection returnInstructions =
539 | methodDefinition.Body.Instructions.ToList().Where(x => x.OpCode == OpCodes.Ret).ToList();
540 |
541 | if (returnInstructions.Count == 0)
542 | {
543 | LogWarning(string.Format("Method {0} does not contain any return statement. Skip weaving of method {0}.",
544 | methodDefinition.Name));
545 | return;
546 | }
547 |
548 | // Add local variables
549 | int cacheKeyIndex = methodDefinition.AddVariable(ModuleDefinition.TypeSystem.String);
550 | int resultIndex = methodDefinition.AddVariable(methodDefinition.ReturnType);
551 | int objectArrayIndex = methodDefinition.AddVariable(ModuleDefinition.TypeSystem.Object.MakeArrayType());
552 |
553 | ILProcessor processor = methodDefinition.Body.GetILProcessor();
554 |
555 | // Generate CacheKeyTemplate
556 | string cacheKey = CreateCacheKeyString(methodDefinition);
557 |
558 | Instruction current = firstInstruction.Prepend(processor.Create(OpCodes.Ldstr, cacheKey), processor);
559 |
560 | current = SetCacheKeyLocalVariable(current, methodDefinition, processor, cacheKeyIndex, objectArrayIndex);
561 |
562 | current = InjectCacheKeyCreatedCode(methodDefinition, current, processor, cacheKeyIndex);
563 |
564 | TypeDefinition propertyGetReturnTypeDefinition = propertyGet.ReturnType.Resolve();
565 |
566 | if (!propertyGet.Resolve().IsStatic)
567 | {
568 | current = current.AppendLdarg(processor, 0);
569 | }
570 |
571 | MethodReference methodReferenceContain =
572 | methodDefinition.Module.Import(CacheTypeGetContainsMethod(propertyGetReturnTypeDefinition,
573 | CacheTypeContainsMethodName));
574 |
575 | current = current
576 | .Append(processor.Create(OpCodes.Call, methodDefinition.Module.Import(propertyGet)), processor)
577 | .AppendLdloc(processor, cacheKeyIndex)
578 | .Append(processor.Create(OpCodes.Callvirt, methodReferenceContain), processor)
579 | .Append(processor.Create(OpCodes.Brfalse, firstInstruction), processor);
580 |
581 | // False branche (store value in cache of each return instruction)
582 | foreach (Instruction returnInstruction in returnInstructions)
583 | {
584 | returnInstruction.Previous.AppendStloc(processor, resultIndex);
585 |
586 | if (LogDebugOutput)
587 | {
588 | AppendDebugWrite(returnInstruction.Previous, processor, "Storing to cache.");
589 | }
590 |
591 | if (!propertyGet.Resolve().IsStatic)
592 | {
593 | returnInstruction.Previous.AppendLdarg(processor, 0);
594 | }
595 |
596 | MethodReference methodReferenceStore =
597 | methodDefinition.Module.Import(CacheTypeGetStoreMethod(propertyGetReturnTypeDefinition, CacheTypeStoreMethodName));
598 |
599 | returnInstruction.Previous
600 | .Append(processor.Create(OpCodes.Call, methodDefinition.Module.Import(propertyGet)), processor)
601 | .AppendLdloc(processor, cacheKeyIndex)
602 | .AppendLdloc(processor, resultIndex)
603 | .AppendBoxIfNecessary(processor, methodDefinition.ReturnType);
604 |
605 | // Pass parameters to Store method if supported
606 | if (methodReferenceStore.Parameters.Count == 3)
607 | {
608 | returnInstruction.Previous
609 | .Append(processor.Create(OpCodes.Newobj,
610 | methodDefinition.Module.Import(References.DictionaryConstructor)), processor);
611 |
612 | foreach (CustomAttributeNamedArgument property in attribute.Properties.Union(attribute.Fields))
613 | {
614 | returnInstruction.Previous
615 | .AppendDup(processor)
616 | .AppendLdstr(processor, property.Name)
617 | .AppendLd(processor, property.Argument, References)
618 | .AppendBoxIfNecessary(processor,
619 | property.Argument.Type != ModuleDefinition.TypeSystem.Object
620 | ? property.Argument.Type : ((CustomAttributeArgument)property.Argument.Value).Type)
621 | .Append(processor.Create(OpCodes.Callvirt, methodDefinition.Module.Import(References.DictionaryAddMethod)),
622 | processor);
623 | }
624 | }
625 |
626 | returnInstruction.Previous
627 | .Append(processor.Create(OpCodes.Callvirt, methodReferenceStore), processor)
628 | .AppendLdloc(processor, resultIndex);
629 | }
630 |
631 | if (LogDebugOutput)
632 | {
633 | current = AppendDebugWrite(current, processor, "Loading from cache.");
634 | }
635 |
636 | if (!propertyGet.Resolve().IsStatic)
637 | {
638 | current = current.AppendLdarg(processor, 0);
639 | }
640 |
641 | // Start of branche true
642 | MethodReference methodReferenceRetrieve =
643 | methodDefinition.Module.Import(CacheTypeGetRetrieveMethod(propertyGetReturnTypeDefinition,
644 | CacheTypeRetrieveMethodName)).MakeGeneric(new[] { methodDefinition.ReturnType });
645 |
646 | current.Append(processor.Create(OpCodes.Call, methodDefinition.Module.Import(propertyGet)), processor)
647 | .AppendLdloc(processor, cacheKeyIndex)
648 | .Append(processor.Create(OpCodes.Callvirt, methodReferenceRetrieve), processor)
649 | .AppendStloc(processor, resultIndex)
650 | .Append(processor.Create(OpCodes.Br, returnInstructions.Last().Previous), processor);
651 |
652 | methodDefinition.Body.OptimizeMacros();
653 | }
654 |
655 | private void WeaveMethod(MethodDefinition methodDefinition, CustomAttribute attribute)
656 | {
657 | MethodDefinition propertyGet = GetCacheGetter(methodDefinition);
658 |
659 | if (!IsMethodValidForWeaving(propertyGet, methodDefinition))
660 | {
661 | return;
662 | }
663 |
664 | if (methodDefinition.ReturnType == methodDefinition.Module.TypeSystem.Void)
665 | {
666 | LogWarning(string.Format("Method {0} returns void. Skip weaving of method {0}.", methodDefinition.Name));
667 |
668 | return;
669 | }
670 |
671 | LogInfo(string.Format("Weaving method {0}::{1}.", methodDefinition.DeclaringType.Name, methodDefinition.Name));
672 |
673 | WeaveMethod(methodDefinition, attribute, propertyGet);
674 | }
675 |
676 | private void WeaveMethods(IEnumerable> methodDefinitions)
677 | {
678 | foreach (Tuple methodDefinition in methodDefinitions)
679 | {
680 | WeaveMethod(methodDefinition.Item1, methodDefinition.Item2);
681 | }
682 | }
683 |
684 | private void WeaveProperties(IEnumerable> properties)
685 | {
686 | foreach (Tuple propertyTuple in properties)
687 | {
688 | PropertyDefinition property = propertyTuple.Item1;
689 | CustomAttribute attribute = propertyTuple.Item2;
690 |
691 | // Get-Only Property, weave like normal methods
692 | if (property.SetMethod == null)
693 | {
694 | WeaveMethod(property.GetMethod, attribute);
695 | }
696 | else
697 | {
698 | MethodDefinition propertyGet = GetCacheGetter(property.SetMethod);
699 |
700 | if (!IsPropertySetterValidForWeaving(propertyGet, property.SetMethod))
701 | {
702 | continue;
703 | }
704 |
705 | LogInfo(string.Format("Weaving property {0}::{1}.", property.DeclaringType.Name, property.Name));
706 |
707 | WeaveMethod(property.GetMethod, attribute, propertyGet);
708 | WeavePropertySetter(property.SetMethod, propertyGet);
709 | }
710 | }
711 | }
712 |
713 | private void WeavePropertySetter(MethodDefinition setter, MethodReference propertyGet)
714 | {
715 | setter.Body.InitLocals = true;
716 | setter.Body.SimplifyMacros();
717 |
718 | if (propertyGet.DeclaringType.HasGenericParameters)
719 | {
720 | propertyGet = propertyGet.MakeHostInstanceGeneric(propertyGet.DeclaringType.GenericParameters.Cast().ToArray());
721 | }
722 |
723 | Instruction firstInstruction = setter.Body.Instructions.First();
724 | ILProcessor processor = setter.Body.GetILProcessor();
725 |
726 | // Add local variables
727 | int cacheKeyIndex = setter.AddVariable(ModuleDefinition.TypeSystem.String);
728 |
729 | // Generate CacheKeyTemplate
730 | string cacheKey = CreateCacheKeyString(setter);
731 |
732 | Instruction current = firstInstruction.Prepend(processor.Create(OpCodes.Ldstr, cacheKey), processor);
733 |
734 | // Create set cache key
735 | current = current.AppendStloc(processor, cacheKeyIndex);
736 |
737 | current = InjectCacheKeyCreatedCode(setter, current, processor, cacheKeyIndex);
738 |
739 | if (LogDebugOutput)
740 | {
741 | current = AppendDebugWrite(current, processor, "Clearing cache.");
742 | }
743 |
744 | if (!propertyGet.Resolve().IsStatic)
745 | {
746 | current = current.AppendLdarg(processor, 0);
747 | }
748 |
749 | current
750 | .Append(processor.Create(OpCodes.Call, setter.Module.Import(propertyGet)), processor)
751 | .AppendLdloc(processor, cacheKeyIndex)
752 | .Append(processor.Create(OpCodes.Callvirt, setter.Module.Import(
753 | CacheTypeGetRemoveMethod(propertyGet.ReturnType.Resolve(), CacheTypeRemoveMethodName))), processor);
754 |
755 | setter.Body.OptimizeMacros();
756 | }
757 | }
758 | }
--------------------------------------------------------------------------------