The Inquisitive Coder - Davy Brion’s Blog

Thinking outside of the typical .NET box

Archive for the 'ASP.NET' Category


How To Write Testable ASP.NET WebForms

Posted by Davy Brion on 6th July 2008

Microsoft’s upcoming ASP.NET MVC framework makes it easy to write tests for your application layer logic. But what about those of us who are stuck with ASP.NET WebForms? You can still write highly testable ASP.NET WebForms with only a little bit of extra effort. But that extra effort really pays off in the long run. In this post, i’ll give a detailed description of one approach that has worked for me really well. I first started using this approach for a project at work sometime last year. It allowed me to cover my application logic with a lot of tests which weren’t a hassle to write or run, so naturally i’ve always wanted to write a detailed post on this subject. I hope you’ll like it :) (Btw, if you’re new to mocking this might serve as an introduction to that as well)

This is the screen we’re going to create:

The very first thing you’ll notice is that i completely suck at graphic design. So try to ignore the crappy look, and lets focus on what this screen should do. A user can perform a search on products based on the name, the product category and the supplier of the product. After clicking the Search button, the user is presented with a list of products that match the search criteria. Next to each product is an Edit link (because i was too lazy to find a nice image for this). When clicked, the application should navigate to an Edit screen where the chosen product can be edited. To keep this example short (this post will be long enough already!), that’s all for this screen.

So what are the things that we would want to test for a simple screen like this? For starters, i want to be sure that when this page is loaded, it retrieves the list of product categories and suppliers, and that it displays them. Another thing i want to test is that when the search button is clicked, the screen has to retrieve all the matching products and display them. And when the Edit link is clicked, i want to make sure that this screen navigates to the Edit screen with the correct parameters.

I’m going to do this using an MVP (Model-View-Presenter) approach, the Supervising Controller variant to be more specific. As with any pattern, i believe you should use it in a pragmatic way. In this implementation i don’t follow every rule strictly, i will just try to provide an approach that offers you all the advantages of the pattern, while trying to make it as easy as possible to implement.

I’m also going to use a couple of techniques that will allow me to write fast tests which should be easy to maintain as well. I’ll use a mocking framework (Rhino Mocks), Dependency Injection and an Inversion of Control container (Castle Windsor). Don’t worry if you’re unfamiliar with these topics, when needed i’ll try to explain everything. And of course, you’re always welcome to ask questions :)

Anyway, enough talk… let’s get started shall we?

First, we need an abstract way to define a View (which corresponds to a page basically):

    public interface IView

    {

        bool IsPostBack { get; }

        bool IsValid { get; }

 

        void DataBind();

        void DisplayErrorMessage(string message);

    }

Every page in this application implements the IView interface, although each page also implements a more specific interface. Each view will have a controller, which has to be able to communicate with the view. This communication is usually limited to providing data and telling the view to perform a DataBind operation, or telling it to display a certain message. But the controller can sometimes also request information from the view, like asking if the view is currently in a PostBack, or if the view is currently valid, or whatever else you might need.

This is the interface of the page shown above:

    public interface IProductList : IView

    {

        IEnumerable<ProductCategoryDTO> ProductCategories { get; set; }

        IEnumerable<ProductOverviewDTO> Products { get; set; }

        IEnumerable<SupplierDTO> Suppliers { get; set; }

    }

As you can see, this interface merely provides a few properties on top of what the IView interface provides. This is one example of where i deviate from the typical implementations of this pattern. Most people define events in the view’s interface for each user action that can occur. The controller then subscribes to these events when it is bound to the view, and it handles those events. While that is theoretically a nice approach, i found it to be somewhat cumbersome, both in writing more code than you really need and making the tests a bit more cumbersome to write. So in my implementation, the Controller actually offers public methods for each user action. The view then simply calls the controller’s public methods when these actions occur. This means that both the view and the controller know about each other. A lot of purists will not like this, but i believe the (mostly theoretical) downsides to the view and the controller knowing about each other don’t match up to simpler implementation.

Anyways, you probably want to know what the controller looks like. We’ll get to that soon, but first we define a base Controller type that each controller will inherit from:

    public abstract class Controller<T> : Disposable, IController where T : IView

    {

        protected Controller(T view)

        {

            View = view;

        }

 

        protected T View { get; set; }

    }

Right now, this is a pretty simple class, but as you implement more screens, you will most likely refactor common controller methods to this base class. In this application, the controller will usually communicate with a proxy to a remote service. That proxy is actually the model in this implementation. Obviously, if you don’t need a service layer you can simply use the real Model objects in the controller. But since a proxy to a remote service is an expensive object that needs to be cleaned up properly, i made the controller inherit from the Disposable class. Each derived controller will need to provide a method to clean up its expensive resources.

The specific controller for this application looks like this:

    public class ProductListController : Controller<IProductList>

    {

        private IProductManagementService service;

        private readonly IProductsNavigator navigator;

 

        public ProductListController(IProductList view, IProductManagementService service,

            IProductsNavigator navigator) : base(view)

        {

            this.service = service;

            this.navigator = navigator;

        }

 

        protected override void DisposeObjects()

        {

            if (service != null) service.Dispose();

        }

 

        protected override void ClearReferences()

        {

            View = null;

            service = null;

        }

    }

We’ll add the methods to handle the user actions later on, so this class is not complete yet. You can see that the controller has 3 dependencies… the first being the view, the second is the service and the third one is an instance of the IProductsNavigator interface. I use small navigator classes to perform all of my navigation because it makes it easy to test that a navigation has occurred without actually having to move to another page.

The IProductsNavigator interface looks like this:

    public interface IProductsNavigator

    {

        void GoToEdit(int? productId);

        void GoToSearch();

    }

Nothing special here, just a method to move to the edit screen with an optional product Id (the edit screen is also used to edit a new product’s data, and then the productId parameter will be null) and another method to move to the Search screen. The code of the class that implements this interface merely does a redirect to the correct page. But it’s important to get that code out of the controller because it would lower testability.

Anyways, let’s get to the whole writing tests part. Because we’re going to write as much code as possible in the controller instead of the view, we will simply test the controller with a fake view and a fake model (service). That’s right, we’re going to test our application code for this page without an actual page. We will mock the view and the service, and we’ll pass those mocked dependencies to the controller. In our tests we will then instruct the mocks to behave like their real versions, depending on what we’re trying to test.

First of all, we’ll define a base controller test class:

    public abstract class ControllerTest

    {

        protected static void PrepareServiceToReturnResponses(IService service, ServiceRequestResponseSpy spy,

            params Response[] responses)

        {

            spy.SetResponsesToReturn(responses);

            service.Stub(s => s.Process(null))

                .IgnoreArguments()

                .Do(new Func<Request[], Response[]>(spy.GrabRequestsAndReturnGivenResponses));

        }

    }

This class will simply provide some helper methods that will be common to our controller tests. The method that is already there can be ignored for now, but if you want to know what it does you can look here. I’ll also (briefly) explain it when it’s used in a test.

Our test class needs to set up the mocked dependencies and provide a way to create the controller with those mocks so we already have the following code:

        private MockRepository mocks;

        private IProductManagementService service;

        private IProductList view;

        private IProductsNavigator navigator;

        private ProductListController controller;

 

        [SetUp]

        public void SetUp()

        {

            mocks = new MockRepository();

            service = mocks.DynamicMock<IProductManagementService>();

            view = mocks.DynamicMock<IProductList>();

            navigator = mocks.DynamicMock<IProductsNavigator>();

        }

 

        private ProductListController CreateController()

        {

            controller = new ProductListController(view, service, navigator);

            return controller;

        }

Nothing special here… the mocks are create before each test in the SetUp method, and we have helper method which creates the controller with the mocks so we don’t have to do this ourselves in each test.

Ok, now we can finally get to our first test. I don’t know about you, but i really hate it when a page performs code that it really doesn’t have to do in a PostBack. So we’ll guard against that with the following test:

        [Test]

        public void DoesNotRetrieveCategoriesAndSuppliersOnLoadIfPostBack()

        {

            view.Stub(v => v.IsPostBack).Return(true);

 

            mocks.ReplayAll();

            CreateController().Load();

 

            service.AssertWasNotCalled(s => s.Process(null), options => options.IgnoreArguments());

        }

We instruct the mocked view to return true for the IsPostBack property. Then we create the controller, call its Load method and we verify that the Service’s Process method was not called in any way. Pretty simple, right? It does get a bit more complicated when we want to test that the correct data is retrieved when the page is initially loaded:

        [Test]

        public void RetrievesCategoriesAndSuppliersOnLoad()

        {

            var categoriesToReturn = new ProductCategoryDTO[0];

            var suppliersToReturn = new SupplierDTO[0];

 

            var spy = new ServiceRequestResponseSpy();

            PrepareServiceToReturnResponses(service, spy, new GetProductCategoriesResponse(categoriesToReturn),

                new GetSuppliersResponse(suppliersToReturn));

 

            view.Expect(v => v.ProductCategories = categoriesToReturn);

            view.Expect(v => v.Suppliers = suppliersToReturn);

            view.Expect(v => v.DataBind());

 

            mocks.ReplayAll();

            CreateController().Load();

 

            view.VerifyAllExpectations();

            Assert.IsNotNull(spy.GetRequest<GetProductCategoriesRequest>());

            Assert.IsNotNull(spy.GetRequest<GetSuppliersRequest>());

        }

First, we create two empty arrays of objects that we’ll instruct the mocked service to return when its Process method is called. We’re using the PrepareServiceToReturnResponses method here, which you’ve seen listed in the ControllerTest class. It basically allows you to provide Response instances and it uses the ServiceRequestResponseSpy class to hook into the mocked service. If you want to know the details behind this technique, go here.

Then we set some expectations on the view. We expect that its ProductCategories property will be set to the value that we’ve instructed the mocked service to return. Same thing for the Suppliers property. Then we define an expectation that the view’s DataBind method should be called. After that, we create the controller, call the Load method and we verify that all expectations on the view were met. We also assert that the service indeed received the proper requests.

So what code did we just test? Well, the Load method of the controller, which now looks like this:

        public void Load()

        {

            if (!View.IsPostBack)

            {

                var batcher = new ServiceCallBatcher(service);

                batcher.Add(new GetProductCategoriesRequest());

                batcher.Add(new GetSuppliersRequest());

                View.ProductCategories = batcher.Get<GetProductCategoriesResponse>().ProductCategories;

                View.Suppliers = batcher.Get<GetSuppliersResponse>().Suppliers;

                View.DataBind();

            }

        }

The load method uses the service to retrieve the product categories and the suppliers, in one remote call. You can find more information on that here and here.

Now we can write a test to make sure that the controller behaves correctly when the user presses the Search button:

        [Test]

        public void CallsTheServiceToSearchForProductsAndBindsResultsToView()

        {

            const string productPattern = “whatever”;

            const int categoryId = 4;

            const int supplierId = 7;

 

            var productsToReturn = new ProductOverviewDTO[0];

            var spy = new ServiceRequestResponseSpy();

            PrepareServiceToReturnResponses(service, spy, new GetProductOverviewsResponse(productsToReturn));

 

            view.Expect(v => v.Products = productsToReturn);

            view.Expect(v => v.DataBind());

 

            mocks.ReplayAll();

            CreateController().Search(productPattern, categoryId, supplierId);

 

            view.VerifyAllExpectations();

            var request = spy.GetRequest<GetProductOverviewsRequest>();

            Assert.AreEqual(productPattern, request.NamePattern);

            Assert.AreEqual(categoryId, request.CategoryId);

            Assert.AreEqual(supplierId, request.SupplierId);

        }

This should look somewhat familiar by now. We set up an empty array of ProductOverview instances that we want the service to return when it receives a request. We then set the expectations on the view, just like we did in the previous test. Then we create the controller and call its Search method with some search parameters. Then we verify that the view’s expectations were met, and we use the service spy to retrieve the request that it received. We then verify that the request parameters are the same as the ones we sent to the controller.

The Search method of the controller looks like this:

        public void Search(string name, int? productCategoryId, int? supplierId)

        {

            var response = service.GetProductOverviews(

                new GetProductOverviewsRequest(name, productCategoryId, supplierId));

            View.Products = response.Products;

            View.DataBind();

        }

Our final test is very simple. We just need to make sure that the page navigates to another page with correct parameter when the user presses the Edit link next to a product:

        [Test]

        public void NavigatesToEditProductScreenWhenEditProductIsTriggered()

        {

            const int productId = 5;

 

            navigator.Expect(n => n.GoToEdit(productId));

 

            mocks.ReplayAll();

            CreateController().EditProduct(productId);

            navigator.VerifyAllExpectations();

        }

This really doesn’t need any explanation right? :)

And the code in the controller looks like this:

        public void EditProduct(int productId)

        {

            navigator.GoToEdit(productId);  

        }

We’ve already implemented all of the logic we need for this page, and we haven’t even started working on the page yet! First we provide a bit of plumbing code to make sure our pages are capable of correctly creating the correct controller and making sure it gets disposed properly when the page has been rendered. So we have the following base page:

    public abstract class NorthwindPage<T> : Page where T : IController

    {

        protected NorthwindPage()

        {

            Controller = CreateController();

        }

 

        protected T Controller { get; private set; }

 

        protected abstract T CreateController();

 

        protected override void OnPreRenderComplete(EventArgs e)

        {

            base.OnPreRenderComplete(e);

            Controller.Dispose();

        }

 

        // there are also some small helper methods in here that aren’t really

        // relevate to the example so i left them out

    }

And the code of the real page looks like this:

    public partial class ProductList : NorthwindPage<ProductListController>, IProductList

    {

        public IEnumerable<ProductCategoryDTO> ProductCategories { get; set; }

        public IEnumerable<ProductOverviewDTO> Products { get; set; }

        public IEnumerable<SupplierDTO> Suppliers { get; set; }

 

        protected void Page_Load(object sender, EventArgs e)

        {

            Controller.Load();

        }

 

        protected override ProductListController CreateController()

        {

            return Container.Resolve<ProductListController>(new { view = this });

        }

 

        protected void SearchButton_Click(object sender, EventArgs e)

        {

            Controller.Search(NameTextBox.Text, GetSelectedId(ProductCategoryList), GetSelectedId(SupplierList));

        }

 

        protected void EditProductLink_Click(object sender, EventArgs e)

        {

            Controller.EditProduct(GetIdForCurrentRow(sender as IButtonControl));

        }

 

        public override void DataBind()

        {

            if (ProductCategories != null && Suppliers != null)

            {

                var categories = new[] { new ProductCategoryDTO { Id = -1, Name = “All” } }.Union(ProductCategories);

                var suppliers = new[] { new SupplierDTO { Id = -1, CompanyName = “All” } }.Union(Suppliers);

                PrepareDropDownList(ProductCategoryList, categories, “Name”, “Id”);

                PrepareDropDownList(SupplierList, suppliers, “CompanyName”, “Id”);

            }

 

            if (Products != null)

            {

                ProductsGrid.DataSource = Products;

            }

 

            base.DataBind();

        }

    }

Most of this is pretty straightforward, except maybe the code in the CreateController method. I don’t like to repeat myself (although i’m aware that i often do that anyway) so you can find the story behind that line of code here.

So we’ve minimized the code in the actual page, and the important parts are all covered with tests. You’re probably thinking “that is a lot of test code for so little real code”, and you’re right… this approach does lead to a lot of test code. But it also leads to a lot less debugging :)

This was a pretty simple example… but you can of course use this approach on complex screens as well. You just need to provide a public method for each kind of action on your controller, and try to do as much as possible in the controller. You really want the view to remain as “dumb” as possible. It should delegate all logic to the controller, and then simply focus on data binding, and in screens where you can edit data, client-side input validation. The more code you can push to your controller, the more you can cover it with tests.

For instance, for this particular screen you probably want to add sorting capabilities. Just provide a public Sort method on your controller which takes a sort expression and sort direction as parameters. Then you can write tests to verify that the controller indeed offers the view a properly sorted list of data when the Sort method is called. In your view, you would then simply need to call the Sort method and pass the correct parameters when the user clicks on a column header. Or if you want to provide a Delete link (or image, ideally) next to each product, you’d provide a public Delete method on the controller which takes the product’s Id as the parameter. Then you can start writing interesting tests, like verifying that the controller sends a DeleteProductRequest instance to the service, and perhaps retrieves an updated list of products to bind to the view. Or better yet, you can write a test where the mocked service throws a business exception when you try to delete a product, to verify that the controller displays the correct message on the view and doesn’t remove the product from the view’s list.

Anything is pretty much possible, you just gotta make it work :)

Hope you enjoyed this post, i for one am very happy to have finally written it since it’s been on my TODO list for months now :)

kick it on DotNetKicks.com

Posted in ASP.NET, Test Driven Development | 6 Comments »

Automanual Dependency Injection?

Posted by Davy Brion on 16th June 2008

I apologize in advance for using the term ‘automanual’ but if you’ve been reading this blog for a while you already know i completely suck at coming up with good names. So bear with me, and you’ll probably understand what i mean as you work your way through this post. It really is pretty cool… i promise :p

I’m playing around with some supervising controller MVP stuff using regular ASP.NET webforms. Yes i know, i should be using ASP.NET MVC but i have a strict “i don’t touch it before there’s a final release”-policy when it comes to Microsoft products (as opposed to my “lemme just build the latest version of the trunk”-policy for various open source products). Anyways, what i’m trying to do is pretty simple. I have a view (ProductList.aspx) which uses a supervising controller (ProductListController). The view (the aspx page) will notify the controller when it needs to do something through events. The controller will then do whatever it needs to do and it will send the data back to the view through properties of the view. If that’s not clear to you, read the post i linked to for a much better and detailed explanation.

Since ASP.NET automatically instantiates your aspx page, the easiest thing to do is to let the view create the controller and then pass itself as a parameter to the controller. But the controller also has other dependencies, such as a service which exposes the business logic that we need for this screen. So i have two options: i either create the controller myself and provide all the dependencies, or i use an inversion of control container to create the controller and to wire up all the dependencies. I don’t want to have to modify my view code whenever i add/remove a dependency of the controller, so i go with the inversion of control container.

Suppose the constructor of the controller looks like this:

        public ProductListController(IProductList view, IProductsService productsService)

        {

            this.view = view;

            this.productsService = productsService;

        }

Here’s where it gets tricky… since the view (which implements the IProductList interface) is asking the container to create a ProductListController, how can the container pass the correct IProductList dependency to the controller? We can’t use the regular dependency look-up mechanisms because our controller actually needs the current view instance, but that view instance is asking the container to create the controller! By default, the container has no way whatsoever to resolve the IProductList dependency to the current view instance. So basically, what we want to do in this case is to manually provide the view dependency, but still have the container automatically provide the IProductsService dependency (hence the term ‘automanual’ which you have to admit is starting to sound pretty good at this point, right?). And of course, we want all of this to work automagically.

It turns out that Castle’s Windsor actually does have some slick tricks to make this work. Here’s how we can create the controller through the container from the view:

            IProductListController controller =

                Container.Resolve<IProductListController>(new { view = this });

Told you it was slick! It’s pretty easy actually… the parameter we pass to the Resolve method is an instance of an anonymous type with a view property. We set the view property to the current aspx instance (using the ‘this’ keyword obviously) and Windsor is smart enough to figure out that this value should be used to satisfy the view dependency instead of using it’s normal look-up mechanisms. The result is that our controller has a reference to the current view, and its IProductsService reference is resolved as the container would typically resolve dependencies. Pretty sweet.

Btw, i googled the term ‘automanual’ and sure enough, it already exists… but it’s not really used in the context of writing code so if this thing sticks, remember where you heard it first ;)

Posted in ASP.NET, Castle Windsor, Dependency Injection, Inversion Of Control | 2 Comments »

Storing data in the HttpSession

Posted by Davy Brion on 25th May 2008

As we all know (i’d hope!), storing data in the HttpSession is pretty bad. HttpSessions remain in memory long after (depending on the configured timeout) the user has performed his/her last action on your site. Think about that for a second… everything you store in the HttpSession will remain in memory for a while even though it has no chance of being used again, since the user is already gone. If you don’t have a large amount of concurrent users, this might not cause any noticeable problems. But you have to think about the multiplier effect. As your user base increases, so do your memory requirements. You’ll need more memory to keep serving those users. But if you store a lot of data in the HttpSession, your wasted memory will increase along with your increased user base. While you’re serving your active users, you’re still holding a lot of data in memory from users who’ve left your site 5 or 10 minutes ago. Because that unneeded data is still referenced from HttpSessions that will never be used again, it prevents the garbage collector from releasing that memory. When your memory usage gets to the point where the operating system needs to start paging, you’ll notice dramatic slowdowns in your site’s performance.

So you really want to limit the information you store in the HttpSession to that which pertains to the actual ’session’ of the user. Identification of the user, chosen language, stuff like that. But don’t use the HttpSession to store data that is related to the current screen the user is in. This is unfortunately commonly done, but this can easily lead to the problems mentioned above. It’s better to try to work as stateless as possible as this allows your application to scale to a large user base more easily. Don’t store retrieved data in the HttpSession, just retrieve it again when it’s needed. However, some kinds of data are very expensive to retrieve. In these cases, it might actually be better to store it in the HttpSession to avoid having to retrieve it excessively. So how do you avoid that these stored pieces of data have a negative impact on the memory usage of your application?

The Release It book mentions a great trick for this. I haven’t used it yet in an application, but it definitely makes a lot of sense. Instead of storing a reference to data in your HttpSession (and thus, preventing that data from being garbage collected until the HttpSession is garbage collected), you should use a ’soft reference’. A soft reference is kinda like a simple pointer to the data, but it does not prevent the data from being garbage collected. The soft reference doesn’t count as a real reference to the garbage collector, so when the collector needs to clear memory, the data you reference with a soft reference will be garbage collected (if it’s not referenced anywhere else that is…). If that happens your application code has to deal with it. In that case, you should retrieve the data again. Will this cause you to retrieve the expensive piece of data more times than you would have to if you had stored it with a normal reference in the HttpSession? Possibly. You can’t give a definitive answer to that question because it depends on how frequently the garbage collector needs to clear memory. But you will most likely benefit from possibly still having that data in memory. If it’s still there, great! Use it. If it’s no longer there, fetch it again. You’ll probably reduce the amount of times you need to retrieve it, but you’ll also avoid keeping the data from being garbage collected when the server is low on memory.

In .NET, you can use the WeakReference class to obtain a ’soft reference’. Lets demonstrate this approach with a quick-n-dirty example:

When you want to store data in the HttpSession, instead of doing this:

            IEnumerable<OrderView> outstandingOrders

                = orderManagementServiceProxy.GetOverviewOfAllOutstandingOrders();

 

            Session["outstandingOrders"] = outstandingOrders;

You could do this:

            IEnumerable<OrderView> outstandingOrders

                = orderManagementServiceProxy.GetOverviewOfAllOutstandingOrders();

 

            Session["outstandingOrders"] = new WeakReference(outstandingOrders);

And when you’d need to retrieve that data in a later request, you could do this:

            WeakReference reference = Session["outstandingOrders"] as WeakReference;

 

            if (reference != null && reference.IsAlive)

            {

                outstandingOrders = reference.Target as IEnumerable<OrderView>;

            }

            else

            {

                outstandingOrders = orderManagementServiceProxy.GetOverviewOfAllOutstandingOrders();

            }

Like i said, this is a quick-n-dirty example… normally you’d want to prevent direct access to the HttpSession and you’d probably write some kind of class that takes care of wrapping the reference in a WeakReference and unwrapping it from a WeakReference but this code is just to illustrate the approach.

Posted in ASP.NET | No Comments »

ObjectDataSource and sorting collections

Posted by Davy Brion on 10th September 2007

The ObjectDataSource control only supports automatic sorting if you bind it to a DataView, DataTable or a DataSet. If you’re binding collections to it, you need to provide your own sorting. Using Marc Brooks’ excellent DynamicComparer, this is actually a really easy task. In your ObjectDataSource definition, set the SortParameterName attribute to “sortExpression”. Then, implement the ObjectDataSource’s SelectMethod like this:

        public List<Order> LoadOrderList(string sortExpression)

        {

            return Sorter.GetSortedList<Order>(OrderService.GetAllOrders(), sortExpression);

        }

And the Sorter class looks like this:

    public static class Sorter

    {

        public static List<T> GetSortedList<T>(IEnumerable<T> collection, string sortExpression)

        {

            List<T> sortedList = new List<T>(collection);

 

            if (!string.IsNullOrEmpty(sortExpression))

            {

                DynamicComparer<T> dynamicComparer = new DynamicComparer<T>(sortExpression);

                sortedList.Sort(dynamicComparer.Comparer);

            }

 

            return sortedList;

        }

    }

And that’s all there is to it. Note: you have to make sure the SortExpression attribute of each sortable column contains the correct name of the property containing the data you want to sort on.

Posted in ASP.NET | 2 Comments »

ASP.NET control security gotcha

Posted by Davy Brion on 2nd September 2007

At work, we have a way of registering ASP.NET controls to be secured based on the current user’s application role. Securing the controls happens in the PreRender event of the MasterPage of the application. For a while, it seemed to work pretty good. Then we noticed an important bug: if we registered a control inside a TemplateField of a GridView, the control would lose its security settings when a user clicked on one of the column headers to sort the grid. For instance, if we registered an ImageButton that would delete the current row to be secured, and the current user’s role implied that the delete image should not be shown, the image would in fact be invisible. But when the user changes the sorting of the grid, all of a sudden the user can see the image.

I was the lucky one who got to fix the bug. First of all, i want to make it clear that i’m definitely not an experienced ASP.NET developer (i usually try to stay away from the presentation layer). Anyway, after a lot of debugging, i figured out that the GridView’s postback events were handled after the PreRender event of the MasterPage was handled. When clicking on a GridView’s column headers when sorting is enabled, the control performs a new DataBind. When looking at the source code of the GridView through Reflector, it shows that DataBind deletes the existing child controls and then recreates the new ones. Bingo! Our code set the control to be invisible, and after that, the GridView simply deletes its child controls and recreates it, thereby making our ImageButton visible again.

That took me about 5 hours to figure out. An experienced ASP.NET developer probably would’ve figured this out much more quickly or even would’ve avoided the issue by securing the control in a later stage of the page’s lifecycle. Anyway, i fixed the bug by assigning a delegate to the PreRender event of each control that had to be secured, and then simply set the correct settings for the control at that time. It didn’t feel like a good fix, but i couldn’t find anything better at that time. So today i figured i’d play around with that issue at home, and it turns out the solution is actually much simpler and cleaner than what i did at work. The best way is to simply secure the controls in the OnPreRenderComplete method of the Page. This way you’re sure that your code is executed after each control has finished its lifecycle. So you can simply hide or disable controls during the OnPreRenderComplete method of the page without worrying that the controls will be destroyed and created again. If you must secure the controls in the MasterPage instead of the Page itself for whatever reason, it’s best to simply assign a delegate to the content page’s PreRenderComplete event during the MasterPage’s Page_Load() method and then secure the content page’s controls in the event handler.

Posted in ASP.NET | No Comments »