AOP的基本定义及作用
“AOP(Aspect-Oriented Programming)是一种将函数的辅助性功能与业务逻辑相分离的编程泛型(programming paradigm),其目的是将横切关注点(cross-cutting concerns)分离出来,使得程序具有更高的模块化特性。AOP是面向方面软件开发(Aspect-Oriented Software Development)在编码实现层面上的具体表现(面向方面软件开发AOSD是一个囊括面向方面分析、面向方面设计和面向方面编程等一系列概念的完整工程系统——笔者注)。AOP包括编程模型和具体用于实现AOP的框架两部分。”
下面上代码
演示了Fody如何拦截 属性值改变、方法调用、方法执行时间。
1.调用逻辑
Services.FodyTest.SampleA sample = new Services.FodyTest.SampleA
{
A = "A",
AA = "AA",
B = 1,
BB = 2,
C = 1.123m,
CC = 1.223m,
D = true,
DD = false
};
sample.PropertyChanged += (obj, args) =>
{
_logger.LogInformation("sample.PropertyChanged:{0}", args.PropertyName);
};
sample.A += "_";
sample.B += 10;
sample.C += 10.999m;
sample.D = false;
sample.MethodA();
sample.MethodTime("TTTTime);
Services.FodyTest.SampleB sampleB = new Services.FodyTest.SampleB
{
A = "B",
AA = "BB",
B = 2,
BB = 22,
C = 2.123m,
CC = 2.223m,
D = true,
DD = false
};
sampleB.PropertyChanged += (obj, args) =>
{
_logger.LogInformation("sample.PropertyChanged:{0}", args.PropertyName);
};
sampleB.A += "_";
sampleB.B += 20;
sampleB.C += 20.999m;
sampleB.D = false;
sampleB.MethodB();
sampleB.MethodTimeB("BBBTime");
2.代码实现
// Attribute should be "registered" by adding as module or assembly custom attribute
[module: FodyMethodInspect]
//静态类重写读取哪个程序集下的(如:PropertyChangedNotificationInterceptor 分别在Services与Services.FodyTest中,将读取Services中的)
[assembly: PropertyChanged.FilterType("NetCoreTemp.WebApi.Services")]
namespace NetCoreTemp.WebApi.Services.FodyTest
{
//https://github.com/Fody/MethodDecorator
// Any attribute which provides OnEntry/OnExit/OnException with proper args
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Assembly | AttributeTargets.Module)]
public class FodyMethodInspectAttribute : Attribute, IMethodDecorator
{
AppDbContext _appDbContext;
UserService _userService;
ILogger _logger;
object _instance;
MethodBase _method;
object[] _args;
public string Modual { get; set; }
// instance, method and args can be captured here and stored in attribute instance fields
// for future usage in OnEntry/OnExit/OnException
public void Init(object instance, MethodBase method, object[] args)
{
_instance = instance;
_method = method;
_args = args;
_userService = FodyGetService.GetIService<UserService>();
_appDbContext = _userService?.GetDBContext();
_logger = FodyGetService.GetILogger<FodyMethodInspectAttribute>();
_logger.LogInformation("Microsoft.Extension.ILogger-Init: {0} [{1}]", method.DeclaringType.FullName + "." + method.Name, args.Length);
LogTo.Info(string.Format("Init: {0} [{1}]", method.DeclaringType.FullName + "." + method.Name, args.Length));
}
public void OnEntry()
{
var num = _userService.Query(x => x.Status > Models.EnumType.EnumRepo.UseStatusEnum.Draft).Count();
_logger.LogInformation($"Microsoft.Extension.ILogger-Init: OnEntry-User:Count-{num}");
LogTo.Info("OnEntry");
}
public void OnExit()
{
_logger.LogInformation("Microsoft.Extension.ILogger-Init: OnExit");
LogTo.Info("OnExit");
}
public void OnException(Exception exception)
{
_logger.LogError(exception, "Microsoft.Extension.ILogger-OnException: {0}: {1}", exception.GetType(), exception.InnerException?.Message ?? exception.Message);
LogTo.ErrorException($"OnException: {exception.GetType()}: {exception.InnerException?.Message ?? exception.Message}", exception);
}
}
//手动实现改变值前后委托
public delegate void PropertyValueChanedEventHandler(object sender, PropertyChangedEventArgs args, object oldVal,object newVal);
/// <summary>
/// https://github.com/Fody/PropertyChanged/wiki/Attributes#donotnotifyattribute
/// <remark>
/// AddINotifyPropertyChangedInterface 会自动添加 且无需添加 继承INotifyPropertyChanged接口
/// public event PropertyChangedEventHandler PropertyChanged 事件
/// [assembly: PropertyChanged.FilterType("My.Specific.OptIn.Namespace.")] 会自动把命名空间下 所有类加上PropertyChanged通知
/// </remark>
/// </summary>
public class SampleA : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
//public event PropertyValueChanedEventHandler PropertyValueChaned;
public string A { get; set; }
public string AA { get; set; }
public string A_AA
{
get
{
return $"{(A??"").Replace("-", "__")}-{AA}";
}
set
{
if (!string.IsNullOrEmpty(value))
{
var arr = value.Split("-").ToList();
A = arr[0].Replace("__", "-");
AA = arr?.Count > 1 ? string.Join("", arr.Skip(1)) : "";
}
}
}
public int B { get; set; }
private void OnBChanged(int oldValue, int newValue)
{
LogTo.Info("B Changed:" + oldValue + " => " + newValue);
//PropertyValueChaned?.Invoke(this, new PropertyChangedEventArgs("B"), oldValue, newValue);
}
[OnChangedMethod(nameof(OnChangedC))]
public decimal C { get; set; }
private void OnChangedC(object oldValue, object newValue)
{
LogTo.Info("C Changed:" + oldValue + " => " + newValue);
//PropertyValueChaned?.Invoke(this, new PropertyChangedEventArgs("C"), oldValue, newValue);
}
[DoNotNotify]
public bool D { get; set; }
public int? BB { get; set; }
public decimal? CC { get; set; }
public bool? DD { get; set; }
[FodyMethodInspect]
public void MethodA()
{
LogTo.Info(JsonSerializer.Serialize(this));
}
[Time("MethodTime-Time Field Arg:{TimeField}")]
public void MethodTime(string TimeField)
{
LogTo.Info($"MethodTime:{TimeField}");
}
}
/// <summary>
/// 自动加PropertityChangeEvent
/// </summary>
[AddINotifyPropertyChangedInterface]
public class SampleB
{
public string A { get; set; }
public string AA { get; set; }
public string A_AA
{
get
{
return $"{(A ?? "").Replace("-", "__")}-{AA}";
}
set
{
if (!string.IsNullOrEmpty(value))
{
var arr = value.Split("-").ToList();
A = arr[0].Replace("__", "-");
AA = arr?.Count > 1 ? string.Join("", arr.Skip(1)) : "";
}
}
}
public int B { get; set; }
private void OnBChanged(int oldValue, int newValue)
{
LogTo.Info("B Changed:" + oldValue + " => " + newValue);
//PropertyValueChaned?.Invoke(this, new PropertyChangedEventArgs("B"), oldValue, newValue);
}
[OnChangedMethod(nameof(OnChangedC))]
public decimal C { get; set; }
private void OnChangedC(object oldValue, object newValue)
{
LogTo.Info("C Changed:" + oldValue + " => " + newValue);
//PropertyValueChaned?.Invoke(this, new PropertyChangedEventArgs("C"), oldValue, newValue);
}
[DoNotNotify]
public bool D { get; set; }
public int? BB { get; set; }
public decimal? CC { get; set; }
public bool? DD { get; set; }
[FodyMethodInspect]
public void MethodB()
{
LogTo.Info(JsonSerializer.Serialize(this));
}
[Time("MethodTimeB-Time Field Arg:{TimeField}")]
public void MethodTimeB(string TimeField)
{
LogTo.Info($"MethodTime:{TimeField}");
}
}
/// <summary>
/// 方法事件计算日志实例
/// </summary>
public static class MethodTimeLogger
{
public static void Log(MethodBase methodBase, TimeSpan elapsed, string message)
{
LogTo.Info("MethodTimeLogger:{0}-{1}-{2}", methodBase.Name, elapsed.TotalSeconds, message);
}
}
/// <summary>
/// 属性改变通知实例
/// </summary>
public static class PropertyChangedNotificationInterceptor
{
public static void Intercept(object target, Action onPropertyChangedAction,
string propertyName, object before, object after)
{
onPropertyChangedAction();
LogTo.Info("PropertyChangedNotificationInterceptor:{0}-{1}-{2}", propertyName, before, after);
}
}
/// <summary>
/// DI-serviceProvider 静态储存
/// </summary>
public static class FodyGetService
{
private static IServiceProvider _serviceProvider;
public static void Configration(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
/// <summary>
/// 获取BaseService
/// </summary>
/// <typeparam name="TEntity">Entity类</typeparam>
/// <returns></returns>
public static Base.BaseService<TEntity> GetBaseService<TEntity>() where TEntity : class, NetCoreTemp.WebApi.Models.BaseModel.IEntity_
{
return _serviceProvider.GetService<Base.BaseService<TEntity>>();
}
/// <summary>
/// 获取Entity服务
/// </summary>
/// <typeparam name="TService"></typeparam>
/// <returns></returns>
public static TService GetIService<TService>() where TService : class, Base.IEntityService
{
return _serviceProvider.GetService<TService>();
}
/// <summary>
/// 获取日志服务
/// </summary>
/// <typeparam name="TService"></typeparam>
/// <returns></returns>
public static ILogger<T> GetILogger<T>() where T : class, new()
{
return _serviceProvider.GetService<ILogger<T>>();
}
}
}
namespace NetCoreTemp.WebApi.Services
{
/// <summary>
/// 属性改变通知实例
/// </summary>
public static class PropertyChangedNotificationInterceptor
{
public static void Intercept(object target, Action onPropertyChangedAction,
string propertyName, object before, object after)
{
onPropertyChangedAction();
LogTo.Info("PropertyChangedNotificationInterceptor:{0}-{1}-{2}", propertyName, before, after);
}
}
}