Closed murseld closed 4 years ago
Hi,
There is Argument(Source.Triggers)
that you can use to get all the attributes that lead your aspect's advice to be triggered.
[Injection(typeof(CacheAspect))]
class Cache: Attribute
{
public Cache(string data)
{
Data = data;
}
public string Data { get; }
}
[Aspect(Scope.Global)]
class CacheAspect
{
[Advice(Kind.Before)]
public void LogCall([Argument(Source.Triggers)] Attribute[] triggers)
{
// Note you can have as many triggers as you want. You can get here all of them.
// Despite the number of triggers the aspect will be executed only once.
var trigger = triggers.OfType<Cache>().FirstOrDefault();
if (trigger!=null) // If aspect is triggered with cache attribute.
Console.WriteLine($"Trigger's data is: {trigger.Data}");
}
}
class Program
{
[Cache("My parameter")]
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
thank you pamidur but i cant work it. because i need executing just one time like RuntimeInitialize method on Postsharp. Because i have to get instance from CacheManager on this method. I am sharing you postsharp code please review it because i want to left postsharp it is not working on linux os.
thank you..
The thing is that AspectInjector unlike Postsharp doesn't mess with your code in runtime. That is why "method initialization" is done on compilation. Another difference is that attribute in AspectInjector in only a trigger. The life of the attribute instance end on Advice method end. This means that you cannot store ref to cache manager in an attribute. But you can store it aspect itself.
Now aspect can be Global(Singleton) or PerInstance(aspect instance per class instance). The latter mean per target class, and not per trigger(attribute). This feature I'll add in the future, thanks for the idea :)
This means that some things should be done a bit different in AspectInjector.
Like this:
using AspectInjector.Broker;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Reflection;
using System.Runtime.Caching;
using System.Text.RegularExpressions;
namespace AspectInjectorTest
{
public class Program
{
public static void Main()
{
var p = new Program();
Console.WriteLine("1+2:");
var r1 = p.TestCalculation(1, 2);
Console.WriteLine();
Console.WriteLine("1+2:");
var r2 = p.TestCalculation(1, 2);
Console.WriteLine();
Console.WriteLine("2+2:");
var r3 = p.TestCalculation(2, 2);
Console.ReadLine();
}
[Cache(typeof(MemoryCacheManager), 1)]
public int TestCalculation(int a, int b)
{
Console.WriteLine("Real calculation.");
return a + b;
}
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
[Injection(typeof(CacheAspect))]
public class Cache : Attribute
{
public Cache(Type cacheType, int cacheByMinute)
{
//add null checks?
CacheType = cacheType;
CacheByMinute = cacheByMinute;
}
public Type CacheType { get; }
public int CacheByMinute { get; }
}
[Aspect(Scope.Global)]
public class CacheAspect : Attribute
{
private readonly ConcurrentDictionary<Type, ICacheManager> _cacheManagers
= new ConcurrentDictionary<Type, ICacheManager>();
[Advice(Kind.Around, Targets = Target.Method)]
public object OnInvoke(
[Argument(Source.Metadata)] MethodBase metadata,
[Argument(Source.Arguments)] object[] arguments,
[Argument(Source.Instance)] object target,
[Argument(Source.Target)] Func<object[], object> method,
[Argument(Source.Triggers)] Attribute[] triggers
)
{
var cacheTriggers = triggers.OfType<Cache>().ToArray();
if (cacheTriggers.Length != 0)
{
var methodName = string.Format("{0}.{1}.{2}",
metadata.ReflectedType.Namespace,
metadata.ReflectedType.Name,
metadata.Name);
// Warning! not every type gives accurate ToString() results, you might want to serialize the args or even better take their hashcode
var key = string.Format("{0}({1})", methodName,
arguments.Select(x => x != null ? x.GetHashCode() : 0).Sum());
// method might be cached in different caches (memory, file, redis) with different settings at the same time
foreach (var trigger in cacheTriggers)
{
var cache = GetCacheManager(trigger.CacheType);
if (cache.IsAdd(key))
{
Console.WriteLine($"Result from cache {trigger.CacheType.Name}");
return cache.Get<object>(key);
}
}
//if no cache has a result
Console.WriteLine("No results found in cache");
var result = method(arguments);
//add result to every cache
foreach (var trigger in cacheTriggers)
{
GetCacheManager(trigger.CacheType).Add(key, result, trigger.CacheByMinute);
Console.WriteLine($"Storred in {trigger.CacheType.Name} for {trigger.CacheByMinute} min");
}
return result;
}
//this executes in unlikely case when this aspect is triggered not by Cache attribute, but something else
return method(arguments);
}
public ICacheManager GetCacheManager(Type cacheType)
{
if (typeof(ICacheManager).IsAssignableFrom(cacheType) == false)
throw new Exception("Wrong Cache Manager");
return _cacheManagers.GetOrAdd(cacheType, t => (ICacheManager)Activator.CreateInstance(t));
}
}
//no changes below that line
public interface ICacheManager
{
T Get<T>(string key);
void Add(string key, object data, int cacheTime);
bool IsAdd(string key);
void Remove(string key);
void RemoveByPattern(string pattern);
void Clear();
}
public class MemoryCacheManager : ICacheManager
{
protected ObjectCache Cache => System.Runtime.Caching.MemoryCache.Default;
public T Get<T>(string key)
{
return (T)Cache[key];
}
public void Add(string key, object data, int cacheTime = 60)
{
if (data == null)
{
return;
}
var policy = new CacheItemPolicy { AbsoluteExpiration = DateTime.Now + TimeSpan.FromMinutes(cacheTime) };
Cache.Add(new CacheItem(key, data), policy);
}
public bool IsAdd(string key)
{
return Cache.Contains(key);
}
public void Remove(string key)
{
Cache.Remove(key);
}
public void RemoveByPattern(string pattern)
{
var regex = new Regex(pattern, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase);
var keysToRemove = Cache.Where(d => regex.IsMatch(d.Key)).Select(d => d.Key).ToList();
foreach (var key in keysToRemove)
{
Remove(key);
}
}
public void Clear()
{
foreach (var item in Cache)
{
Remove(item.Key);
}
}
}
}
Hi @pamidur thank you very much its working. this will be excelent then postsharp because you are the best :) thanks for interesting...
I was glad to help, thanks for using AspectInjector!
How can i get values from aspect constructor when i use it ?
[CacheAspect(typeof(MemoryCacheManager), 30)]
public Student Save(Student model)
{ return model; }
Cache.txtfor example i want to use Microsoft Memory Cache and cacheMinutes equal 30 minutes and i want to set this settings on aspect.