Extreme ASP.NET Makeover: SoC - Principles to Practice

Download this episode

Download Video


Extreme ASP.NET Makeover: Separation of Concerns – Principles to Practice


When looking at the code, the first thing that our attention was drawn to was the repetitive nature of the authorization code.  As you can see in the code in Figure 1, there are a large number of calls to retrieve single permission values.

Figure 1 Default.aspx Page_Load Code

string currentUsername = SessionFacade.GetCurrentUsername();
string[] currentGroups = SessionFacade.GetCurrentGroupNames();
bool canView = AuthChecker.Instance.CheckActionForPage(currentPage, Actions.ForPages.ReadPage, currentUsername, currentGroups);
bool canEdit = false;
bool canEditWithApproval = false;
Pages.CanEditPage(currentPage, currentUsername, currentGroups, out canEdit, out canEditWithApproval);
if(canEditWithApproval && canEdit) canEditWithApproval = false;
bool canDownloadAttachments = AuthChecker.Instance.CheckActionForPage(currentPage, Actions.ForPages.DownloadAttachments, currentUsername, currentGroups);
bool canSetPerms = AuthChecker.Instance.CheckActionForGlobals(Actions.ForGlobals.ManagePermissions, currentUsername, currentGroups);
bool canAdmin = AuthChecker.Instance.CheckActionForPage(currentPage, Actions.ForPages.ManagePage, currentUsername, currentGroups);
bool canViewDiscussion = AuthChecker.Instance.CheckActionForPage(currentPage, Actions.ForPages.ReadDiscussion, currentUsername, currentGroups);
bool canPostDiscussion = AuthChecker.Instance.CheckActionForPage(currentPage, Actions.ForPages.PostDiscussion, currentUsername, currentGroups);
bool canManageDiscussion = AuthChecker.Instance.CheckActionForPage(currentPage, Actions.ForPages.ManageDiscussion, currentUsername, currentGroups);

Now, we certainly can’t look at this code and simply say, “Get rid of all that stuff”, because it is necessary for the page to properly function. But how permissions are retrieved, whether from the AuthChecker or Pages or even hard-coding, is the responsibility of some other class, not this one's responsibility.  All Page_Load needs are the final values.

Since we have decided that the Page_Load no longer needs to have intimate knowledge of these details, we can refactor them out. Our first refactoring step is to create a class that is responsible solely for providing the authorization information to the Default.aspx.cs file. We’ll call this class AuthorizationServices. Instead of diving straight into the contents of AuthorizationServices, let’s look at how we want to use it in the Default.aspx.cs Page_Load method. What we want to do is eliminate all of the individual calls to the AuthChecker class and replace them with a single call that returns all of the same authorization information, but in a consolidated fashion. That is, we are looking for something like this:

AuthorizationServices authorizationServices = new AuthorizationServices();
CurrentCapabilitiesStatus currentUser = authorizationServices.RetrieveCurrentStatusFor(CurrentPage);

That single call to AuthorizationServices is what we want to determine what levels of permission are available for the current user. The results of making that method call are returned in a CurrentCapabilitiesStatus object. That object is quite simple and has only properties containing the different authorization values.


SIDEBAR—When refactoring a brownfield application, it often helps to look at things from the client perspective, as we did here. We determined how we want the code to look from the perspective of the Page_Load method in an ideal scenario, and then worked toward making it happen. This is an effective way of isolating the responsibility of a class and separating other responsibilities into other classes.


Now that we know what we want the usage of the code to look like, we can move on to creating those classes and filling them out. Since our refactoring step is to remove the detailed information about the authorization concern out of the Page_Load method, let’s start there. In this case, the simplest refactoring task is to cut and paste the existing AuthChecker calls in the Page_Load code into the RetrieveCurrentStatusFor method.  To make the AuthChecker code compile and function, we will need to move some other code as well. This will include:

string currentUsername = SessionFacade.GetCurrentUsername();
string[] currentGroups = SessionFacade.GetCurrentGroupNames();
var canEdit = false;
var canEditWithApproval = false;
Pages.CanEditPage(currentPage, currentUsername, currentGroups, out canEdit, out canEditWithApproval);

Now that we have all of that code in the RetrieveCurrentStatusFor method, the final step is to create and populate the output object type of CurrentCapabilitiesStatus. As we mentioned before, this is a simple class that contains only properties in it. You may have noticed in an earlier code snippet that we assigned an instance of this object to a variable named “currentUser”. At the time, this may have seemed odd, but when we combine that with the property names, we see that the resulting code in Page_Load becomes more intention-revealing. Since we don’t know what the intended use of the properties are when we’re inside the CurrentCapabilitiesStatus class, let’s step back to the Page_Load method and look at how we intend to use them there.

Chances are, if you did a complete cut and paste from Page_Load to AuthorizationServices.RetrieveCurrentStatusFor, there will be noncompiling code in the Page_Load method now.  This should be limited to the variables that used to be in the method, but are now being moved to the CurrentCapabilitiesStatus class as properties. These are the points of usage that we’re looking for. The first one that you will see is an "if" statement that checks to see if the current user cannot view the current page. Let’s change this to use our “currentUser” variable now.

if(!currentUser.CanView) {
    // body of method remains unchanged

This is where you see how using the name currentUser starts to come into play. When scanning this code, it should be clear exactly what we are doing in this method. We have decided to name the property “CanView”, since we are predominantly working with positive questions when using the CurrentCapabilitiesStatus variable type. If you were to go through the rest of the authorization information used, you would end up with properties for CanDownloadAttachments, CanSetPerms, CanAdmin, CanViewDiscussion, CanEditDiscussion, CanManageDiscussion, CanEdit, and CanEditWithApproval, all defined on the CurrentCapabilitiesStatus object.

Once you have those properties added to the CurrentCapabilitiesStatus object and being used in the Page_Load method where needed, we still have one remaining loose end to tie up. While we moved the authorization code from Page_Load to the RetrieveCurrentStatusFor method in AuthorizationServices, we didn’t actually create a CurrentCapabilitiesStatus object and populate it. Doing that property value assignment is the final step in this refactoring.

Other videos from this article

Read the full article at http://msdn.microsoft.com/en-us/magazine/ee210417



Available formats for this video:

Actual format may change based on video formats available and browser capability.

    The Discussion

    Comments closed

    Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums, or Contact Us and let us know.