aspect-oriented programming (aop) in .net
TRANSCRIPT
Yuriy GutsSoftware Architect @ ELEKS
Clients
Application Services
Infrastructure Services
Desktop Browser
Desktop Application
Native Mobile App. . .
API Business Components Reporting. . .ORM
RDBMS Cache SearchIndex. . .Filesystem
Clients
Application Services
Infrastructure Services
Desktop Browser
Desktop Application
Native Mobile App. . .
API Business Components Search. . .ORM
RDBMS Cache SearchIndex. . .Filesystem
Cross-Cutting Concerns
Identity & Access Mgmt.
Audit
Logging
. . .
Import & Export
public class ReputationService : IReputationService...private readonly IUserDataService _userDataService;
public ReputationService(IUserDataService userDataService){
_userDataService = userDataService;}
public int AddReputationForQuestion(Question question){
int pointsToAdd = question.UpVotes * 5 - question.DownVotes * 2;int newReputation = _userDataService.AddReputationForUser(question.Author, pointsToAdd);return newReputation;
}
public int AddReputationForAnswer(Answer answer){
int pointsToAdd = answer.UpVotes * 10 - answer.DownVotes * 3;int newReputation = _userDataService.AddReputationForUser(answer.Author, pointsToAdd);return newReputation;
}...
public int AddReputationForQuestion(Question question){
int pointsToAdd = question.UpVotes * 5 - question.DownVotes * 2;int newReputation = _userDataService.AddReputationForUser(question.Author, pointsToAdd);return newReputation;
}
public int AddReputationForQuestion(Question question){
Console.WriteLine("{0:HH:mm:ss.fff}: AddReputationForQuestion started.", DateTime.Now);
int pointsToAdd = question.UpVotes * 5 - question.DownVotes * 2;int newReputation = _userDataService.AddReputationForUser(question.Author, pointsToAdd);
Console.WriteLine("{0:HH:mm:ss.fff}: AddReputationForQuestion completed.", DateTime.Now);return newReputation;
}
public int AddReputationForQuestion(Question question){
Console.WriteLine("{0:HH:mm:ss.fff}: AddReputationForQuestion started.", DateTime.Now);
if (question == null){
throw new ArgumentNullException("question");}if (question.Author == null){
throw new ArgumentNullException("question", "Author cannot be null.");}if (question.UpVotes < 0 || question.DownVotes < 0){
throw new ArgumentException("A question cannot have a negative number of votes");}
int pointsToAdd = question.UpVotes * 5 - question.DownVotes * 2;int newReputation = _userDataService.AddReputationForUser(question.Author, pointsToAdd);
Console.WriteLine("{0:HH:mm:ss.fff}: AddReputationForQuestion completed.", DateTime.Now);return newReputation;
}
public int AddReputationForQuestion(Question question){
Console.WriteLine("{0:HH:mm:ss.fff}: AddReputationForQuestion started.", DateTime.Now);
if (question == null) throw new ArgumentNullException("question");if (question.Author == null) throw new ArgumentNullException("question", "Author cannot be null.");if (question.UpVotes < 0 || question.DownVotes < 0) throw new ArgumentException("A question cannot have a negative number of votes");
var attemptsLeft = 3;try{
while (attemptsLeft > 0){
try{
int pointsToAdd = question.UpVotes * 5 - question.DownVotes * 2;int newReputation = _userDataService.AddReputationForUser(question.Author, pointsToAdd);
Console.WriteLine("{0:HH:mm:ss.fff}: AddReputationForQuestion completed.", DateTime.Now);return newReputation;
}catch{
attemptsLeft--;if (attemptsLeft == 0){
throw;}
}}
}catch (Exception ex){
Console.WriteLine("{0:HH:mm:ss.fff}: AddReputationForQuestion failed with exception: {1}.", DateTime.Now, ex);}throw new Exception();
}
public int AddReputationForQuestion(Question question){
QuestionValidator.ValidateQuestion(question);return BoundaryLogging<int>.Run("AddReputationForQuestion", () =>{
return MultipleAttemptExecutor<int>.Run(3, () =>{
int pointsToAdd = question.UpVotes * 5 - question.DownVotes * 2;int newReputation = _userDataService.AddReputationForUser(question.Author, pointsToAdd);return newReputation;
});});
}
[DomainValidation][BoundaryLogging][MultipleAttemptExecution(3)]public int AddReputationForQuestion(Question question){
int pointsToAdd = question.UpVotes * 5 - question.DownVotes * 2;int newReputation = _userDataService.AddReputationForUser(question.Author, pointsToAdd);return newReputation;
}
Language Compiler
AOP Post-Compiler
Source Code
IL Code
IL Code withgenerated aspects
IoC ContainerCalling Code
Dynamic Proxy
Original class
1
2
34
5
return this;
return F(this);
• PostSharp (IL weaving)
• Castle.DynamicProxy (call interception)
(just to name a few)
[Serializable]public class BoundaryLogging : OnMethodBoundaryAspect{
public override void OnEntry(MethodExecutionArgs args){
Console.WriteLine("{0:HH:mm:ss.fff}: {1} started.", DateTime.Now, args.Method.Name);}
public override void OnExit(MethodExecutionArgs args){
Console.WriteLine("{0:HH:mm:ss.fff}: {1} completed.", DateTime.Now, args.Method.Name);}
public override void OnException(MethodExecutionArgs args){
Console.WriteLine("{0:HH:mm:ss.fff}: {1} failed with exception: {2}.",DateTime.Now, args.Method.Name, args.Exception);
}}
[Serializable]public class MultipleAttemptExecution : MethodInterceptionAspect{
private readonly int _maxAttempts;public MultipleAttemptExecution(int maxAttempts){
_maxAttempts = maxAttempts;}
public override void OnInvoke(MethodInterceptionArgs args){
var attemptsLeft = _maxAttempts;while (attemptsLeft > 0){
try{
args.Proceed();return;
}catch{
attemptsLeft--;if (attemptsLeft == 0) throw;
}}throw new Exception("Maximum attempts reached.");
}}
[DomainValidation][BoundaryLogging][MultipleAttemptExecution(3)]public int AddReputationForQuestion(Question question){
int pointsToAdd = question.UpVotes * 5 - question.DownVotes * 2;int newReputation = _userDataService.AddReputationForUser(question.Author, pointsToAdd);return newReputation;
}
• [Authorize] attribute in ASP.NET MVC
• HttpModules in ASP.NET lifecycle
• Anything else?
• Logging & Audit
• Transactions
• Security
• Caching
• Exception Management
• Generating and validating code
1. Introduces “magic”, can reduce understanding of the big picture (*).
2. Requires special tools or code decorations.
3. Even mature frameworks have bugs.
(*) Greg Young:www.infoq.com/presentations/8-lines-code-refactoring
yuriy.guts @ eleks.com
https://github.com/YuriyGuts/refactoring-with-aop