Castle

The Castle is. Net system under a common open source project. Designed to simplify enterprise application development. Let’s introduce Castle DynamicProxy.

Aspect oriented programming (AOP) is a programming idea. The design pattern’s dynamic proxy pattern is one of the ways AOP implements it. With Castle you can quickly implement section-oriented programming.

Application Scenarios:

  1. With proxies, we can do the desired behavior before and after the client execution. Such as log, do cache.
  2. Separate control logic from business code. For example, we are requesting a third-party API. In order to ensure high availability of the service, we expect to introduce some policies, such as retry, circuit breaker, degrade, etc. Polly, Caslte enables us to implement policies in a faceted manner, separating business logic from control logic.
  3. Mock frameworks that help us model the behavior of objects. If you are used to writing unit tests, you should have used Moq, which is also developed based on castle components. It quickly helps us Mock back values and solves the problem of environment dependence.

Let’s take a look at an example of how Caslte DynamicProxy can quickly help us achieve DynamicProxy.

Global log capture

public class Main { public static void Create() { var proxyGenerator = new ProxyGenerator(); IMachine target = new Machine(); var machine = proxyGenerator.CreateInterfaceProxyWithTarget(target, new IInterceptor[]{new LogInterceptor()}); machine.Buy(100); } } public class LogInterceptor: IInterceptor {public void Intercept(IInvocation) {// Log before Object [] invocationArguments = invocation.Arguments; invocation.Proceed(); After Object invocationReturnValue = Invocation.ReturnValue; }}Copy the code

Control logic is separated from business code

The logic of the control layer is realized with Polly + Castle facets.

public class FeiShuRestClientPolicyInterceptor : IInterceptor { private static readonly LogWrapper Logger = new LogWrapper(); private readonly PolicyWrap<IRestResponse> _policyWrap; public FeiShuRestClientPolicyInterceptor() { //http:400, Code: 99991663 Token failure _policyWrap = Policy. Wrap (RequestExceptionRetryPolicy (), LimitedRetryPolicy (1, 1)); } / / / < summary > / / / frequency limit retry strategy / / / < summary > / / / < param name = "sleep" > < param > / / / < param name = "retryTimes" > < param > / / / <returns></returns> public static RetryPolicy<IRestResponse> LimitedRetryPolicy(int sleep, int retryTimes) { var retryPolicy = Policy<IRestResponse>.HandleResult(restResponse => (int)restResponse.StatusCode == 429) .WaitAndRetry(retryTimes, i => TimeSpan.FromSeconds(sleep)); return retryPolicy; } /// <summary> // network transient Retry /// </summary> // <returns></returns> public static RetryPolicy<IRestResponse> RequestExceptionRetryPolicy () {var retryPolicy = Policy < IRestResponse >. HandleResult (restResponse = > {/ / / / HTTP network was detected ReSharper disable once ConvertIfStatementToReturnStatement if (InvalidStatus(restResponse)) { return true; } return false; }) .WaitAndRetry(3, i => TimeSpan.FromSeconds(1)); return retryPolicy; } /// <summary> // Abnormal network status condition /// </summary> // <param name=" Response "></param> // <returns></returns> public static bool InvalidStatus(IRestResponse response) { return response.ResponseStatus == ResponseStatus.TimedOut || response.StatusCode >= HttpStatusCode.InternalServerError; } public void Intercept(IInvocation invocation) { invocation.BeforeLog(); _policyWrap.Execute(() => { invocation.Proceed(); return invocation.ReturnValue as IRestResponse; }); invocation.AfterLog(); }}Copy the code

Interface Mock

Mock data: Demo relies on IMachin. If we need to test DoSomething’s logic, we need to Mock an IMachine object’s Buy method.

public interface IMachine { bool Buy(double price); void Purchase(int count); } public class Demo { private readonly IMachine _machine; public Demo(IMachine machine) { _machine = machine; } public void DoSomething() {if (_machine.Buy(1)) {else {// Buy failed}}}Copy the code

The Mock return value

private static void MockDemo(ProxyGenerator proxyGenerator) { IMachine mockMachine = proxyGenerator.CreateInterfaceProxyWithoutTarget<IMachine>(new MockInterceptor(true)); //true Console.WriteLine(mockMachine.Buy(1)); } public class MockInterceptor : IInterceptor { private readonly bool _resultValue; public MockInterceptor(bool resultValue) { _resultValue = resultValue; } public void Intercept(IInvocation) {// Set invocation.ReturnValue = _resultValue; }}Copy the code

Tips

Use of a single ProxyGenerator’s instance: If you have a long running process (web site, windows service, etc.) and you have to create many dynamic proxies, you should make sure to reuse the same ProxyGenerator instance. If not, be aware that you will then bypass the caching mechanism. Side effects are high CPU usage and constant increase in memory consumption.

When creating proxy objects with ProxyGenerator, there is a caching mechanism, so we should use a singleton ProxyGenerator object. Otherwise, CPU usage is high and memory consumption increases.