As the business gets more complex , Recently, I decided to cache some interfaces that are frequently queried but the data will not change much , This function is generally used AOP You can do it , After looking for a client, there is no ready-made one that can be used directly , A kind of , You can only develop it yourself .

Agent mode and AOP

After understanding the proxy pattern , Yes AOP It's natural to get caught , So let's start with some pre knowledge .

The proxy pattern is an example code that uses one class to control method calls from another class .

The proxy pattern has three roles :

  • ISubject Interface , Responsibility is to define behavior .
  • ISubject Implementation class of RealSubject, Responsibility is to achieve behavior .
  • ISubject Proxy class ProxySubject, Responsibility is to control RealSubject The interview of .

There are three implementations of the proxy pattern :

  • General agent .
  • Compulsory agency , Coercion means not having direct access to RealSubject Methods , Must be accessed through a proxy class .
  • A dynamic proxy , Dynamic means to generate proxy classes through reflection ,AOP It is generally based on dynamic proxy .

AOP There are four key knowledge points :

  • The breakthrough point JoinPoint. Namely RealSubject The method of controlled access in .
  • notice Advice, Is the method in the proxy class , Can control or enhance RealSubject Methods , With advance notice 、 The rear notice 、 Surround notifications, etc
  • Weaving Weave, Is to call the notification and in order RealSubject Method process .
  • section Aspect, Multiple entry points will form a facet .
public interface ISubject
{
void DoSomething(string value); Task DoSomethingAsync(string value);
} public class RealSubject : ISubject
{
public void DoSomething(string value)
{
Debug.WriteLine(value);
} public async Task DoSomethingAsync(string value)
{
await Task.Delay(2000);
Debug.WriteLine(value);
}
} public class Proxy : ISubject
{
private readonly ISubject _realSubject; public Proxy()
{
_realSubject = new RealSubject();
} /// <summary>
/// That's the entry point
/// </summary>
/// <param name="value"></param>
public void DoSomething(string value)
{
// This process is weaving in
Before();
_realSubject.DoSomething(value);
After();
} public Task DoSomethingAsync(string value)
{
throw new NotImplementedException();
} public void Before()
{
Debug.WriteLine(" General proxy class pre notification ");
} public void After()
{
Debug.WriteLine(" General proxy class post notification ");
}
}

I'm using Castle.Core This library is used to implement dynamic proxy . But the asynchronous method with return value of this proxy is hard to write , however github There are already many libraries that encapsulate the implementation process , Here I use Castle.Core.AsyncInterceptor To implement an asynchronous method proxy .

public class CastleInterceptor : StandardInterceptor
{
protected override void PostProceed(IInvocation invocation)
{
Debug.WriteLine("Castle Proxy class pre notification "); } protected override void PreProceed(IInvocation invocation)
{
Debug.WriteLine("Castle Proxy class post notification ");
}
} public class AsyncCastleInterceptor : AsyncInterceptorBase
{
protected override async Task InterceptAsync(IInvocation invocation, IInvocationProceedInfo proceedInfo, Func<IInvocation, IInvocationProceedInfo, Task> proceed)
{
Before();
await proceed(invocation, proceedInfo);
After();
} protected override async Task<TResult> InterceptAsync<TResult>(IInvocation invocation, IInvocationProceedInfo proceedInfo, Func<IInvocation, IInvocationProceedInfo, Task<TResult>> proceed)
{
Before();
var result = await proceed(invocation, proceedInfo);
After();
return result;
} public void Before()
{
Debug.WriteLine(" asynchronous Castle Proxy class pre notification ");
} public void After()
{
Debug.WriteLine(" asynchronous Castle Proxy class post notification ");
}
}

Implement facet class and interface caching

Implementation process :

  1. Definition CacheAttribute Feature to mark the methods that need to be cached .
  2. Definition CacheInterceptor section , Implement the logic of caching data in memory .
  3. Using facets , Generate dynamic proxy classes for interfaces , And inject the proxy class into IOC In the container .
  4. Interface by IOC Get the interface implementation class to access the implementation .

The client uses Prism Of IOC To achieve inversion of control ,Prism Support for multiple IOC, I'm going to use DryIoc, Because the others IOC No more updates .

Client memory cache usage Microsoft.Extensions.Caching.Memory, This is the most commonly used .

  • Definition CacheAttribute Feature to mark the methods that need to be cached .
[AttributeUsage(AttributeTargets.Method)]
public class CacheAttribute : Attribute
{
public string? CacheKey { get; }
public long Expiration { get; } public CacheAttribute(string? cacheKey = null, long expiration = 0)
{
CacheKey = cacheKey;
Expiration = expiration;
} public override string ToString() => $"{{ CacheKey: {CacheKey ?? "null"}, Expiration: {Expiration} }}";
}
  • Definition CacheInterceptor Section class , Implement the logic of caching data in memory
public class CacheInterceptor : AsyncInterceptorBase
{
private readonly IMemoryCache _memoryCache; public CacheInterceptor(IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
} ...
// Intercept asynchronous methods
protected override async Task<TResult> InterceptAsync<TResult>(IInvocation invocation, IInvocationProceedInfo proceedInfo, Func<IInvocation, IInvocationProceedInfo, Task<TResult>> proceed)
{
var attribute = invocation.Method.GetCustomAttribute<CacheAttribute>();
if (attribute == null)
{
return await proceed(invocation, proceedInfo).ConfigureAwait(false);
} var cacheKey = attribute.CacheKey ?? GenerateKey(invocation);
if (_memoryCache.TryGetValue(cacheKey, out TResult cacheValue))
{
if (cacheValue is string[] array)
{
Debug.WriteLine($"[Cache] Key: {cacheKey}, Value: {string.Join(',', array)}");
} return cacheValue;
}
else
{
cacheValue = await proceed(invocation, proceedInfo).ConfigureAwait(false);
_memoryCache.Set(cacheKey, cacheValue);
return cacheValue;
}
}
// Generate cached Key
private string GenerateKey(IInvocation invocation)
{
...
}
// Format it
private string FormatArgumentString(ParameterInfo argument, object value)
{
...
}
}
  • Define extension classes to generate facets , And realize chain programming , You can easily add multiple facet classes to an interface .
public static class DryIocInterceptionAsyncExtension
{
private static readonly DefaultProxyBuilder _proxyBuilder = new DefaultProxyBuilder();
// Generate section
public static void Intercept<TService, TInterceptor>(this IRegistrator registrator, object serviceKey = null)
where TInterceptor : class, IInterceptor
{
var serviceType = typeof(TService); Type proxyType;
if (serviceType.IsInterface())
proxyType = _proxyBuilder.CreateInterfaceProxyTypeWithTargetInterface(
serviceType, ArrayTools.Empty<Type>(), ProxyGenerationOptions.Default);
else if (serviceType.IsClass())
proxyType = _proxyBuilder.CreateClassProxyTypeWithTarget(
serviceType, ArrayTools.Empty<Type>(), ProxyGenerationOptions.Default);
else
throw new ArgumentException(
$"{serviceType} Can't be intercepted , Only interfaces or classes can be intercepted "); registrator.Register(serviceType, proxyType,
made: Made.Of(pt => pt.PublicConstructors().FindFirst(ctor => ctor.GetParameters().Length != 0),
Parameters.Of.Type<IInterceptor[]>(typeof(TInterceptor[]))),
setup: Setup.DecoratorOf(useDecorateeReuse: true, decorateeServiceKey: serviceKey));
}
// Chain programming , Easy to add multiple slices
public static IContainerRegistry InterceptAsync<TService, TInterceptor>(
this IContainerRegistry containerRegistry, object serviceKey = null)
where TInterceptor : class, IAsyncInterceptor
{
var container = containerRegistry.GetContainer();
container.Intercept<TService, AsyncInterceptor<TInterceptor>>(serviceKey);
return containerRegistry;
}
}
  • Define the target interface , And mark the method
public interface ITestService
{
/// <summary>
/// An interface for querying large amounts of data
/// </summary>
/// <returns></returns>
[Cache]
Task<string[]> GetLargeData();
} public class TestService : ITestService
{
public async Task<string[]> GetLargeData()
{
await Task.Delay(2000);
var result = new[]{" Big "," The amount "," Count "," According to the "};
Debug.WriteLine(" Query data from the interface ");
return result;
}
}
  • towards IOC The container injects facet classes and business interfaces .
public partial class App
{
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
// Inject cache class
containerRegistry.RegisterSingleton<IMemoryCache>(_ => new MemoryCache(new MemoryCacheOptions()));
// Injection section class
containerRegistry.Register<AsyncInterceptor<CacheInterceptor>>();
// Inject interface and application facet classes
containerRegistry.RegisterSingleton<ITestService, TestService>()
.InterceptAsync<ITestService, CacheInterceptor>();
containerRegistry.RegisterSingleton<ITestService2, TestService2>()
.InterceptAsync<ITestService2, CacheInterceptor>();
}
...
}

effect

// AopView.xaml
<Button x:Name="cache" Content="Aop Cache interface data " /> // AopView.xaml.cs
cache.Click += (sender, args) => ContainerLocator.Container.Resolve<ITestService>().GetLargeData(); // Output
// Click Print for the first time
// Query data from the interface // Then click Print
// [Cache] Key: PrismAop.Service.TestService2.GetLargeData(), Value: Big , The amount , Count , According to the

Last

In fact, there are many details that can be improved , For example, cache refresh rules , The server refreshes the client cache, and so on , But the client AOP The implementation of is almost like this .

I think it's helpful for you to make a recommendation or leave a message !

Source code https://github.com/yijidao/blog/tree/master/WPF/PrismAop

stay WPF Client implementation AOP And interface caching

  1. springboot 2.x Integrate redis,spring aop Implement interface caching

    pox.xml: <dependency> <groupId>org.springframework.boot</groupId> <artifactId&g ...

  2. Use AOP Realization Redis Cache annotations , Support SPEL

    The company's project is very important to the company Redis Use more , Because I didn't do it before AOP, So cache logic and business logic are intertwined , It is difficult to maintain, so we have recently implemented the Redis Of @Cacheable, Store the cached objects in categories redis Of Ha ...

  3. stay ASP.NET Core Use in AOP To simplify caching operations

    Preface About the use of caching , I believe we are all familiar with each other, and we can't be familiar with each other any more , In short, the following sentence . Take data from the cache first , If you can't get it from the cache, go to the database to get it , Take it and throw it into the cache . Then we'll see code like this in the project . pu ...

  4. .NetCore Interface cache

    1. problem : We must use the cache function when we are doing development , The general writing method is to read the cache in the required business code . Judge whether it exists . If it does not exist, read the database and set the cache . But if we have a lot of businesses that use caching , We just need ...

  5. ssm+redis How to use custom annotations more succinctly +AOP Realization redis cache

    be based on ssm + maven + redis Use custom annotations utilize aop be based on AspectJ The way Realization redis cache How to use it more succinctly aop Realization redis cache , Don't talk much , On demo demand : Data query ...

  6. wpf client 【JDAgent Desktop assistant 】 Development details ( Four ) popup The control of win8.0 Of bug

    Directory area : Amateur development wpf The client is finally finished .. Sun screen shot wpf client [JDAgent Desktop assistant ] Development details - The opening wpf client [JDAgent Desktop assistant ] Detailed explanation ( One ) main window Round menu ... wpf customer ...

  7. SpringCloud Use Feign Call other client interfaces with parameters , The incoming parameter is null Or report a mistake status 405 reading IndexService#del(Integer);

    SpringCloud Use Feign Call other client interfaces with parameters , The incoming parameter is null Or report a mistake status 405 reading IndexService#del(Integer); The first method : If you ...

  8. Springboot Study 06-Spring AOP Encapsulation interface custom verification

    Springboot Study 06-Spring AOP Encapsulation interface custom verification keyword BindingResult.Spring AOP. Custom annotation . Custom exception handling .ConstraintValidator Preface ...

  9. wpf client 【JDAgent Desktop assistant 】 Development details ( 3、 ... and ) Waterfall effect and UI Virtualization optimizes big data display

    Directory area : Amateur development wpf The client is finally finished .. Sun screen shot wpf client [JDAgent Desktop assistant ] Development details - The opening wpf client [JDAgent Desktop assistant ] Detailed explanation ( One ) main window Round menu ... wpf customer ...

  10. wpf client 【JDAgent Desktop assistant 】 The amateur development is finally finished .. Sun screen shot

    Directory area : Amateur development wpf The client is finally finished .. Sun screen shot wpf client [JDAgent Desktop assistant ] Development details - The opening wpf client [JDAgent Desktop assistant ] Detailed explanation ( One ) main window Round menu ... wpf customer ...

Random recommendation

  1. python And c Call each other

    although python Development efficiency is very high , But as a scripting language , Its performance is not high , So in order to give consideration to both development efficiency and performance , Modules with high performance requirements are usually used c or c++ To implement or in c or c++ Run in python Scripts to handle logic , The former is usually python in ...

  2. opencart introduce TWIG template engine

     1. First of all, will twig Put in bags system\library Catalog . 2. stay system/startup.php Add the introduction statement at the end of the file . require_once(DIR_SYSTEM . 'lib ...

  3. Access Version control of project files

    Make a brief record of the use MS Access SVN( hereinafter referred to as AccessSVN) Step by step . AccessSVN stay http://accesssvn.codeplex.com/, The purpose of this product is :Access SV ...

  4. I want a pc Client shared with mobile phone , Just like Baidu cloud (iBarn Network disk is easy to use )

    https://github.com/zhimengzhe/iBarn iBarn Net disk is a network disk based on PHP Developed advanced cloud storage system , Provide network backup of files , Synchronization and sharing services : Support breakpoint renewal , Second transmission and other functions : You can select file ...

  5. BZOJ_4530_[Bjoi2014] Great fusion _LCT

    BZOJ_4530_[Bjoi2014] Great fusion _LCT Description Xiaoqiang will be in N Set up a communication system on an isolated planet . This communication system is the connection N A tree with a dot . The edges of the tree are added one by one . In a certain ...

  6. UVA11419 SAM I AM

    UVA11419 SAM I AM Given a \(R\times C\) In the matrix of \(N\) A little bit , Find the minimum number of rows or columns to be selected so that each given point is covered by a row or column , Output plan \(R,\ C\leq1 ...

  7. HDU 6345( Substring query violence )

    Each group is given a string , Output the number of substrings with the smallest dictionary order of the interval to be queried within a limited number of queries . The substring with the smallest dictionary order , Is the single character with the smallest dictionary order in the query interval , The problem is to find the minimum number of characters in the dictionary order in a section . Start blind ...

  8. understand HTTPS agreement

    The original English text :Understanding HTTPS Protocol Recently, we have seen many sites using HTTPS The agreement provides Web services . Usually we see it on some sites containing confidential information, such as banks HTTPS agreement . Such as ...

  9. CSS- Pull down the navigation bar

    Web There are many drop-down navigation bars in websites , Some are through CSS Realization , Some go through JavaScript Plug-in implementation , Actually CSS It's easy to implement , Let's start with a simple version of the drop-down menu : Html The code passes ul List implementation : <ul ...

  10. I don't Big ice 2017 new book pdf As a free download

    Good intentions can dispel violence , Good will makes good fortune , Good intentions can bring karma to life , Kindness can turn around . Kindness can help people capture and build a unique sense of happiness . “ I don't ” It's kind and calm , It's also a kind of good luck , It's a kind introspection . < I don't >—— Millions of best sellers ...