I ran into a crazy issue while working on my ASP.Net 2.0 application in Firefox this week and thought I'd share...
When a certain ASPX page completed rendering and emitted it's final markup to the browser, I noticed the status bar read 'Done' for an instant, but then immediately changed to 'Waiting for localhost...' again. It definitely seemed as if the page was being requested twice. I immediately looked at FireBug to see what was loading, and indeed the same page showed up twice in the 'Net' listing. To see it in action, I ran the site in DEBUG mode and put a break on Page_Load. Sure enough, the page WAS being requested twice. IE(7) worked just fine. It was not behaving in this odd manner, so I set out to figure out what was causing this strange behavior in Firefox.
Two Browsers - Two Different Behaviors
After many Google searches, I finally found a few message board posts talking about pages being loaded twice. It seems that an empty src attribute (like in an <img> tag) will cause this undesirable behavior in Firefox. The browser's rendering engine sees an empty src attribute and tries to put the current page name (URI) in it's place.
So, when Firefox sees this...
<IMG id="fooImage" src="" />
...it substitutes the empty attribute value like this...
Firefox
<IMG id="fooImage" src="http://localhost/foo/bar.aspx" />
Inserting the current page within the src attribute will of course cause the browser to request the page once again. That's why I was seeing the 'waiting for localhost...' browser status message fire up again after the page had already visually rendered. IE (7) handles the empty src attribute by adding '/null' rather than the actual page name, like this...
IE7
<IMG id="fooImage" src="http://localhost/foo/null" />
AJAX Extender Control To Blame
The reason this strange behavior started to occur was because I recently began using the ASP.Net AJAX DropDown extender on the problematic ASPX page in question. The DropDown extender has several properties that you can configure to fit your display needs. One of these properties is the 'DropArrowImageUrl'. Since I didn't have the need to override this image, I didn't assign a value to this property. When ASP.Net renders the image tag for this control, the src attribute is left empty because I didn't specify a particular image.
The Fix (Hack)
It was clear that some sort of valid image needed to be set on this control in order to prevent Firefox from requesting the page twice. So we plugged in a 1 pixel transparent image and the problem went away. I've provided a few links below to a good blog post and a couple forum threads that discuss the issue and helped point me to a solution. In the blog post there's a comment about the URI specification and how Firefox seems to adhere to the specification - which doesn't really make sense for <img> tags.
Either way, it's silly that I had to go through this in order to prevent a (potentially expensive - depending on the page in question) needless page request. The AJAX extender controls have some nice behaviors that can really help out your UI development. But since these controls are fairly new, we should be mindful of potential side effects our sites may experience due to the fact that these controls probably have not been as rigorously tested as the standard ASP.Net controls have.
Links
http://brian.pontarelli.com/2006/05/02/is-your-browser-requesting-a-page-twice/
http://forums.asp.net/t/978268.aspx?PageIndex=2
http://forums.mozillazine.org/viewtopic.php?p=3022038&
I've received a handful of comments/questions surrounding part of the code sample from my previous post.
The comments I received have to do with the few lines of code in the Authentication WebService that included a custom interface and a call to a third party tool. Here's the code in question:
public class Authentication : WebService
{
private IWebContext _webContext = ObjectFactory.GetInstance<IWebContext>();
[WebMethod]
public bool IsLoggedIn()
{
return this._webContext.IsUserAuthenticated;
}
}
Create An Abstraction
IWebContext is an interface I created for use within my web site that abstracts calls to lower level ASP.Net stuff like QueryString, Session, and Identity thru the HTTPContext object. Often I want to have a testable object in an ASP.Net web project that will depend on something at the ASP.Net lower level. The way to put a seam in my code that decouples the object I want to test from the ASP.Net stuff is to abstract it away...into something like IWebContext. Here's the IWebContext interface definition:
public interface IWebContext
{
bool IsUserAuthenticated { get; }
void Clear();
void Save(string key, object value);
object Get(string key);
string GetQuerystringValue(string key);
void Set(string s, object value);
}
Dependency Inversion / Inversion of Control
Regarding the ObjectFactory.GetInstance<IWebContext>(); line of code -- to help with decoupling, I use the Dependency Inversion Principle (DIP). Another name for DIP is Inversion of Control (IoC). The DIP has been proposed by Robert C. Martin. It states that:
High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions.
In short, strive to make your classes depend upon abstractions, not concretions. When you see the keyword 'new' - that's a tight coupling to a specific concrete type. If you cannot easily isolate an object from it's dependencies, and if this makes it more difficult to test, then that's a tight coupling that needs to be addressed. You need to invert the dependencies.
The Flavors of DIP - Inverting with StructureMap
There are two main flavors of Dependency Inversion - Dependency Injection and Service Location. Beyond that, Dependency Injection has two main forms - Constructor Injection and Setter Injection. Constructor Injection is when an object is instantiated with a dependency. Setter Injection is when a dependency is provided through some sort of setting method/property.
The other main form of Dependency Injection is Service Location. Using a method like StructureMap's ObjectFactory.GetInstance() is another way to locate a service/dependency. In the code sample, the ObjectFactory object lives in a third party tool called StructureMap ( http://structuremap.sourceforge.net ). We ask StructureMap.ObjectFactory to return an instance of a certain type (i.e. IWebContext). This effectively inverts the control of resolving dependencies from the object needing to use the dependency to a third party, adhering to the DIP, and promoting looser object coupling.
If you are interested in learning more about the open source project StructureMap, visit the project page on SourceForge at http://structuremap.sourceforge.net/. I won't try and detail all of the implementation details of StructureMap, but here's a quick rundown of the basic usage.
StructureMap allows us to decorate abstractions with a [PluginFamily] attribute, as well as a [Pluggable] attribute on the types that implement the abstraction. When a call is made to ObjectFactory.GetInstance<IWebContext>(), StructureMap finds the classes that implement the type being requested and resolves, instantiates, and returns the correct concrete instance. Let's look at an example:
Interface: This is the IAccountRepository interface, decorated with the PluginFamily attribute and key name of 'Default'.
1: [PluginFamily("Default")]
2: public interface IAccountRepository
3: {
4: BillToAccount[] GetBillingAccountsForUser(User user);
5: ShipToAccount GetShipToAccount(AccountIdentifier accountIdentifier);
6: BillToAccount GetBillToAccount(AccountIdentifier accountIdentifier);
7: }
Default Implementation: Here we have an implementation of the IAccountRepository interface decorated with the Pluggable attribute and a key name of 'Default'.
1: [Pluggable("Default")]
2: public class AccountRepository : AccountRepositoryBase
3: {
4: ...
5: }
Cached Implementation: Here we have another implementation of the IAccountRepository interface decorated with the Pluggable attribute and a key name of 'Cached'.
1: [Pluggable("Cached")]
2: public class CachedAccountRepository : IAccountRepository
3: {
4: ...
5: }
When code needs to make use of a dependency like IAccountRepository, it can ask ObjectFactory for the default instance in this way:
_accountRepository = ObjectFactory.GetInstance<IAccountRepository>();
You can have StructureMap return specific implementations of IAccountRepository in a couple easy ways: you can set the PluginFamily attribute's key on the interface to match that of the desired "default" Pluggable instance like this:
1: [PluginFamily("Cached")]
2: public interface IAccountRepository
3: {
4: ...
5: }
...Or you may ask for a particular instance by name, like this:
_cachedAccountRepository = ObjectFactory.GetNamedInstance<IAccountRepository>("Cached");
Dynamic Behavior / Configuration
StructureMap also allows you to specify type configurations (and all sorts of other behaviors your system might need) in a config file. Some may prefer the config file method because it's a little more obvious (in a file, rather than living inside the code at various locations) as to what objects implement certain interfaces and what the default behavior is for a particular abstraction. Having these settings in a config file can also be helpful when you have an automated build that might require different system behavior (i.e. different implementation of various abstractions) based on the particular environment you are deploying to or maybe the state of an entire subsystem.
When deploying your system, behavior can be switched around by having your build tool like NAnt issue an 'XMLPoke' command to change key names inside the StructureMap.config file. Also, your application may need to behave differently based on some runtime situations like database or service availability (think 'offline mode'). This can easily be achieved by using a tool like StructureMap. Simply request a different implementation of a type using the 'ObjectFactory.GetNamedInstance' method as shown above and your system can gracefully adapt to any unexpected environmental situation.
Hopefully I've shown here the importance of abstracting away the factors that can make code more difficult to get under test, as well as a couple mechanics on how to approach the problem.
/bc