In the new architecture we've been developing, one of the features we've developed is a common persistence mechanism. Once again, our attempts are to make development as clear and painless as possible. We want to make it incredibly easy to write good code, and difficult to make mistakes. As our development team grows and code authors move on to other things and other devs end up maintaining that code, we want it to be downright difficult to misunderstand or misuse anything.
Persistence is one area that we've seen misused in the past. Basically, anyone can put anything in Session or ViewState from almost anywhere they want. Well, they can from the code-behind of pages, which, like I discussed earlier, ends up getting a great deal of logic thrown in there. We decided to put a stop to this in our new architecture.
We've refactored all the persistence code into base classes, and we're using attributes to decorate our View properties like the following:
[Persist(PersistenceType.Session)]
public int IndividualID {get{} set{}}
This allows us to very simply specify what needs to be persisted and how. Currently the three different methods of persistence we're starting with are - ViewState, Session, and Process.
ViewState - this one is pretty self-explanatory
[Persist(PersistenceType.ViewState)]
Session - this is for page-level session only. It persists the data for as long as the user doesn't navigate to a different page. The fact that we've refactored all our persistence code allows us to implement a very aggressive state management strategy that deletes any session data that does not EXPLICITLY belong at any given time.
[Persist(PersistenceType.Session)]
Process - this is for state that needs to be maintained across pages. If a page wants to enlist in a shared-state scenario (like a wizard or any sequence of pages the user can move back and forth between). The developer just passes the name of the process to the attribute's constructor, which assumes a Process persistence. Once again, due to aggressive state management, we immediately delete any process data that isn't explicitly enlisted in by the currently loading page.
[Persist(ProcessList.RegistrationProcess)]
For state that needs to be persisted across the user's entire session, our base Page class will maintain this responsibility. No other code should have this capability. Also note that nothing in our Controllers or business layer can push anything to persistent state. Those layers deal with explicit instructions and methods, some of which may store things in the database, etc, but there's no concept of a Controller saying, "I want this to be placed in persistent state". Controllers just manipulate properties and expect them to be stateful. It's up to the presentation layer to decide how something should be persisted. The difference between WinForms and WebForms is so great that the idea of placing that logic in the Controller makes no sense to us.
As to the performance issues of using Reflection to read and act upon attributes, we feel it's negligable compared to the benefits. We ran some simple tests, and found that using Attribute-based persistence was approximately 100 times slower than not using Attributes. While that may sound huge, it's not when you're talking about the difference (in the worst case) between .00281 and .000228 seconds. Or, in a longer running scenario, between .000268 and .0000363. Not only that, but we've researched several methods to make long-running reflection usage far more performant via the use of Reflection.Emit and also ASP.NET 2.0's Dynamic Methods. Once we've solidified other areas of our new architecture, we can go back and tighten the screws.
These changes will increase the visibility and maintainability of our code by allowing any developer to very quickly see exactly what is persisted and how. Having all our persistence logic refactored into one place will eliminate any potential mistakes a developer could make in persisting data, and will increase our agility in extending the functionality in the future. The point is, there's too much to gain from Reflection not to use it.