├── _config.yml ├── SimpleProxy.Diagnostics ├── DiagnosticsAttribute.cs ├── SimpleProxy.Diagnostics.csproj └── DiagnosticsInterceptor.cs ├── Unit Tests └── SimpleProxy.Tests │ ├── UnitTest1.cs │ └── SimpleProxy.Tests.csproj ├── SampleApp ├── ITestClass.cs ├── SampleApp.csproj ├── Program.cs └── TestClass.cs ├── SimpleProxy.Logging ├── SimpleProxy.Logging.csproj ├── LogAttribute.cs └── LogInterceptor.cs ├── SimpleProxy.Caching ├── SimpleProxy.Caching.csproj ├── CacheAttribute.cs └── CacheInterceptor.cs ├── SimpleProxy ├── Attributes │ └── MethodInterceptionAttribute.cs ├── Internal │ ├── Interfaces │ │ └── IInterceptorMapping.cs │ ├── Configuration │ │ └── InterceptorMapping.cs │ ├── ProxyFactory.cs │ ├── Extensions │ │ └── InvocationExtensions.cs │ └── CoreInterceptor.cs ├── Interfaces │ ├── IMethodInterceptor.cs │ ├── IHideBaseTypes.cs │ └── IOrderingStrategy.cs ├── Exceptions │ └── InvalidInterceptorException.cs ├── Strategies │ ├── SequentialOrderStrategy.cs │ └── PyramidOrderStrategy.cs ├── SimpleProxy.csproj ├── Configuration │ └── SimpleProxyConfiguration.cs ├── Extensions │ └── ServiceCollectionExtensions.cs └── InvocationContext.cs ├── SimpleProxy.UnitOfWork ├── SimpleProxy.UnitOfWork.csproj ├── IUnitOfWorkFactory.cs ├── UnitOfWorkAttribute.cs ├── DatabaseContext.cs ├── UnitOfWorkFactory.cs └── UnitOfWorkInterceptor.cs ├── LICENSE ├── README.md ├── SimpleProxy.sln └── .gitignore /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /SimpleProxy.Diagnostics/DiagnosticsAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleProxy.Diagnostics 2 | { 3 | using Attributes; 4 | 5 | public class DiagnosticsAttribute : MethodInterceptionAttribute 6 | { 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Unit Tests/SimpleProxy.Tests/UnitTest1.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleProxy.Tests 2 | { 3 | using NUnit.Framework; 4 | 5 | [TestFixture] 6 | public class UnitTest1 7 | { 8 | [Test] 9 | public void Test1() 10 | { 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /SampleApp/ITestClass.cs: -------------------------------------------------------------------------------- 1 | namespace SampleApp 2 | { 3 | using System; 4 | using System.Threading.Tasks; 5 | 6 | public interface ITestClass 7 | { 8 | DateTime TestMethod(); 9 | DateTime TestMethodWithExpirationPolicy(); 10 | Task TestMethodAsync(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /SimpleProxy.Diagnostics/SimpleProxy.Diagnostics.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /SimpleProxy.Logging/SimpleProxy.Logging.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /SimpleProxy.Caching/SimpleProxy.Caching.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /SimpleProxy.Caching/CacheAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleProxy.Caching 2 | { 3 | using Attributes; 4 | using System; 5 | 6 | /// 7 | /// Invocation Attribute that applies caching 8 | /// 9 | public class CacheAttribute : MethodInterceptionAttribute 10 | { 11 | /// 12 | /// Set the milliseconds to expire the registry 13 | /// 14 | public int MillisecondsToExpire { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SimpleProxy/Attributes/MethodInterceptionAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleProxy.Attributes 2 | { 3 | using System; 4 | using System.Diagnostics.CodeAnalysis; 5 | 6 | /// 7 | /// Method Interception Attribute (inherit from this to create your interception attributes) 8 | /// 9 | [AttributeUsage(AttributeTargets.Method)] 10 | [ExcludeFromCodeCoverage] 11 | public abstract class MethodInterceptionAttribute : Attribute 12 | { 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /SimpleProxy/Internal/Interfaces/IInterceptorMapping.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleProxy.Internal.Interfaces 2 | { 3 | using System; 4 | 5 | /// 6 | /// Interceptor Mapping Interface 7 | /// 8 | internal interface IInterceptorMapping 9 | { 10 | /// 11 | /// Attribute Type 12 | /// 13 | Type AttributeType { get; } 14 | 15 | /// 16 | /// Interceptor Type 17 | /// 18 | Type InterceptorType { get; } 19 | } 20 | } -------------------------------------------------------------------------------- /SimpleProxy.UnitOfWork/SimpleProxy.UnitOfWork.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /SimpleProxy.UnitOfWork/IUnitOfWorkFactory.cs: -------------------------------------------------------------------------------- 1 | using Arch.EntityFrameworkCore.UnitOfWork; 2 | 3 | 4 | namespace SimpleProxy.UnitOfWork 5 | { 6 | using Microsoft.EntityFrameworkCore; 7 | 8 | /// 9 | /// The Unit of Work Factory for creating Unit of Work instances with different DbContext Types 10 | /// 11 | public interface IUnitOfWorkFactory 12 | { 13 | /// 14 | /// Provides an instance of the with the Injected 15 | /// 16 | IUnitOfWork Create() where TDbContextType : DbContext; 17 | } 18 | } -------------------------------------------------------------------------------- /Unit Tests/SimpleProxy.Tests/SimpleProxy.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | all 14 | runtime; build; native; contentfiles; analyzers; buildtransitive 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /SimpleProxy/Interfaces/IMethodInterceptor.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleProxy.Interfaces 2 | { 3 | /// 4 | /// Defines a mapping between an interceptor and the attribute the interceptor responds to. 5 | /// 6 | public interface IMethodInterceptor 7 | { 8 | /// 9 | /// Method that is invoked instead of the method to which the interceptor has been applied 10 | /// 11 | void BeforeInvoke(InvocationContext invocationContext); 12 | 13 | /// 14 | /// Method that is invoked instead of the method to which the interceptor has been applied 15 | /// 16 | void AfterInvoke(InvocationContext invocationContext, object methodResult); 17 | } 18 | } -------------------------------------------------------------------------------- /SimpleProxy/Exceptions/InvalidInterceptorException.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleProxy.Exceptions 2 | { 3 | using System; 4 | using System.Diagnostics.CodeAnalysis; 5 | 6 | /// 7 | /// Exception thrown when an Interceptor Attribute is applied but the Interceptor hasnt been configured 8 | /// 9 | [ExcludeFromCodeCoverage] 10 | public class InvalidInterceptorException : Exception 11 | { 12 | /// 13 | /// Initialises a new instance of the class 14 | /// 15 | /// Error Message Text 16 | public InvalidInterceptorException(string errorMessage) : base(errorMessage) 17 | { 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /SimpleProxy/Strategies/SequentialOrderStrategy.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleProxy.Strategies 2 | { 3 | using Interfaces; 4 | using System.Collections.Generic; 5 | 6 | /// 7 | /// Sequential Ordering Strategy for Interceptors 8 | /// 9 | public sealed class SequentialOrderStrategy : IOrderingStrategy 10 | { 11 | /// 12 | public IEnumerable OrderBeforeInterception(IEnumerable interceptors) 13 | { 14 | return interceptors; 15 | } 16 | 17 | /// 18 | public IEnumerable OrderAfterInterception(IEnumerable interceptors) 19 | { 20 | return interceptors; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /SimpleProxy.Logging/LogAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleProxy.Logging 2 | { 3 | using Attributes; 4 | using Microsoft.Extensions.Logging; 5 | 6 | /// 7 | /// Method Interceptor Example Attribute 8 | /// 9 | public class LogAttribute : MethodInterceptionAttribute 10 | { 11 | /// 12 | /// The Logging Level 13 | /// 14 | internal LogLevel LoggingLevel { get; set; } 15 | 16 | /// 17 | /// Initialises a new instance of the 18 | /// 19 | /// Logging Level 20 | public LogAttribute(LogLevel loggingLevel) 21 | { 22 | this.LoggingLevel = loggingLevel; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /SimpleProxy/Strategies/PyramidOrderStrategy.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleProxy.Strategies 2 | { 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using Interfaces; 6 | 7 | /// 8 | /// Pyramid Ordering Strategy for Interceptors 9 | /// 10 | public sealed class PyramidOrderStrategy : IOrderingStrategy 11 | { 12 | /// 13 | public IEnumerable OrderBeforeInterception(IEnumerable interceptors) 14 | { 15 | return interceptors; 16 | } 17 | 18 | /// 19 | public IEnumerable OrderAfterInterception(IEnumerable interceptors) 20 | { 21 | return interceptors.Reverse(); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /SimpleProxy/Interfaces/IHideBaseTypes.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleProxy.Interfaces 2 | { 3 | using System; 4 | using System.ComponentModel; 5 | 6 | /// 7 | /// Daniel Cazzulino's hack to hide methods defined on for IntelliSense on Fluent Interfaces 8 | /// 9 | [EditorBrowsable(EditorBrowsableState.Never)] 10 | public interface IHideBaseTypes 11 | { 12 | /// 13 | [EditorBrowsable(EditorBrowsableState.Never)] 14 | Type GetType(); 15 | 16 | /// 17 | [EditorBrowsable(EditorBrowsableState.Never)] 18 | int GetHashCode(); 19 | 20 | /// 21 | [EditorBrowsable(EditorBrowsableState.Never)] 22 | string ToString(); 23 | 24 | /// 25 | [EditorBrowsable(EditorBrowsableState.Never)] 26 | bool Equals(object other); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /SimpleProxy/SimpleProxy.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | Simple Proxy is a simple extension library built to solve the problem of Aspect Orientated Programming in Net Core projects 6 | Robert Perry 7 | https://github.com/f135ta/SimpleProxy 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /SimpleProxy/Interfaces/IOrderingStrategy.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleProxy.Interfaces 2 | { 3 | using System.Collections.Generic; 4 | 5 | /// 6 | /// Interface to define an ordering strategy 7 | /// 8 | public interface IOrderingStrategy 9 | { 10 | /// 11 | /// Defines the order of interceptors before the method is executed 12 | /// 13 | /// Interceptor Collection 14 | /// Ordered Collection of Interceptors 15 | IEnumerable OrderBeforeInterception(IEnumerable interceptors); 16 | 17 | /// 18 | /// Defines the order of interceptors after the method is executed 19 | /// 20 | /// Interceptor Collection 21 | /// Ordered Collection of Interceptors 22 | IEnumerable OrderAfterInterception(IEnumerable interceptors); 23 | } 24 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Robert Perry 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /SimpleProxy/Internal/Configuration/InterceptorMapping.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleProxy.Internal.Configuration 2 | { 3 | using System; 4 | using Attributes; 5 | using Interfaces; 6 | using SimpleProxy.Interfaces; 7 | 8 | /// 9 | /// Maps an interceptor to an attribute 10 | /// 11 | internal class InterceptorMapping : IInterceptorMapping where TAttribute : MethodInterceptionAttribute where TInterceptor : IMethodInterceptor 12 | { 13 | /// 14 | /// Gets the type of the Interceptor 15 | /// 16 | public Type InterceptorType { get; } 17 | 18 | /// 19 | /// Gets the instance of the attribute, which applies interceptor to a method. 20 | /// 21 | public Type AttributeType { get; } 22 | 23 | /// 24 | /// Initialises a new instance of the 25 | /// 26 | public InterceptorMapping() 27 | { 28 | this.InterceptorType = typeof(TInterceptor); 29 | this.AttributeType = typeof(TAttribute); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /SimpleProxy.UnitOfWork/UnitOfWorkAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleProxy.UnitOfWork 2 | { 3 | using System; 4 | using Attributes; 5 | 6 | /// 7 | /// The Unit of Work Attribute 8 | /// 9 | [AttributeUsage(AttributeTargets.Method)] 10 | public class UnitOfWorkAttribute : MethodInterceptionAttribute 11 | { 12 | /// 13 | /// Gets the DbContext Type 14 | /// 15 | public Type DbContextType; 16 | 17 | /// 18 | /// Gets or sets a value to override the ChangeTracking setting 19 | /// 20 | public bool ChangeTrackingEnabled; 21 | 22 | /// 23 | /// Gets or sets a value which automatically calls DbContext.SaveChanges() at the end of the method 24 | /// 25 | public bool SaveChanges; 26 | 27 | /// 28 | /// Initialises a new instance of the 29 | /// 30 | public UnitOfWorkAttribute(Type dbContextType, bool enableChangeTracking = false, bool saveChanges = false) 31 | { 32 | this.DbContextType = dbContextType; 33 | this.ChangeTrackingEnabled = enableChangeTracking; 34 | this.SaveChanges = saveChanges; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /SampleApp/SampleApp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | latest 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /SimpleProxy.UnitOfWork/DatabaseContext.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleProxy.UnitOfWork 2 | { 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using Microsoft.EntityFrameworkCore; 6 | 7 | /// 8 | /// Example Data Model 9 | /// 10 | public class DatabaseContext : DbContext 11 | { 12 | public DbSet Users { get; set; } 13 | 14 | /// 15 | public DatabaseContext(DbContextOptions options) : base(options) 16 | { 17 | } 18 | } 19 | 20 | public class User 21 | { 22 | public int Id { get; set; } 23 | public string Name { get; set; } 24 | } 25 | 26 | public class UserService 27 | { 28 | private readonly DatabaseContext context; 29 | 30 | public UserService(DatabaseContext context) 31 | { 32 | this.context = context; 33 | } 34 | 35 | public void Add(string url) 36 | { 37 | var blog = new User { Name = "Robert"}; 38 | this.context.Users.Add(blog); 39 | this.context.SaveChanges(); 40 | } 41 | 42 | public IEnumerable Find(string term) 43 | { 44 | return this.context.Users 45 | .Where(b => b.Name.Contains(term)) 46 | .OrderBy(b => b.Id) 47 | .ToList(); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /SimpleProxy.Diagnostics/DiagnosticsInterceptor.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleProxy.Diagnostics 2 | { 3 | using System; 4 | using System.Diagnostics; 5 | using Interfaces; 6 | using Microsoft.Extensions.Logging; 7 | using SimpleProxy; 8 | 9 | /// 10 | /// Interceptor for Diagnostics Logging 11 | /// 12 | public class DiagnosticsInterceptor : IMethodInterceptor 13 | { 14 | private readonly Stopwatch diagnosticStopwatch; 15 | 16 | /// 17 | /// The Logger 18 | /// 19 | private ILogger logger; 20 | 21 | /// 22 | /// Initialises a new instance of the 23 | /// 24 | public DiagnosticsInterceptor(ILoggerFactory loggerFactory) 25 | { 26 | this.diagnosticStopwatch = new Stopwatch(); 27 | this.logger = loggerFactory.CreateLogger(); 28 | } 29 | 30 | /// 31 | public void BeforeInvoke(InvocationContext invocationContext) 32 | { 33 | this.diagnosticStopwatch.Start(); 34 | } 35 | 36 | /// 37 | public void AfterInvoke(InvocationContext invocationContext, object methodResult) 38 | { 39 | this.diagnosticStopwatch.Stop(); 40 | this.logger.LogInformation($"Method executed in: {this.diagnosticStopwatch.ElapsedMilliseconds}ms"); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /SimpleProxy.UnitOfWork/UnitOfWorkFactory.cs: -------------------------------------------------------------------------------- 1 | using Arch.EntityFrameworkCore.UnitOfWork; 2 | 3 | 4 | namespace SimpleProxy.UnitOfWork 5 | { 6 | using System; 7 | using Microsoft.EntityFrameworkCore; 8 | using Microsoft.Extensions.DependencyInjection; 9 | 10 | /// 11 | public class UnitOfWorkFactory : IUnitOfWorkFactory 12 | { 13 | /// 14 | /// The Services Builder Instance 15 | /// 16 | private readonly IServiceProvider services; 17 | 18 | /// 19 | /// Initialises a new instance of the 20 | /// 21 | /// Services Collection 22 | public UnitOfWorkFactory(IServiceProvider services) 23 | { 24 | this.services = services; 25 | } 26 | 27 | /// 28 | public IUnitOfWork Create() where TDbContextType : DbContext 29 | { 30 | var options = new DbContextOptionsBuilder() 31 | .UseInMemoryDatabase("Test Database") 32 | .Options; 33 | 34 | var dbContext = ActivatorUtilities.CreateInstance(this.services, options); 35 | if (dbContext == null) 36 | { 37 | throw new ArgumentNullException(nameof(dbContext)); 38 | } 39 | 40 | return new UnitOfWork(dbContext); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /SampleApp/Program.cs: -------------------------------------------------------------------------------- 1 | namespace SampleApp { 2 | using System; 3 | using System.Threading.Tasks; 4 | 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Microsoft.Extensions.Logging; 7 | 8 | using SimpleProxy.Caching; 9 | using SimpleProxy.Diagnostics; 10 | using SimpleProxy.Extensions; 11 | using SimpleProxy.Logging; 12 | using SimpleProxy.Strategies; 13 | 14 | public class Program 15 | { 16 | public static async Task Main(string[] args) 17 | { 18 | // Configure the Service Provider 19 | var services = new ServiceCollection(); 20 | 21 | // Required 22 | services.AddOptions(); 23 | services.AddMemoryCache(); 24 | services.AddLogging(p => p.AddConsole(x => x.IncludeScopes = true).SetMinimumLevel(LogLevel.Trace)); 25 | 26 | services.EnableSimpleProxy(p => p 27 | .AddInterceptor() 28 | .AddInterceptor() 29 | .AddInterceptor() 30 | .WithOrderingStrategy()); 31 | 32 | services.AddTransientWithProxy(); 33 | 34 | // Get a Proxied Class and call a method 35 | var serviceProvider = services.BuildServiceProvider(); 36 | 37 | var testProxy = serviceProvider.GetService(); 38 | 39 | testProxy.TestMethod(); 40 | 41 | await testProxy.TestMethodAsync(); 42 | 43 | testProxy.TestMethodWithExpirationPolicy(); // set the cache 44 | testProxy.TestMethodWithExpirationPolicy(); // execute just using the cache value 45 | System.Threading.Thread.Sleep(25000); // time to expire the registry 46 | testProxy.TestMethodWithExpirationPolicy(); // execute the method again 47 | 48 | Console.WriteLine("====> All Test Methods Complete. Press a key. <===="); 49 | 50 | Console.ReadLine(); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /SimpleProxy.Logging/LogInterceptor.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleProxy.Logging 2 | { 3 | using Interfaces; 4 | using Microsoft.Extensions.Logging; 5 | using SimpleProxy; 6 | 7 | /// 8 | /// Interceptor for Logging 9 | /// 10 | public class LogInterceptor : IMethodInterceptor 11 | { 12 | /// 13 | /// The Logger 14 | /// 15 | private readonly ILoggerFactory loggerFactory; 16 | 17 | /// 18 | /// The Logging Attribute 19 | /// 20 | private LogAttribute loggingAttribute; 21 | 22 | /// 23 | /// The Logger 24 | /// 25 | private ILogger logger; 26 | 27 | /// 28 | /// Initializes a new instance of the class. 29 | /// 30 | /// Logger Factory 31 | public LogInterceptor(ILoggerFactory loggerFactory) 32 | { 33 | this.loggerFactory = loggerFactory; 34 | } 35 | 36 | /// 37 | public void BeforeInvoke(InvocationContext invocationContext) 38 | { 39 | // Create a logger based on the class type that owns the executing method 40 | this.logger = this.loggerFactory.CreateLogger(invocationContext.GetOwningType()); 41 | 42 | // Get the Logging Attribute 43 | this.loggingAttribute = invocationContext.GetAttributeFromMethod(); 44 | 45 | // Get the Logging Level 46 | var loggingLevel = this.loggingAttribute.LoggingLevel; 47 | 48 | // Log the method being executed 49 | this.logger.Log(loggingLevel, $"{invocationContext.GetOwningType()}: Method executing: {invocationContext.GetExecutingMethodName()}"); 50 | } 51 | 52 | /// 53 | public void AfterInvoke(InvocationContext invocationContext, object methodResult) 54 | { 55 | this.logger.Log(this.loggingAttribute.LoggingLevel, $"{invocationContext.GetOwningType()}: Method executed: {invocationContext.GetExecutingMethodName()}"); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /SampleApp/TestClass.cs: -------------------------------------------------------------------------------- 1 | namespace SampleApp 2 | { 3 | using System; 4 | using System.Threading.Tasks; 5 | using Microsoft.Extensions.Logging; 6 | using SimpleProxy.Caching; 7 | using SimpleProxy.Diagnostics; 8 | using SimpleProxy.Logging; 9 | 10 | /// 11 | /// Sample Class used to demonstrate the SimpleProxy Interception 12 | /// 13 | public class TestClass : ITestClass 14 | { 15 | /// 16 | /// The Logger 17 | /// 18 | private readonly ILogger logger; 19 | 20 | /// 21 | /// Initialises a new instance of the 22 | /// 23 | /// Logger Factory 24 | public TestClass(ILoggerFactory loggerFactory) 25 | { 26 | this.logger = loggerFactory.CreateLogger(); 27 | } 28 | 29 | /// 30 | /// Test Method 31 | /// 32 | [Log(LogLevel.Debug)] 33 | [Diagnostics] 34 | [Cache] 35 | public DateTime TestMethod() 36 | { 37 | var dateTime = DateTime.Now; 38 | 39 | this.logger.LogInformation("====> The Real Method is Executed Here! <===="); 40 | 41 | return dateTime; 42 | } 43 | 44 | /// 45 | /// Test Method With Expire Policy 46 | /// 47 | [Log(LogLevel.Debug)] 48 | [Diagnostics] 49 | [Cache(MillisecondsToExpire = 20000)] 50 | public DateTime TestMethodWithExpirationPolicy() 51 | { 52 | var dateTime = DateTime.Now; 53 | 54 | this.logger.LogInformation($"====> The Real Method With Expiration is Executed Here! <===="); 55 | 56 | return dateTime; 57 | } 58 | 59 | [Log(LogLevel.Debug)] 60 | [Diagnostics] 61 | [Cache] 62 | public Task TestMethodAsync() 63 | { 64 | var dateTime = DateTime.Now; 65 | 66 | this.logger.LogInformation("====> The Real Async Method is Executed Here! <===="); 67 | 68 | return Task.FromResult(dateTime); 69 | } 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /SimpleProxy.Caching/CacheInterceptor.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleProxy.Caching 2 | { 3 | using Extensions; 4 | using Interfaces; 5 | using Microsoft.Extensions.Caching.Memory; 6 | using SimpleProxy; 7 | using System; 8 | 9 | /// 10 | /// Interceptor for Caching Method return values 11 | /// 12 | public class CacheInterceptor : IMethodInterceptor 13 | { 14 | /// 15 | /// Memory Cache Instance 16 | /// 17 | private readonly IMemoryCache memoryCache; 18 | 19 | /// 20 | /// Initialises a new instance of the 21 | /// 22 | public CacheInterceptor(IMemoryCache memoryCache) 23 | { 24 | this.memoryCache = memoryCache; 25 | } 26 | 27 | /// 28 | public void BeforeInvoke(InvocationContext invocationContext) 29 | { 30 | // Check the Memory Cache for a cached value for this method 31 | this.memoryCache.TryGetValue(invocationContext.GetExecutingMethodName(), out var result); 32 | 33 | // If a cached value is found; replace the method return value and dont execute 34 | // the real underlying method 35 | if (result != null) 36 | { 37 | invocationContext.OverrideMethodReturnValue(result); 38 | invocationContext.BypassInvocation(); 39 | } 40 | } 41 | 42 | /// 43 | public void AfterInvoke(InvocationContext invocationContext, object methodResult) 44 | { 45 | // Save the result to the MemoryCahe with an expiration time if availble 46 | if (invocationContext.GetAttributeFromMethod() != null 47 | && invocationContext.GetAttributeFromMethod().MillisecondsToExpire > 0) 48 | { 49 | this.memoryCache.Set(invocationContext.GetExecutingMethodName(), methodResult, TimeSpan.FromMilliseconds(invocationContext.GetAttributeFromMethod().MillisecondsToExpire)); 50 | } 51 | else 52 | { 53 | // Save the result to the MemoryCache without any expiration time 54 | this.memoryCache.Set(invocationContext.GetExecutingMethodName(), methodResult); 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /SimpleProxy/Internal/ProxyFactory.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleProxy.Internal 2 | { 3 | using System; 4 | using Castle.DynamicProxy; 5 | using SimpleProxy.Configuration; 6 | 7 | /// 8 | /// Proxy Factory that generates Proxy Classes 9 | /// 10 | /// Type of Object to Proxy 11 | internal class ProxyFactory 12 | { 13 | /// 14 | /// Gets or sets the Proxy Configuration 15 | /// 16 | private readonly SimpleProxyConfiguration proxyConfiguration; 17 | 18 | /// 19 | /// Gets the CastleCore Proxy Generator 20 | /// 21 | private readonly IProxyGenerator proxyGenerator; 22 | 23 | /// 24 | /// Gets the Service Provider 25 | /// 26 | private readonly IServiceProvider serviceProvider; 27 | 28 | /// 29 | /// Initialises a new instance of the MasterProxy that wraps an object 30 | /// 31 | /// Services Collection 32 | /// Proxy Generator Instance 33 | /// Proxy Configuration 34 | public ProxyFactory(IServiceProvider serviceProvider, IProxyGenerator proxyGenerator, SimpleProxyConfiguration config) 35 | { 36 | this.proxyConfiguration = config; 37 | this.proxyGenerator = proxyGenerator; 38 | this.serviceProvider = serviceProvider; 39 | } 40 | 41 | /// 42 | /// Creates a Proxy Object of the object passed in 43 | /// 44 | /// The Object to be Proxied 45 | /// The Proxied Object 46 | public T CreateProxy(T originalObject) 47 | { 48 | // Proxy the Original Object 49 | var masterInterceptor = new CoreInterceptor(this.serviceProvider, this.proxyConfiguration); 50 | var proxy = this.proxyGenerator.CreateInterfaceProxyWithTarget(typeof(T), originalObject, masterInterceptor); 51 | 52 | // Make sure the Proxy was created correctly 53 | if (proxy == null) 54 | { 55 | throw new ArgumentNullException(nameof(proxy)); 56 | } 57 | 58 | // Return the Proxied Object 59 | return (T)proxy; 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /SimpleProxy.UnitOfWork/UnitOfWorkInterceptor.cs: -------------------------------------------------------------------------------- 1 | using Arch.EntityFrameworkCore.UnitOfWork; 2 | 3 | 4 | namespace SimpleProxy.UnitOfWork 5 | { 6 | using System.Linq; 7 | using Extensions; 8 | using Interfaces; 9 | using Microsoft.EntityFrameworkCore; 10 | 11 | /// 12 | /// Unit of Work Interceptor 13 | /// 14 | public class UnitOfWorkInterceptor : IMethodInterceptor 15 | { 16 | /// 17 | /// Unit of Work Factory 18 | /// 19 | private readonly IUnitOfWorkFactory unitOfWorkFactory; 20 | 21 | /// 22 | /// Unit of Work Attribute 23 | /// 24 | private UnitOfWorkAttribute unitOfWorkAttribute; 25 | 26 | /// 27 | /// Unit of Work 28 | /// 29 | private IUnitOfWork unitOfWork; 30 | 31 | /// 32 | /// Initialises a new instance of the 33 | /// 34 | /// Unit of Work Factory 35 | public UnitOfWorkInterceptor(IUnitOfWorkFactory unitOfWorkFactory) 36 | { 37 | this.unitOfWorkFactory = unitOfWorkFactory; 38 | } 39 | 40 | /// 41 | public void BeforeInvoke(InvocationContext invocationContext) 42 | { 43 | this.unitOfWorkAttribute = invocationContext.GetOwningAttribute() as UnitOfWorkAttribute; 44 | var dbContextType = this.unitOfWorkAttribute?.DbContextType; 45 | var uowPropertyPosition = invocationContext.GetParameterPosition(); 46 | var uow = invocationContext.GetParameterValue(uowPropertyPosition); 47 | 48 | // If the Unit of Work is already set - skip the interception 49 | if (uow != null) 50 | { 51 | return; 52 | } 53 | 54 | // Get the Generic Type Definition 55 | var methodInfo = this.unitOfWorkFactory.GetType().GetMethods().FirstOrDefault(p => p.IsGenericMethod && p.Name == nameof(this.unitOfWorkFactory.Create)); 56 | 57 | // Build a method with the DB Context Type 58 | var method = methodInfo?.MakeGenericMethod(dbContextType); 59 | 60 | // Create the new UnitOfWork 61 | this.unitOfWork = (IUnitOfWork)method?.Invoke(this.unitOfWorkFactory, null); 62 | 63 | // Replace the UOW on the method with the new one 64 | invocationContext.SetParameterValue(uowPropertyPosition, this.unitOfWork); 65 | } 66 | 67 | /// 68 | public void AfterInvoke(InvocationContext invocationContext, object methodResult) 69 | { 70 | // Save Changes 71 | if (this.unitOfWorkAttribute.SaveChanges) 72 | { 73 | this.unitOfWork.SaveChanges(); 74 | } 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /SimpleProxy/Configuration/SimpleProxyConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleProxy.Configuration 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics.CodeAnalysis; 6 | using Attributes; 7 | using Interfaces; 8 | using Internal.Configuration; 9 | using Internal.Interfaces; 10 | using Strategies; 11 | 12 | /// 13 | /// Configuration Class for Proxy Generation 14 | /// 15 | [ExcludeFromCodeCoverage] 16 | public sealed class SimpleProxyConfiguration 17 | { 18 | /// 19 | /// Gets or sets a collection of all configured interceptors 20 | /// 21 | internal List ConfiguredInterceptors { get; set; } = new List(); 22 | 23 | /// 24 | /// Gets or sets the Ordering Strategy for Interceptors 25 | /// 26 | internal IOrderingStrategy OrderingStrategy { get; set; } = new PyramidOrderStrategy(); 27 | 28 | /// 29 | /// Gets or sets a value which determines whether invalid interceptors are ignored (rather than throw exceptions) 30 | /// 31 | internal bool IgnoreInvalidInterceptors = true; 32 | 33 | /// 34 | /// Adds an interceptor to the configuration 35 | /// 36 | /// Attribute to trigger interception 37 | /// Interceptor to call when attribute is applied 38 | /// 39 | public SimpleProxyConfiguration AddInterceptor() where TAttribute : MethodInterceptionAttribute where TInterceptor : IMethodInterceptor 40 | { 41 | // Adds an Interceptor Mapping for matching up attributes to interceptors 42 | this.ConfiguredInterceptors.Add(new InterceptorMapping()); 43 | 44 | // Return the ProxyConfiguration for chaining configuration 45 | return this; 46 | } 47 | 48 | /// 49 | /// Prevents exceptions being thrown when interceptors are not configured correctly 50 | /// 51 | /// so that configuration can be chained 52 | public SimpleProxyConfiguration IgnoreInvalidInterceptorConfigurations() 53 | { 54 | // Invalid Interceptor Configurations are ignored and wont throw exceptions 55 | this.IgnoreInvalidInterceptors = true; 56 | 57 | // Return the ProxyConfiguration for chaining configuration 58 | return this; 59 | } 60 | 61 | /// 62 | /// Applies an ordering strategy to the interceptors 63 | /// 64 | /// Strategy (Class) Type 65 | /// so that configuration can be chained 66 | public SimpleProxyConfiguration WithOrderingStrategy() where TStrategy : IOrderingStrategy 67 | { 68 | // Creates a new instance of the Ordering Strategy and assigns it the configuration 69 | this.OrderingStrategy = Activator.CreateInstance(); 70 | 71 | // Return the ProxyConfiguration for chaining configuration 72 | return this; 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /SimpleProxy/Internal/Extensions/InvocationExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleProxy.Internal.Extensions 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Reflection; 7 | using Attributes; 8 | using Castle.DynamicProxy; 9 | using Exceptions; 10 | using Microsoft.Extensions.DependencyInjection; 11 | using SimpleProxy.Configuration; 12 | using SimpleProxy.Interfaces; 13 | 14 | /// 15 | /// Extension methods for 16 | /// 17 | internal static class InvocationExtensions 18 | { 19 | /// 20 | /// Gets a collection of all the interceptors associated with the current executing method 21 | /// 22 | /// Current Invocation 23 | /// Service Provider Instance 24 | /// Proxy Configuration 25 | /// of all configured interceptors for this method 26 | internal static List GetInterceptorMetadataForMethod(IInvocation invocation, IServiceProvider serviceProvider, SimpleProxyConfiguration proxyConfiguration) 27 | { 28 | // Create the Interceptor List to store the configured interceptors 29 | var interceptorList = new List(); 30 | 31 | // Get the Attributes applied to the method being invoked 32 | var methodAttributes = invocation 33 | .MethodInvocationTarget 34 | .GetCustomAttributes() 35 | .Where(p => p.GetType().IsSubclassOf(typeof(MethodInterceptionAttribute))) 36 | .Cast(); 37 | 38 | var index = 0; 39 | foreach (var methodAttribute in methodAttributes) 40 | { 41 | // Get the Interceptor that is bound to the attribute 42 | var interceptorType = proxyConfiguration.ConfiguredInterceptors.FirstOrDefault(p => p.AttributeType == methodAttribute.GetType())?.InterceptorType; 43 | if (interceptorType == null) 44 | { 45 | if (proxyConfiguration.IgnoreInvalidInterceptors) 46 | { 47 | continue; 48 | } 49 | 50 | throw new InvalidInterceptorException($"The Interceptor Attribute '{methodAttribute}' is applied to the method, but there is no configured interceptor to handle it"); 51 | } 52 | 53 | // Use the Service Provider to create the Interceptor instance so you can inject dependencies into the constructor 54 | var instance = (IMethodInterceptor)ActivatorUtilities.CreateInstance(serviceProvider, interceptorType); 55 | 56 | // New InvocationContext Instance 57 | var context = new InvocationContext 58 | { 59 | Attribute = methodAttribute, 60 | Interceptor = instance, 61 | Invocation = invocation, 62 | Order = index, 63 | ServiceProvider = serviceProvider 64 | }; 65 | 66 | interceptorList.Add(context); 67 | index += 1; 68 | } 69 | 70 | // Return the list of configured interceptors 71 | return interceptorList; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SimpleProxy 2 | 3 | ## What is SimpleProxy? 4 | 5 | SimpleProxy solves the problem of Aspect Orientated Programming (AoP) in Net Core. It builds on the current Net Core Dependency Injection functions to extend it, providing Proxy services with a very small learning curve. 6 | 7 | ## What is Aspect Orientated Programming? 8 | 9 | Wikipedia describes AOP as the following: 10 | 11 | "In computing, _aspect-oriented programming (AOP) is a programming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns_. It does so by adding additional behavior to existing code (an advice) without modifying the code itself..." 12 | 13 | Source: https://en.wikipedia.org/wiki/Aspect-oriented_programming 14 | 15 | ## SimpleProxy vs PostSharp 16 | PostSharp is a long established and mature solution for doing what SimpleProxy does. It allows you to add additional functionality to your classes without spoiling them with repetitive boilerplater code. The fundamental difference is that PostSharp injects its code AFTER compilation, changing the the binaries that are output with additional code. SimpleProxy will wrap your code at runtime, so debugging is easy, and the changes more transparent to a developer. In short, SimpleProxy makes things more obvious. 17 | 18 | ## Getting Started 19 | 20 | ##### Option 1 => Source Code 21 | - Download the code from GitHub, either by cloning the repository or downloading the files directly. 22 | - Open the [SimpleProxy.sln] file in Visual Studio 23 | - Build the solution 24 | - Start the [SampleApp] project to see it in action 25 | 26 | ##### Option 2 => Binary 27 | Visit https://www.nuget.org/packages/SimpleProxy/1.0.1 and download the latest binary 28 | 29 | ##### Option 3 => Nuget 30 | Install directly into your project using: ```Install-Package SimpleProxy -Version 1.0.1``` in your Nuget Package Manager 31 | 32 | #### What do I need to run the code? 33 | 34 | - Visual Studio 2017 or later 35 | - Net Core 2.2 SDK installed => https://dotnet.microsoft.com/download/dotnet-core/2.2 (Official Download) 36 | 37 | #### How does it work? 38 | 39 | Creating proxies for objects is **not** straightforward. One of the most common frameworks for doing so is (and has been for a long time) Castle Core. (https://github.com/castleproject/Core). Infact, Castle Core is used as a fundamental building block for this project. Documentation can be difficult to find for Castle Core and its not straightforward to work with on its own. 40 | 41 | SimpleProxy is designed to simplify the whole process. Interception is done in just a few steps: 42 | 43 | - Create a custom attribute that derives from the SimpleProxy base attribute 44 | 45 | ``` 46 | public class MyCustomAttribute : MethodInterceptionAttribute 47 | { 48 | public MyCustomAttribute() 49 | { 50 | } 51 | } 52 | ``` 53 | 54 | - Create an interceptor that implements the SimpleProxy IMethodInterceptor 55 | 56 | ``` 57 | public class MyCustomInterceptor : IMethodInterceptor 58 | { 59 | public void BeforeInvoke(InvocationContext invocationContext) 60 | { 61 | } 62 | 63 | public void AfterInvoke(InvocationContext invocationContext, object methodResult) 64 | { 65 | } 66 | } 67 | ``` 68 | 69 | - Register a mapping for the Attribute & Interceptors in the ServiceCollection 70 | 71 | ``` 72 | // Configure the Service Provider 73 | var services = new ServiceCollection(); 74 | 75 | // Enable SimpleProxy 76 | services.EnableSimpleProxy(p => p.AddInterceptor()); 77 | ``` 78 | 79 | - Add your class to the ServiceCollection using one of the SimpleProxy IServiceCollection overloads 80 | 81 | ``` 82 | services.AddTransientWithProxy(); 83 | ``` 84 | 85 | SimpleProxy uses the default Microsoft.Extensions.DependencyInjection library built into ASP Net Core to register interceptors and then intercept method calls based on the attributes applied to your methods. Methods are intercepted with YOUR own code either before and/or after a method call. 86 | 87 | #### How do I register interceptors with the DI Framework? 88 | 89 | Interceptors are registered in the Microsoft DI framework using the EnableSimpleProxy extension method on IServiceCollection. The ```AddInterceptor()``` method uses the fluent interface so it can be chained for easier configuration. 90 | 91 | ``` 92 | // Configure the Service Provider 93 | var services = new ServiceCollection(); 94 | 95 | // Enable SimpleProxy 96 | services.EnableSimpleProxy(p => p 97 | .AddInterceptor() 98 | .AddInterceptor()); 99 | ``` 100 | 101 | #### What is InvocationContext? 102 | ... 103 | 104 | #### How do I intercept my method calls? 105 | ... 106 | 107 | #### Can I change the method values? 108 | ... 109 | 110 | #### How do I get the return value from the method that was invoked? 111 | ... 112 | 113 | #### Are there any example interceptors to get started with? 114 | ... 115 | 116 | #### How can I extend SimpleProxy? 117 | ... 118 | 119 | #### Is SimpleProxy actively developed? 120 | ... 121 | 122 | 123 | -------------------------------------------------------------------------------- /SimpleProxy.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.489 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleProxy", "SimpleProxy\SimpleProxy.csproj", "{076C09AD-12A5-4CE0-BE41-B461606FED62}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleApp", "SampleApp\SampleApp.csproj", "{F1589C2B-CA8A-4B56-B964-C64C561ADB01}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleProxy.Caching", "SimpleProxy.Caching\SimpleProxy.Caching.csproj", "{3B583E8E-6FB9-40E6-A294-35A26F9A9430}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleProxy.Diagnostics", "SimpleProxy.Diagnostics\SimpleProxy.Diagnostics.csproj", "{D513380A-89DF-4074-B6C9-EA146AE7D8B5}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleProxy.Logging", "SimpleProxy.Logging\SimpleProxy.Logging.csproj", "{65B0C87F-FA32-4EC3-8D67-15D1746924E2}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleProxy.UnitOfWork", "SimpleProxy.UnitOfWork\SimpleProxy.UnitOfWork.csproj", "{8E20AC91-9FBA-4975-90EE-46078318F4C8}" 17 | EndProject 18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleProxy.Tests", "Unit Tests\SimpleProxy.Tests\SimpleProxy.Tests.csproj", "{4463FE87-A9DE-4076-B942-595C0B107924}" 19 | EndProject 20 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Unit Tests", "Unit Tests", "{8E20A6AB-AC37-43E6-89F9-BF21FEB70339}" 21 | EndProject 22 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Interceptors", "Interceptors", "{08F8CC95-6646-4F02-8A2C-C7884CA0DBD0}" 23 | EndProject 24 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{4A4DE7A9-6C40-4F7F-9E49-0090BAA9B1E1}" 25 | EndProject 26 | Global 27 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 28 | Debug|Any CPU = Debug|Any CPU 29 | Release|Any CPU = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 32 | {076C09AD-12A5-4CE0-BE41-B461606FED62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {076C09AD-12A5-4CE0-BE41-B461606FED62}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {076C09AD-12A5-4CE0-BE41-B461606FED62}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {076C09AD-12A5-4CE0-BE41-B461606FED62}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {F1589C2B-CA8A-4B56-B964-C64C561ADB01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {F1589C2B-CA8A-4B56-B964-C64C561ADB01}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {F1589C2B-CA8A-4B56-B964-C64C561ADB01}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {F1589C2B-CA8A-4B56-B964-C64C561ADB01}.Release|Any CPU.Build.0 = Release|Any CPU 40 | {3B583E8E-6FB9-40E6-A294-35A26F9A9430}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 41 | {3B583E8E-6FB9-40E6-A294-35A26F9A9430}.Debug|Any CPU.Build.0 = Debug|Any CPU 42 | {3B583E8E-6FB9-40E6-A294-35A26F9A9430}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {3B583E8E-6FB9-40E6-A294-35A26F9A9430}.Release|Any CPU.Build.0 = Release|Any CPU 44 | {D513380A-89DF-4074-B6C9-EA146AE7D8B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {D513380A-89DF-4074-B6C9-EA146AE7D8B5}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {D513380A-89DF-4074-B6C9-EA146AE7D8B5}.Release|Any CPU.ActiveCfg = Release|Any CPU 47 | {D513380A-89DF-4074-B6C9-EA146AE7D8B5}.Release|Any CPU.Build.0 = Release|Any CPU 48 | {65B0C87F-FA32-4EC3-8D67-15D1746924E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 49 | {65B0C87F-FA32-4EC3-8D67-15D1746924E2}.Debug|Any CPU.Build.0 = Debug|Any CPU 50 | {65B0C87F-FA32-4EC3-8D67-15D1746924E2}.Release|Any CPU.ActiveCfg = Release|Any CPU 51 | {65B0C87F-FA32-4EC3-8D67-15D1746924E2}.Release|Any CPU.Build.0 = Release|Any CPU 52 | {8E20AC91-9FBA-4975-90EE-46078318F4C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 53 | {8E20AC91-9FBA-4975-90EE-46078318F4C8}.Debug|Any CPU.Build.0 = Debug|Any CPU 54 | {8E20AC91-9FBA-4975-90EE-46078318F4C8}.Release|Any CPU.ActiveCfg = Release|Any CPU 55 | {8E20AC91-9FBA-4975-90EE-46078318F4C8}.Release|Any CPU.Build.0 = Release|Any CPU 56 | {4463FE87-A9DE-4076-B942-595C0B107924}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 57 | {4463FE87-A9DE-4076-B942-595C0B107924}.Debug|Any CPU.Build.0 = Debug|Any CPU 58 | {4463FE87-A9DE-4076-B942-595C0B107924}.Release|Any CPU.ActiveCfg = Release|Any CPU 59 | {4463FE87-A9DE-4076-B942-595C0B107924}.Release|Any CPU.Build.0 = Release|Any CPU 60 | EndGlobalSection 61 | GlobalSection(SolutionProperties) = preSolution 62 | HideSolutionNode = FALSE 63 | EndGlobalSection 64 | GlobalSection(NestedProjects) = preSolution 65 | {076C09AD-12A5-4CE0-BE41-B461606FED62} = {4A4DE7A9-6C40-4F7F-9E49-0090BAA9B1E1} 66 | {3B583E8E-6FB9-40E6-A294-35A26F9A9430} = {08F8CC95-6646-4F02-8A2C-C7884CA0DBD0} 67 | {D513380A-89DF-4074-B6C9-EA146AE7D8B5} = {08F8CC95-6646-4F02-8A2C-C7884CA0DBD0} 68 | {65B0C87F-FA32-4EC3-8D67-15D1746924E2} = {08F8CC95-6646-4F02-8A2C-C7884CA0DBD0} 69 | {8E20AC91-9FBA-4975-90EE-46078318F4C8} = {08F8CC95-6646-4F02-8A2C-C7884CA0DBD0} 70 | {4463FE87-A9DE-4076-B942-595C0B107924} = {8E20A6AB-AC37-43E6-89F9-BF21FEB70339} 71 | EndGlobalSection 72 | GlobalSection(ExtensibilityGlobals) = postSolution 73 | SolutionGuid = {8F627740-E1C8-4756-8BA6-39E4135E9350} 74 | EndGlobalSection 75 | EndGlobal 76 | -------------------------------------------------------------------------------- /SimpleProxy/Extensions/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleProxy.Extensions 2 | { 3 | using System; 4 | using Castle.DynamicProxy; 5 | using Configuration; 6 | using Internal; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Microsoft.Extensions.Options; 9 | 10 | /// 11 | /// Extension Methods for the 12 | /// 13 | public static class ServiceCollectionExtensions 14 | { 15 | /// 16 | /// Enables Proxy Generation for Services registered in 17 | /// 18 | /// Services Collection 19 | /// Proxy Configuration Options 20 | /// 21 | public static IServiceCollection EnableSimpleProxy(this IServiceCollection services, Action options) 22 | { 23 | // Check Inputs for Null 24 | if (options == null) 25 | { 26 | throw new ArgumentNullException(nameof(options)); 27 | } 28 | 29 | // Store the Proxy Configuration 30 | services.Configure(options); 31 | 32 | // Proxy Generator needs to be registered as a Singleton for performance reasons 33 | services.AddSingleton(); 34 | 35 | // Return the IServiceCollection for chaining configuration 36 | return services; 37 | } 38 | 39 | /// 40 | /// Adds a Transient Service to the that is wrapped in a Proxy 41 | /// 42 | /// Interface Type 43 | /// Implementation Type 44 | /// Services Collection 45 | /// 46 | public static IServiceCollection AddTransientWithProxy(this IServiceCollection services) where TService : TInterface 47 | { 48 | var serviceProvider = services.BuildServiceProvider(); 49 | var proxyConfiguration = services.GetProxyConfiguration(); 50 | var proxyGenerator = serviceProvider.GetService(); 51 | var proxyInstance = ActivatorUtilities.CreateInstance(serviceProvider); 52 | 53 | // Wrap the service with a Proxy instance and add it with Transient Scope 54 | services.AddTransient(typeof(TInterface), 55 | p => new ProxyFactory(serviceProvider, proxyGenerator, proxyConfiguration) 56 | .CreateProxy(ActivatorUtilities.CreateInstance(serviceProvider))); 57 | 58 | // Return the IServiceCollection for chaining configuration 59 | return services; 60 | } 61 | 62 | /// 63 | /// Adds a Scoped Service to the that is wrapped in a Proxy 64 | /// 65 | /// Interface Type 66 | /// Implementation Type 67 | /// Services Collection 68 | /// 69 | public static IServiceCollection AddScopedWithProxy(this IServiceCollection services) where TService : TInterface 70 | { 71 | var serviceProvider = services.BuildServiceProvider(); 72 | var proxyConfiguration = services.GetProxyConfiguration(); 73 | var proxyGenerator = serviceProvider.GetService(); 74 | var proxyInstance = ActivatorUtilities.CreateInstance(serviceProvider); 75 | 76 | // Wrap the service with a Proxy instance and add it with Scoped Scope 77 | services.AddScoped(typeof(TInterface), p => new ProxyFactory(serviceProvider, proxyGenerator, proxyConfiguration).CreateProxy(proxyInstance)); 78 | 79 | // Return the IServiceCollection for chaining configuration 80 | return services; 81 | } 82 | 83 | /// 84 | /// Adds a Singleton Service to the that is wrapped in a Proxy 85 | /// 86 | /// Interface Type 87 | /// Implementation Type 88 | /// Services Collection 89 | /// 90 | public static IServiceCollection AddSingletonWithProxy(this IServiceCollection services) where TService : TInterface 91 | { 92 | var serviceProvider = services.BuildServiceProvider(); 93 | var proxyConfiguration = services.GetProxyConfiguration(); 94 | var proxyGenerator = serviceProvider.GetService(); 95 | var proxyInstance = ActivatorUtilities.CreateInstance(serviceProvider); 96 | 97 | // Wrap the service with a Proxy instance and add it with Singleton Scope 98 | services.AddSingleton(typeof(TInterface), p => new ProxyFactory(serviceProvider, proxyGenerator, proxyConfiguration).CreateProxy(proxyInstance)); 99 | 100 | // Return the IServiceCollection for chaining configuration 101 | return services; 102 | } 103 | 104 | /// 105 | /// Gets the ProxyConfiguration to pass to the Proxy Factory Method 106 | /// 107 | /// 108 | /// Proxy Configuration 109 | private static SimpleProxyConfiguration GetProxyConfiguration(this IServiceCollection services) 110 | { 111 | return services.BuildServiceProvider().GetRequiredService>().Value; 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /SimpleProxy/Internal/CoreInterceptor.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | namespace SimpleProxy.Internal 4 | { 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | using Castle.DynamicProxy; 10 | using Extensions; 11 | using SimpleProxy.Configuration; 12 | using SimpleProxy.Interfaces; 13 | 14 | /// 15 | /// The Master Interceptor Class wraps a proxied object and handles all of its interceptions 16 | /// 17 | internal class CoreInterceptor : IAsyncInterceptor 18 | { 19 | /// 20 | /// Gets the 21 | /// 22 | private readonly SimpleProxyConfiguration proxyConfiguration; 23 | 24 | /// 25 | /// Gets the 26 | /// 27 | private readonly IServiceProvider serviceProvider; 28 | 29 | /// 30 | /// Initialises a new instance of the class 31 | /// 32 | /// Service Provider 33 | /// Proxy Configuration 34 | public CoreInterceptor(IServiceProvider serviceProvider, SimpleProxyConfiguration proxyConfiguration) 35 | { 36 | this.serviceProvider = serviceProvider; 37 | this.proxyConfiguration = proxyConfiguration; 38 | } 39 | 40 | public void InterceptSynchronous(IInvocation invocation) 41 | { 42 | var proceedWithInterception = InterceptBeforeProceed(invocation, out var invocationMetadataCollection, out var orderingStrategy); 43 | 44 | if (!proceedWithInterception) { 45 | invocation.Proceed(); 46 | return; 47 | } 48 | 49 | // Execute the Real Method 50 | if (!invocationMetadataCollection.Any(p => p.InvocationIsBypassed)) { 51 | invocation.Proceed(); 52 | } 53 | 54 | InterceptAfterProceed(invocationMetadataCollection, orderingStrategy); 55 | } 56 | 57 | public void InterceptAsynchronous(IInvocation invocation) 58 | { 59 | invocation.ReturnValue = InternalInterceptAsynchronousAsync(invocation); 60 | } 61 | 62 | private async Task InternalInterceptAsynchronousAsync(IInvocation invocation) 63 | { 64 | var proceedWithInterception = InterceptBeforeProceed(invocation, out var invocationMetadataCollection, out var orderingStrategy); 65 | 66 | if (!proceedWithInterception) { 67 | invocation.Proceed(); 68 | var task = (Task)invocation.ReturnValue; 69 | await task; 70 | return; 71 | } 72 | 73 | // Execute the Real Method 74 | if (!invocationMetadataCollection.Any(p => p.InvocationIsBypassed)) { 75 | invocation.Proceed(); 76 | var task = (Task)invocation.ReturnValue; 77 | await task; 78 | } 79 | 80 | InterceptAfterProceed(invocationMetadataCollection, orderingStrategy); 81 | } 82 | 83 | public void InterceptAsynchronous(IInvocation invocation) 84 | { 85 | invocation.ReturnValue = InternalInterceptAsynchronousAsync(invocation); 86 | } 87 | 88 | private async Task InternalInterceptAsynchronousAsync(IInvocation invocation) 89 | { 90 | var proceedWithInterception = InterceptBeforeProceed(invocation, out var invocationMetadataCollection, out var orderingStrategy); 91 | 92 | TResult result; 93 | 94 | if (!proceedWithInterception) { 95 | invocation.Proceed(); 96 | var task = (Task)invocation.ReturnValue; 97 | result = await task; 98 | return result; 99 | } 100 | 101 | // Execute the Real Method 102 | if (!invocationMetadataCollection.Any(p => p.InvocationIsBypassed)) { 103 | invocation.Proceed(); 104 | var task = (Task)invocation.ReturnValue; 105 | result = await task; 106 | } 107 | else { 108 | result = default; 109 | } 110 | 111 | InterceptAfterProceed(invocationMetadataCollection, orderingStrategy); 112 | 113 | return result; 114 | } 115 | 116 | private bool InterceptBeforeProceed(IInvocation invocation, out List invocationMetadataCollection, out IOrderingStrategy orderingStrategy) 117 | { 118 | // Map the configured interceptors to this type based on its attributes 119 | invocationMetadataCollection = InvocationExtensions.GetInterceptorMetadataForMethod(invocation, this.serviceProvider, this.proxyConfiguration); 120 | 121 | // If there are no configured interceptors, leave now 122 | if (invocationMetadataCollection == null || !invocationMetadataCollection.Any()) { 123 | orderingStrategy = null; 124 | return false; 125 | } 126 | 127 | // Get the Ordering Strategy for Interceptors 128 | orderingStrategy = this.proxyConfiguration.OrderingStrategy; 129 | 130 | // Process the BEFORE Interceptions 131 | foreach (var invocationContext in orderingStrategy.OrderBeforeInterception(invocationMetadataCollection)) { 132 | invocationContext.Interceptor.BeforeInvoke(invocationContext); 133 | } 134 | 135 | return true; 136 | } 137 | 138 | private static void InterceptAfterProceed(List invocationMetadataCollection, IOrderingStrategy orderingStrategy) 139 | { 140 | // Process the AFTER Interceptions 141 | foreach (var invocationContext in orderingStrategy.OrderAfterInterception(invocationMetadataCollection)) { 142 | invocationContext.Interceptor.AfterInvoke(invocationContext, invocationContext.GetMethodReturnValue()); 143 | } 144 | } 145 | 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | -------------------------------------------------------------------------------- /SimpleProxy/InvocationContext.cs: -------------------------------------------------------------------------------- 1 | namespace SimpleProxy 2 | { 3 | using System; 4 | using System.Collections.Concurrent; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Reflection; 8 | using Attributes; 9 | using Castle.DynamicProxy; 10 | using Interfaces; 11 | 12 | /// 13 | /// Metadata Class that describes the Current Invocation 14 | /// 15 | public class InvocationContext : IHideBaseTypes 16 | { 17 | #region Internal Properties 18 | 19 | /// 20 | /// Gets or sets the Invocation Context 21 | /// 22 | internal IInvocation Invocation { get; set; } 23 | 24 | /// 25 | /// Gets or sets the Attribute that triggered the interceptor 26 | /// 27 | internal MethodInterceptionAttribute Attribute { get; set; } 28 | 29 | /// 30 | /// Gets or sets the Interceptor that is triggered for this method 31 | /// 32 | internal IMethodInterceptor Interceptor { get; set; } 33 | 34 | /// 35 | /// Stores data which can be passed between interceptors 36 | /// 37 | internal ConcurrentDictionary TempData { get; set; } 38 | 39 | /// 40 | /// Gets or sets the 41 | /// 42 | internal IServiceProvider ServiceProvider { get; set; } 43 | 44 | /// 45 | /// Gets or sets the Order Priority of this invocation 46 | /// 47 | internal int Order { get; set; } 48 | 49 | /// 50 | /// Gets or sets a value that indicates whether invocation of the underlying method is bypassed 51 | /// 52 | internal bool InvocationIsBypassed { get; set; } 53 | 54 | #endregion 55 | 56 | /// 57 | /// Inititalises a new instance of the 58 | /// 59 | public InvocationContext() 60 | { 61 | this.TempData = new ConcurrentDictionary(); 62 | } 63 | 64 | /// 65 | /// Gets the attribute that initiated the interception 66 | /// 67 | /// 68 | public MethodInterceptionAttribute GetOwningAttribute() 69 | { 70 | return this.Attribute; 71 | } 72 | 73 | /// 74 | /// Gets the type that owns the executing method 75 | /// 76 | /// 77 | public Type GetOwningType() 78 | { 79 | return this.Invocation.Method.DeclaringType; 80 | } 81 | 82 | /// 83 | /// Gets the Service Provider for resolving dependencies 84 | /// 85 | /// 86 | public IServiceProvider GetServiceProvider() 87 | { 88 | return this.ServiceProvider; 89 | } 90 | 91 | /// 92 | /// Gets the position of the specified type in the methods parameter list 93 | /// 94 | /// Type of value to get 95 | /// Parameter Position 96 | /// Returns the value of the Parameter at the given position as {T} 97 | public T GetParameterValue(int parameterPosition) 98 | { 99 | return (T)this.Invocation.GetArgumentValue(parameterPosition); 100 | } 101 | 102 | /// 103 | /// Gets the position of the specified type in the methods parameter list 104 | /// 105 | /// Parameter Position 106 | /// Returns the value of the Parameter at the given position 107 | public object GetParameterValue(int parameterPosition) 108 | { 109 | return this.Invocation.GetArgumentValue(parameterPosition); 110 | } 111 | 112 | /// 113 | /// Sets the value of the parameter at the specified location 114 | /// 115 | /// Parameter Position 116 | /// New Value 117 | public void SetParameterValue(int parameterPosition, object newValue) 118 | { 119 | this.Invocation.SetArgumentValue(parameterPosition, newValue); 120 | } 121 | 122 | /// 123 | /// Adds temporary data to the context 124 | /// 125 | /// Name to identify the data 126 | /// Data Value 127 | public void SetTemporaryData(string name, object value) 128 | { 129 | this.TempData.TryAdd(name, value); 130 | } 131 | 132 | /// 133 | /// Gets temporary data from the context 134 | /// 135 | /// Name to identify the data 136 | /// 137 | public object GetTemporaryData(string name) 138 | { 139 | return this.TempData.GetValueOrDefault(name); 140 | } 141 | 142 | /// 143 | /// Gets the return value from the method that was called 144 | /// 145 | /// Method Return Value 146 | public object GetMethodReturnValue() 147 | { 148 | return this.Invocation.ReturnValue; 149 | } 150 | 151 | /// 152 | /// Overrides the return value for the method being called. Usually called with [BypassInvocation] to shortcut interception 153 | /// 154 | /// Method Return Value 155 | public void OverrideMethodReturnValue(object returnValue) 156 | { 157 | this.Invocation.ReturnValue = returnValue; 158 | } 159 | 160 | /// 161 | /// Sets the Invocation of the underlying method to be bypassed 162 | /// 163 | public void BypassInvocation() 164 | { 165 | this.InvocationIsBypassed = true; 166 | } 167 | 168 | /// 169 | /// Gets the Executing Method Info 170 | /// 171 | /// Returns the position of the type in the method parameters. Returns -1 if not found 172 | public MethodInfo GetExecutingMethodInfo() 173 | { 174 | return this.Invocation.Method; 175 | } 176 | 177 | /// 178 | /// Gets the Executing Method Name 179 | /// 180 | /// The Name of the executing method 181 | public string GetExecutingMethodName() 182 | { 183 | return this.Invocation.Method.Name; 184 | } 185 | 186 | /// 187 | /// Method to try and identify the position of the specified type in the methods parameter list 188 | /// 189 | /// Type To Find 190 | /// Returns the position of the type in the method parameters. Returns -1 if not found 191 | public int GetParameterPosition(Type typeToFind) 192 | { 193 | var method = this.Invocation.Method; 194 | if (method == null) 195 | { 196 | throw new ArgumentNullException(nameof(method)); 197 | } 198 | 199 | for (var i = method.GetParameters().Length - 1; i >= 0; i--) 200 | { 201 | var paramType = method.GetParameters()[i].ParameterType; 202 | if (paramType != typeToFind) 203 | { 204 | continue; 205 | } 206 | 207 | return i; 208 | } 209 | 210 | return -1; 211 | } 212 | 213 | /// 214 | /// Gets the position of the specified type in the methods parameter list 215 | /// 216 | /// Type to find 217 | /// Returns the position of the type in the method parameters. Returns -1 if not found 218 | public int GetParameterPosition() 219 | { 220 | return this.GetParameterPosition(typeof(TTypeToFind)); 221 | } 222 | 223 | /// 224 | /// Gets the specified attribute from the executing method 225 | /// 226 | public TAttribute GetAttributeFromMethod() where TAttribute : Attribute 227 | { 228 | return this.Invocation.MethodInvocationTarget.GetCustomAttributes().OfType().FirstOrDefault(); 229 | } 230 | } 231 | } 232 | --------------------------------------------------------------------------------