Log In
Or create an account ->
Imperial Library
Home
About
News
Upload
Forum
Help
Login/SignUp
Index
AOP in .NET: Practical Aspect-Oriented Programming
Matthew D. Groves
Copyright
Dedication
Brief Table of Contents
Table of Contents
Foreword
Preface
Acknowledgments
About this Book
Roadmap
Who should read this book?
Code conventions and downloads
Author Online
About the author
About the cover illustration
Part 1. Getting started with AOP
Chapter 1. Introducing AOP
1.1. What is AOP?
1.1.1. Features
AOP’s purpose: Cross-cutting concerns
An aspect’s job: The advice
An aspect’s map: A pointcut
Figure 1.1. A low-level flowchart of a program that uses a single service
Figure 1.2. The same low-level flowchart with possible join points identified
Listing 1.1. A simple program that calls service methods in sequence
Figure 1.3. Flowchart representation—imagine exit join points after each step
How AOP works: Weaving
Figure 1.4. Tangling and scattering. In the printed volume, X represents red code and Y, the green code.
Figure 1.5. Splitting up the classes and recombining them with weaving
1.1.2. Benefits
Clean up spaghetti code
Reduce repetition
Listing 1.2. Example of refactoring using DI instead of AOP
Listing 1.3. Use of the decorator pattern in pseudocode
Listing 1.4. Using AOP instead of DI for cross-cutting concerns
Encapsulation
Listing 1.5. Pseudocode example of an extremely simple AddressBookService
Listing 1.6. Pseudocode example with argument checking split out using AOP
Listing 1.7. Encapsulated and reusable code
1.1.3. AOP in your daily life
Figure 1.6. The HttpModule lifecycle in relation to the request->ASP.NET page->response
Figure 1.7. The ASP.NET MVC ActionFilter lifecycle
1.2. Hello, World
Figure 1.8. Starting NuGet with the UI
Figure 1.9. Search for PostSharp and install with NuGet UI
Figure 1.10. Console output of “Hello, world!”
Figure 1.11. Output with MyAspect applied
1.3. Summary
Chapter 2. Acme Car Rental
2.1. Start a new project
Figure 2.1. Three-layer system architecture
2.1.1. Business requirements
Figure 2.2. Loyalty program rules
2.1.2. Necessary nonfunctional requirements
2.2. Life without AOP
Figure 2.3. Start new project
2.2.1. Write the business logic
Listing 2.1. An implementation of the accrual service
2.2.2. Testing the business logic
Listing 2.2. A simple Console application to test the business logic
Figure 2.4. Console output simulating writes to the database
2.2.3. Add logging
Listing 2.3. Accrue code, now with logging
Listing 2.4. Redeem code, now with logging
2.2.4. Introducing defensive programming
Listing 2.5. Accrue with defensive programming
Listing 2.6. Redeem with defensive programming
2.2.5. Working with transactions and retries
Listing 2.7. Accrue with a transaction
Listing 2.8. Redeem with a transaction
Listing 2.9. Accrue with transaction and retries
Listing 2.10. Redeem with transaction and retries
2.2.6. Handling exceptions
Listing 2.11. Accrue with exception handling
Listing 2.12. Redeem with exception handling
2.2.7. Refactor without AOP
Listing 2.13. Redemption service refactored with DI
Listing 2.14. Accrual service refactored with DI
Listing 2.15. Combined exception handler and transaction handler
The Facade Pattern
Listing 2.16. An aggregate service for orchestrating two services
2.3. The cost of change
2.3.1. Requirements will change
2.3.2. Small versus large projects
2.3.3. Signature changes
Listing 2.17. Accrue method’s defensive programming and logging
2.3.4. Working on a team
2.4. Refactor with AOP
2.4.1. Start simple and isolate the logging
Listing 2.18. A method boundary aspect to handle logging
Listing 2.19. Applying the logging aspect to the Accrue and Redeem methods
Listing 2.20. Examining and logging argument value
Listing 2.21. An alternative way to log certain entities
2.4.2. Refactor defensive programming
Listing 2.22. A defensive programming aspect
Listing 2.23. Refactoring to aspects with an attribute
2.4.3. Creating an aspect for transactions and retries
Listing 2.24. A transaction aspect
Listing 2.25. Continuing to refactor with aspects
Figure 2.5. Console output with the use of AOP
2.4.4. Put exception handling into its own class
Listing 2.26. An aspect to handle exceptions
Listing 2.27. All cross-cutting concerns refactored into aspects
2.5. Summary
Part 2. The Fundamentals of AOP
Chapter 3. Call this instead: intercepting methods
3.1. Method interception
Figure 3.1. Normal flow: calling a method and returning control
Figure 3.2. Intercepted flow: the method interceptor is a middleman
3.1.1. PostSharp method interception
Listing 3.1. Calling Send method on TwitterClient service class
Figure 3.3. Console output simulating a tweet
Listing 3.2. Indicating which method to intercept with an attribute
Figure 3.4. Diagram with a real PostSharp method interception aspect
Figure 3.5. Console output with interceptor
3.1.2. Castle DynamicProxy method interception
Listing 3.3. Calling the Send method on the TwitterClient service class
Figure 3.6. Console output simulating a tweet again
Listing 3.4. Adding code to the interceptor
Figure 3.7. Console output with a tweet and interceptors
3.2. Real-world example: data transactions
3.2.1. Ensuring data integrity with begin and commit
Listing 3.5. Three different transaction scenarios
Listing 3.6. A Main method to try out the Save scenarios
Figure 3.8. Console reporting that the save was successful.
Figure 3.9. SaveRetry fails on the first attempt.
Figure 3.10. SaveFail will always fail.
Listing 3.7. Basic transaction interceptor, used with proxy generator
Listing 3.8. Using ProxyGenerator to apply the TransactionWithRetries aspect
3.2.2. When transactions go bad: rollback
Listing 3.9. Using Dispose to rollback
3.2.3. When all else fails, retry
Listing 3.10. Adding a loop to retry
Figure 3.11. Success after one retry
Listing 3.11. Transaction with maximum retries specified in constructor
Figure 3.12. Console reporting that there was one retry before successful save
3.3. Real-world example: threading
3.3.1. The basics of .NET threading
3.3.2. UI threads and worker threads
Figure 3.13. A desktop application
Listing 3.12. Full Form1 class with button click event handler
Figure 3.14. Because GetTweet runs on the UI thread, the other UI controls are unable to accept input while it’s running.
Listing 3.13. Checking whether Invoke is necessary
Listing 3.14. Wouldn’t it be nice if threading were more like this?
3.3.3. Declarative threading with AOP
Listing 3.15. UIThread interceptor aspect
Listing 3.16. Declarative threading is now a reality
Listing 3.17. Changing from Thread to Task
3.4. Summary
Chapter 4. Before and after: boundary aspects
4.1. Boundary aspects
4.1.1. PostSharp method bounding
Listing 4.1. Calling a GetBattingAverage method on a service class
Figure 4.1. Console reporting results
Listing 4.2. Using an attribute to indicate which method to bound
Figure 4.2. Console out with method boundary aspect
4.1.2. Method boundaries versus method interception
Figure 4.3. Side-by-side comparison of a method boundary and a method interception
Shared State Between Aspect Methods
Listing 4.3. Using a class member for shared state
Listing 4.4. A Console application showing the pitfalls of using an aspect field in PostSharp
Figure 4.4. Output of the shared state demo
Listing 4.5. Using MethodExecutionTag to share state
Figure 4.5. Output using MethodExecutionTag for shared state
Clarity/intent of code
Listing 4.6. A boundary aspect that overrides only OnExit
Listing 4.7. An equivalent interception aspect
4.1.3. ASP.NET HttpModule bounding
Figure 4.6. Demo.aspx in a browser
Listing 4.8. Configuring MyHttpModule in Web.config
Listing 4.9. Event handlers in MyHttpModule
Figure 4.7. Demo.aspx page as viewed in a browser
Listing 4.10. Writing text to the response at the beginning and end of a page
Figure 4.8. Demo.aspx with HttpModule viewed in a browser
4.2. Real-world example: detecting mobile users
4.2.1. Offer a link to an application
Figure 4.9. Examples of a mobile interstitial when visiting LinkedIn or the Verge
Figure 4.10. Routing a mobile user to their preferred experience
Create an HttpModule
Listing 4.11. Subscribing to the BeginRequest boundary event
Check for mobile user
Listing 4.12. A class to detect mobile users
Redirect to a Mobile Splash Screen
Listing 4.13. Detecting a mobile browser
Figure 4.11. Display an interstitial to the mobile user
Listing 4.14. Basic splash screen with two buttons
Listing 4.15. Handling the button clicks in code-behind
Adding checks
Listing 4.16. Adding checks to avoid redirect loops
4.2.2. Don’t be a pest
Listing 4.17. Checking for a cookie
4.3. Real-world example: caching
Figure 4.12. You might be victim of slow request transit, slow processes, slow response transit, or all of the above
Figure 4.13. How a cache helps with a slow process or slow transit
4.3.1. ASP.NET Cache
4.3.2. An application that could benefit from caching
Figure 4.14. A page with a form to look up a car’s market value
Listing 4.18. CarValue.aspx HTML and ASP.NET controls
Listing 4.19. Add items to each drop-down list
Listing 4.20. An event to handle a button click
Listing 4.21. Show the entire contents of Cache
Figure 4.15. The CarValue.aspx page in a browser
Listing 4.22. Clicking the Get Value button
Figure 4.16. The CarValue.aspx showing a dollar amount after clicking Get Value
4.3.3. Caching a result
Listing 4.23. Storing the results of the method in a cache
Listing 4.24. Naïve GetCacheKey implementation
Figure 4.17. After three different requests, the cache contains three values
Figure 4.18. How the Cache key is constructed
4.3.4. Retrieving from the cache
Listing 4.25. Add an OnEntry override to the CacheAspect class
Listing 4.26. Checking the cache in OnEntry
Listing 4.27. Refactored CarValueService to encapsulate options
4.3.5. A more robust cache key
Listing 4.28. A more robust GetCacheKey using serialization
Figure 4.19. Serialized object used for cache keys
4.4. Summary
Chapter 5. Get this instead: intercepting locations
5.1. Location interception
5.1.1. Fields and properties in .NET
Listing 5.1. Encapsulation of a private field with methods
Listing 5.2. The compiled program viewed through the ILSpy tool
5.1.2. PostSharp location interception
Listing 5.3. Using a method interception aspect on a property
Figure 5.1. The Console output of method interception on a property
Listing 5.4. Using a PostSharp location interception aspect
5.2. Real-world example: lazy loading
5.2.1. Lazy loading approaches in .NET
Listing 5.5. Lazy loading with a property and a backing field
Listing 5.6. Thread-safe lazy loading with double-checked locking
Listing 5.7. Using System.Lazy<T>
5.2.2. Implementing lazy loading with AOP
Listing 5.8. Basic Console application with no lazy loading
Figure 5.2. Output with no lazy-loading
Listing 5.9. A lazy-loading aspect
Figure 5.3. Output with lazy-loading
5.2.3. What about lazy-loading fields?
Listing 5.10. Lazy loading using Activator
Listing 5.11. SlowConstructor with a dependency on IMyService
Listing 5.12. Initializing
Listing 5.13. Lazy loading with StructureMap
Listing 5.14. Using StructureMap in place of Activator
Figure 5.4. Lazy loading with dependencies via StructureMap
5.3. Real-world example: INotifyPropertyChanged
5.3.1. Using INotifyPropertyChanged in a desktop application
Figure 5.5. MainWindow layout with text boxes and labels
Listing 5.15. XAML to create textbox, button, and textblocks
Listing 5.16. A view model class representing the data
Listing 5.17. Binding a NameViewModel object to MainWindow’s DataContext
Listing 5.18. Binding controls to ViewModel properties
Listing 5.19. Implementing INotifyPropertyChanged on NameViewModel
Figure 5.6. Type in first and last name, and watch the full name populate
5.3.2. Problems and constraints with INotifyPropertyChanged
Listing 5.20. Using CallerMemberName attribute
5.3.3. Reducing boilerplate with AOP
Figure 5.7. Installing NotifyPropertyWeaver
Listing 5.21. Boilerplate-free use of INotifyPropertyChanged
Listing 5.22. NameViewModel with a custom PostSharp aspect
Listing 5.23. Constructor that accepts derived property names
Listing 5.24. Perform all the steps of property notification in OnSetValue
Listing 5.25. RaisePropertyChanged method
5.4. Summary
Chapter 6. Unit testing aspects
6.1. Writing tests with NUnit
6.1.1. Writing and running NUnit tests
Listing 6.1. Writing a test of the Reverse method
Figure 6.1. The NUnit test runner UI
Figure 6.2. One failing test
Figure 6.3. One passing test
6.1.2. Testing strategies for aspects
Listing 6.2. Testing for the presence of attributes
6.2. Castle DynamicProxy testing
6.2.1. Testing an interceptor
Listing 6.3. A static Log class
Listing 6.4. NUnit fixture to test MyInterceptor
Listing 6.5. Using Moq
6.2.2. Injecting dependencies
Putting inversion of control to work
Figure 6.4. Dependency diagram of a more complex domain
Implement the services
Listing 6.6. MyOtherService service implementation and interface
Listing 6.7. LoggingService implementation and interface
Write the logging aspect
Listing 6.8. MyLoggingAspect implementation
Unit testing the aspect
Listing 6.9. Testing MyLoggingAspect
Create the MyService class
Listing 6.10. MyService implementation and interface
Listing 6.11. Using MyService object in Main
Using an IOC tool to manage the dependencies
Listing 6.12. Initializing and using StructureMap
Using DynamicProxy with StructureMap
Listing 6.13. One approach to using aspects with a complex domain
Refactor using EnrichWith
Listing 6.14. Using EnrichWith feature of StructureMap
Listing 6.15. More concise use of EnrichWith
A ProxyHelper
Listing 6.16. ProxyHelper
6.3. PostSharp testing
6.3.1. Unit testing a PostSharp aspect
Listing 6.17. A test of a PostSharp aspect
6.3.2. Injecting dependencies
Listing 6.18. Program’s Main method
Listing 6.19. MyLoggingAspect with PostSharp
Listing 6.20. Dependency inversion via RuntimeInitialize
Figure 6.5. Correct Console output
Listing 6.21. Creating an aspect object for the test
Listing 6.22. Call OnEntry and OnSuccess to execute the aspect
Listing 6.23. Verify that the logging operations happened
6.3.3. Problems with PostSharp and testing
Compile-time weaving
Listing 6.24. A class/method that reverses a string, and its unit test
Listing 6.25. Reverse method with aspect applied
Listing 6.26. Mocking ILoggingService to satisfy the aspect
Workaround by turning off PostSharp
Listing 6.27. Workaround with custom symbols
Listing 6.28. A global variable setting for unit testing
Inaccessible constructors
Testing PostSharp indirectly
Figure 6.6. A fat aspect and a thin aspect
Listing 6.29. A thin aspect and the interface to which it’s delegating
6.4. Summary
Part 3. Advanced AOP concepts
Chapter 7. AOP implementation types
7.1. How does AOP work?
Figure 7.1. A high-level diagram of AOP
7.2. Runtime weaving
Figure 7.2. A proxy class constructed at runtime
7.2.1. Proxy pattern revisited
Figure 7.3. The proxy pattern
7.2.2. Dynamic proxies
Listing 7.1. A Console program that uses a proxy generator
Listing 7.2. Creating an AssemblyBuilder and a ModuleBuilder
Listing 7.3. Define a Type and get a TypeBuilder
Listing 7.4. Define a field and get a FieldBuilder
Listing 7.5. Define a constructor and get an ILGenerator
Listing 7.6. Emitting CIL code for the constructor
Listing 7.7. Tweet method for the interface with an ILGenerator
Listing 7.8. Writing to Console and calling the real Tweet method with OpCodes
Figure 7.4. The output after creating a proxy class at runtime
7.3. Compile-time weaving
Figure 7.5. From C# to CIL to the CLR
Figure 7.6. The same process from figure 7.5 with PostSharp added
7.3.1. Postcompiling
Figure 7.7. From source to assembly to execution
7.3.2. Before and after
Figure 7.8. Decompiling an assembly with ILSpy
Listing 7.9. Introducing a PostSharp aspect
Figure 7.9. Decompiling an assembly that’s been modified with PostSharp
Listing 7.10. ILSpy decompile of MyClass
7.4. Runtime versus compile-time weaving
7.4.1. Pros of runtime weaving
7.4.2. Pros of compile-time weaving
7.5. Summary
Chapter 8. Using AOP as an architectural tool
8.1. Compile-time initialization and validation
Figure 8.1. Build process with PostSharp
Figure 8.2. Zooming into the post-compiler steps of PostSharp aspects
Note
8.1.1. Initializing at compile time
Listing 8.1. Logging the method that was called
Listing 8.2. Using Initialize to get the method name at compile time
8.1.2. Validating the correct use of an aspect
Listing 8.3. A LocationInterceptionAspect to log usage of get
Listing 8.4. Using CompileTimeValidate to check the location name
Figure 8.3. CompileTimeValidate-generated error message
8.1.3. Real-world example: Threading revisited
Listing 8.5. Runtime checks on casting
Listing 8.6. Threading aspect of compile-time validation
Figure 8.4. UIThread validation error message
8.2. Architectural constraints
8.2.1. Enforcing architecture
Scalar constraints
Referential constraints
Table 8.1. PostSharp out-of-the-box architectural constraints
Listing 8.7. Unsealable referential constraint
Listing 8.8. Unsealable demonstration
Figure 8.5. Error caused by trying to seal an Unsealable class
8.2.2. Real-world example: NHibernate and virtual
Figure 8.6. The InvalidProxyTypeException of death
Figure 8.9. Multicasting at the class level
Listing 8.9. NHibernate ScalarConstraint
Listing 8.10. An NHibernate entity with a nonvirtual property
Figure 8.7. Result of NHEntity scalar constraint
8.3. Multicasting
Figure 8.8. Pointcuts on a low-level flowchart
8.3.1. At the class level
Figure 8.10. Aspect applied to the constructor and three methods
Listing 8.11. Excluding a member with AttributeExclude
Figure 8.11. No aspect applied to Method3
Listing 8.12. AspectPriority for ordering
Figure 8.12. Swapping AspectPriority
Listing 8.13. AttributeTargetElements to target only the instance constructor
8.3.2. At the assembly level
8.4. Summary
Chapter 9. Aspect composition: example and execution
Listing 9.1. Multiple aspects on the same method
9.1. Using multiple aspects
Table 9.1. Ordering of aspects
Figure 9.1. Scenario: caching first
Figure 9.2. Scenario: authorization first
9.2. Aspect roles with PostSharp
9.2.1. PostSharp aspect roles
Listing 9.2. Specifying an aspect role
9.2.2. Role dependencies
Listing 9.3. Specifying aspect role dependency
Listing 9.4. An aspect that requires another role
Figure 9.3. Failed aspect dependency
9.3. Composing aspects with DynamicProxy
Listing 9.5. ProxyHelper from chapter 6
9.3.1. Ordering aspects
Listing 9.6. A demo class and two demo aspects
Listing 9.7. Use of multiple aspects on the same code in StructureMap
Figure 9.4. A diagram of what gets returned by StructureMap
Figure 9.5. Switching the order of Aspect1 and Aspect2
9.3.2. Reducing repetition with custom conventions
Class name convention
Listing 9.8. A StructureMap name-based convention for applying aspects
Listing 9.9. Added overloaded Proxify to ProxyHelper
Listing 9.10. Adding a convention to the assembly scanner
Namespace Convention
Listing 9.11. Using a namespace-based convention
9.4. Real-world example: caching and authorization
9.4.1. Application architecture
Figure 9.6. The correct ordering of authorization and caching.
Figure 9.7. A high-level view of the architecture
Dependency configuration
Listing 9.12. Configure StructureMap with default conventions
Services
Listing 9.13. Budget service, interface, and implementation
Listing 9.14. A static caching service
Listing 9.15. User repository
Console UI
Listing 9.16. The main UI of the program
Listing 9.17. The main UI prepared for exception
Concerns
Note
Listing 9.18. Caching concern interface and constructor
Listing 9.19. OnEntry of the caching concern
Listing 9.20. Adapter interface to get method context
Listing 9.21. OnSuccess of the caching concern
Listing 9.22. Authorization concern interface and constructor
Listing 9.23. OnEntry of the authorization concern
9.4.2. PostSharp
Listing 9.24. PostSharp caching aspect
Listing 9.25. PostSharp method context adapter
Figure 9.8. The thin logging aspect, repeated from chapter 6
Figure 9.9. Execution, using budget service with caching aspect
Listing 9.26. PostSharp authorization aspect
Note
Figure 9.10. Execution again, with caching and authorization aspects, undefined composition
Listing 9.27. Aspect composition of caching and authorization
Figure 9.11. Execution, with authorization and caching composed correctly
9.4.3. Castle DynamicProxy
Write two new aspects
Listing 9.28. DynamicProxy caching aspect Intercept code
Listing 9.29. Castle DynamicProxy method context adapter
Listing 9.30. Constructor and members of the Authorization interceptor
Listing 9.31. Authorization Intercept code
Remove PostSharp attributes
Change IoC configuration
Listing 9.32. Another Proxify overload in ProxyHelper
Listing 9.33. Name a mapping for Manager authorization
Listing 9.34. Complete IoC initialization
Figure 9.12. Execution with Auth and Cache aspects, using Castle DynamicProxy
9.5. Summary
Appendix A. Ecosystem of .NET AOP tools
A.1. Compile-time AOP tools
A.1.1. PostSharp
A.1.2. LinFu
Listing A.1. Simple LinFu example
Listing A.2. Modifying project file to add PostWeaveTask
A.1.3. SheepAspect
Listing A.3. Simple SheepAspect example
A.1.4. Fody
Listing A.4. Simple MethodDecorator example
A.1.5. CIL manipulation tools
Mono.Cecil
PostSharp SDK
Microsoft CCI
A.2. Runtime AOP tools
A.2.1. Castle Windsor/DynamicProxy
Listing A.5. A basic Castle Windsor and DynamicProxy example
A.2.2. StructureMap
Listing A.6. StructureMap interception
A.2.3. Unity
Listing A.7. Unity interception example
A.2.4. Spring.NET
Listing A.8. Spring.NET example
Listing A.9. Spring.NET configuration
Appendix B. NuGet basics
B.1. Introduction to NuGet
B.1.1. Installing NuGet
Figure B.1. Installing Nuget from NuGet.org
Figure B.2. Install NuGet from Extension Manager
B.1.2. Installing packages with NuGet UI
Figure B.3. Installing a package with the NuGet UI
B.1.3. Install packages with Package Manager Console
Figure B.4. Opening the Package Manager Console
B.2. NuGet package restore
B.2.1. Solution Explorer
Figure B.5. Always show solution in Visual Studio
B.2.2. Enabling package restore
Figure B.6. Enabling package restore feature
B.2.3. What package restore does
Figure B.7. Enabling downloading of missing packages
Index
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
List of Figures
List of Tables
List of Listings
← Prev
Back
Next →
← Prev
Back
Next →