See a lot of the garden introduction caching articles, most of them only introduce the basic mechanism, for the cache update and dependency part, is simply to implement the Icacheitemrefreshaction interface, which in the actual project is far from enough. In the actual project, the following 3 points should be considered at least:
- External data: Data that is taken from other systems through external services. We can't control it, and we don't know when it's going to be updated. For this part of the data, we use a timed update policy, the default 1 hours Update 1 times, configurable.
- Internal data: The data generated by the system itself can be fully controlled. For this part of the data, we implement the Icacheitemrefreshaction interface, once invalid, immediately re-fetch.
- How the internal data triggers the update: Which actions trigger which cache invalidation, such as add/delete/update[user][s]->get[user][s] failure, should be configurable.
then proceed separately.
0. Preface
The following discussion is divided into 2 chunks, one of which is the construction of the environment, including using unity to provide service instances + interception with unity. After this piece, you can realize caching in the way of AOP, this part of the garden is a lot of articles, I briefly pass a bit. The second is the implementation of caching, respectively, from the external data cache + Internal cache + Internal trigger Update 3 part to discuss.
1, the construction of the environment
1.1. Using Unity to provide service examples
This part is actually using unity to achieve iinstanceprovider, and then with Iendpointbehavior and the like, to achieve the integration of WCF and unity. The core code is as follows:
1 Public class unityinstanceprovider:iinstanceprovider{2 Public Object getinstance (InstanceContext context, Message msg) {3 return UnityWrapper.Instance.Resolve (servicetype); 4 }5 }
See Artech for a follow-up tour of WCF (7): Integration with the Enterprise Library Unity container through the WCF extension implementation.
1.2. Provide interception (ie piab,policyinjection) with unity
We're going to have to spit it out here first. EntLib, backwards compatibility is too poor, completely does not conform to the usual style of Microsoft. Each big version on a bunch of breaking changes and deprecated, really a bit depressed. Of course, they also have difficulties, such as EntLib6 in the caching, is included in the NET40, indeed can only give up. All right, back to the chase. Fortunately, EntLib's own Help documentation is very detailed , the problem should be prioritized to manual to see the relevant content.
EntLib4.1 's Piab module, in EntLib5, was completely based on Unity's interception, which was simply canceled in EntLib6. Here we use the configuration in the ENTLIB5 to implement the policy injection, the core configuration is as follows:
1 <Unity>2 <sectionextensiontype= "Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration "/>3 <Assemblyname= "Service"/>4 <namespacename= "Service"/>5 <aliasalias= "Cachingcallhandler"type= "Service.Unity.CachingCallHandler, Service"/>6 <Containername= "UnityContainer">7 <extensiontype= "interception" />8 <interception>9 <Policyname= "Caching">Ten <Matchingrulename= "Allservicematch"type= "Namespacematchingrule"> One <Constructor> A <paramname= "NamespaceName"value= "Service"/> - </Constructor> - </Matchingrule> the <Callhandlername= "Cachingservice"type= "Cachingcallhandler"/> - </Policy> - </interception> - <Registertype= "IService1"> + <Interceptortype= "Transparentproxyinterceptor" /> - <policyinjection/> + </Register> A </Container> at </Unity>
Here is a detail, for the convenience of configuration, 1 is available in EntLib with the entlibconfig.exe,2 is in the VS menu->xml-> architecture, Add unityconfiguration20.xsd or EnterpriseLibrary.Configuration.xsd, so there's a smart tip. If you cannot find an. xsd file in the EntLib installation directory, you can use everything to search for it.
2, the realization of caching
2.1. External Data cache
External data is our uncontrolled data and when updates are uncertain. We take a time-updated strategy, the frequency of the update depends mainly on the frequency of external data changes, such as our project depends on the external data base, change less, so only 1 hours or even half a day to update.
Originally, I thought absolutetime was what I wanted. (Note: Slidingtime is the first time a user hits the cache and then slips for a period of time before it expires), but it is only known by Ilspy to view the internal code. EntLib does not automatically trigger the Icacheitemrefreshaction interface when the user accesses the cache, and then determines whether the time expires, if it expires, and then triggers the refreshaction. The code is as follows:
1 namespacemicrosoft.practices.enterpriselibrary.caching{2 Public classcache{3 Public ObjectGetData (stringkey) {4 if(cacheitem.hasexpired ()) {5 This. Inmemorycache.remove (key);6Refreshactioninvoker.invokerefreshaction (CacheItem, cacheitemremovedreason.expired, This. Instrumentationprovider);7 This. instrumentationprovider.firecacheexpired (1L);8}}}}
This kind of user experience is certainly a little bit worse, not to allow users to enjoy the full benefits of caching, we use a Timer to automatically update the cache, note that you should be using System.Threading.Timer instead of System.Timers.Timer. The code is as follows://We use custom Timercacheattribute to identify such methods
1 varAtts = input. Methodbase.getcustomattributes (typeof(Timercacheattribute),false);2 if(Atts. Length >0){3Refreshtimerutil.instance[key] =NewTimer (E = {4 Object[] args = E as Object[];5 if(args = =NULL|| Args. Length! =2)return;6 7 varTmpinput = args[0] asimethodinvocation;8 varTmpnext = args[1] asgetnexthandlerdelegate;9 varTmpreturn =Tmpnext (Tmpinput, tmpnext);Ten if(Tmpreturn.exception = =NULL) { One CacheWrapper.Instance.Add (key, tmpreturn.returnvalue); A } - }, - New Object[] {input, getNext}, the TimeSpan.Zero, - NewTimeSpan (1,0,0)); -}
2.2. Internal data cache
Internal data can be directly inserted into the cache, and then quietly waiting for additions and deletions to change the operation to set it to expire.
1 CacheWrapper.Instance.Add (Key, Methodreturn.returnvalue,2 cacheitempriority.normal, 3 new cacherefreshaction {input = input, GetNext = GetNext});
Of course, to implement the Icacheitemrefreshaction interface, the code is as follows:
1 [Serializable]2 Public classcacherefreshaction:icacheitemrefreshaction {3 PublicImethodinvocation Input {Get;Set;}4 PublicGetnexthandlerdelegate GetNext {Get;Set;}5 Public voidRefresh (stringRemovedkey,ObjectExpiredvalue, CacheItemRemovedReason Removalreason) {6 if(Input = =NULL|| GetNext = =NULL)return;7 varMethodreturn =GetNext () (Input, GetNext);8 if(Methodreturn.exception = =NULL) {9 CacheWrapper.Instance.Add (Removedkey, Methodreturn.returnvalue,Ten Cacheitempriority.normal, One Newcacherefreshaction {input = input, GetNext =GetNext}); A } - } -}
It is said that MemoryCache does not need to implement ISerializable, while the other caching either implement ISerializable or hit [Serializable] tags. However, this way of implementation, Imethodinvocation and getnexthandlerdelegate visual serialization are slightly troublesome, fortunately, we only use the MemoryCache, you can first regardless of, and then break through.
2.3. Internal data Trigger Update
Finally, there is a trigger update, how to achieve similar add/delete/update[user][s]->get[user][s] effect? The general idea is: 1) determine whether the method name Startwith keyword (add/delete/update), 2) intercept the target keyword user, and generate the corresponding complex form users;3) and get and other actions stitching into cachekey, All hit CacheKey are set to expire (Cachemanager.remove) to trigger the corresponding refreshaction. The code is as follows:
1 varentity =string. Empty;2 foreach(varActinchconfigutil.action) {3 if(Methodname.startswith (Act)) {4entity =methodname.substring (Act. Length);5 Break;6 }}7 8 varentities =pluralutil.pluralize (entity);9 if(entities = = entity) entities =pluralutil.singularize (entity);Ten foreach(varPrefixinchconfigutil.prefix) { OneCacheWrapper.Instance.RemoveStartWith (prefix +entity); ACacheWrapper.Instance.RemoveStartWith (prefix +entities); -}
Here the generation of the complex code is borrowed from EntityFramework, please see Englishpluralizationservice.
If there is an incorrect place, please criticize it!
Using EntLib5.0 (unity+interception+caching) to implement the Caching mechanism available in the project