Data Access Confusion/Pain

I recently had an idea to retarget my blogging efforts to address confusing/little-known scenarios and pain points with LINQ to SQL and the Entity Framework. Since I’m using these technologies just about all the time now I have noticed that there is no end to the amount of discussion they bring up—from high-level architectural concerns like supporting n-tier and IoC to low-level implementation-specific questions like how to use the Attach family of methods in both technologies.

I’m going to prioritize my posts based on feedback I get personally (from here and other sources) and what I see on Twitter; KristoferA challenged twitterers (is that right?) to tweet their pains with both technologies with #linqtosqlpain and #efpain. I certainly have a few of my own that I want to address too.

(And yes, I have joined Twitter, too!)

Don’t Change Your Firefox Add-On’s ID!

That is, unless you want to experience pain.

Two weeks ago I blogged about how someone had taken an add-on of mine that was in production and uploaded a derived version to https://addons.mozilla.org (AMO) without changing its ID. This means that because the AMO web site identifies add-ons by their ID, it would not allow me to upload my add-on. I tried to solve this problem by engineering a solution to change my add-on’s ID.

I thought this would work and even provided some sample code for my ideas in my previous blog entry; however, there were just too many problems to overcome.

The sample code might work if Firefox’s UI gave users a pleasant experience when an add-on’s automatic update facility installs an add-on with a different ID. (I’m not blaming Firefox here; it’s probably not a supported scenario.)

Instead this is what the user sees:

image 

Compare this to what they should see:

image 

Notice anything missing? 5 points if you said the "Restart Firefox" button. It’s pretty important.

"OK," I say, full of resolve, "let’s just create an update with the same ID that downloads the new version for them." Essentially, we use an update with the same ID to get around the problem laid out above, but all that update does is to download the newest version of the add-on automatically. Finally, it uninstalls itself and then restarts Firefox. Here’s what I envisioned for the user experience:

  1. Joe Bloggs sees there’s an update for one of his extensions. Firefox guides him through the update process, at which point he sees the picture above to restart Firefox to complete his changes.
  2. Joe clicks "Restart," and as soon as this intermediate version of the extension springs into action and downloads the newest version of the extension. Firefox doesn’t have time to show its windows again before the download completes, and Firefox is forced to restart by the intermediate extension.
  3. Joe sees his windows and tabs being restored, and he has the newest version of your extension.

Unfortunately, it never quite worked out that well. This is closer to the end user experience:

  1. Joe sees there’s an update for one of his extensions. Firefox guides him through the update process, at which point he sees the picture above to restart Firefox to complete his changes.
  2. Joe clicks "Restart," and as soon as this intermediate version of the extension springs into action and downloads the newest version of the extension. Meanwhile, Joe sees his windows and tabs being restored. All of a sudden, all of his windows disappear.
  3. Joe at this point could try and relaunch Firefox, but that wouldn’t work since Firefox is technically already restarted. If he waits long enough he’ll see Firefox return, but all of his windows and tabs have gone.

Whether these were technical limitations of Firefox itself or problems in my code I never confirmed. What I knew was that the investigation had cost a lot of time, and I was very nervous about deploying something that could potentially frustrate a large percentage of my user base.

Turns out I made the right call. Nick Nguyen, who was my liaison at AMO, allowed me to take over the extension that used my add-on’s ID. No more problems, and definitely no more hitting my head against a brick wall.

So if you have deployed an add-on publically, and you’re thinking of changing its ID, don’t do it. There’s a better way to achieve what you want.

Burned by AMO and OSS (An Idea on How to Change Your Firefox Add-On’s ID)

I’ve helped to build a Firefox add-on called NAMFox over the past couple of years to help refine my skills with JavaScript and provide an avenue for more technical challenges. (Of course, I also like to provide value to customers, but that’s not the focus of this blog entry.) For add-on developers, AMO (https://addons.mozilla.org/) is THE site to use to host your add-on and put your work out there for other people to use. Of course, you can always host the add-on on your own server, but if you want integration with Firefox’s add-on searching (see below) or an easy way to manage automatic updates, then you’ll use AMO.

Firefox Add-On Search

The Situation

In early 2008 I wanted to upload NAMFox onto AMO for the reasons mentioned above, and because the server it was already hosted on wasn’t very reliable. I set up a new account and went through the process to upload my add-on, when I encountered an error saying that I didn’t have permission to upload the add-on.* At the time I thought it was because the ID of my extension (superfastoz@neoseeker.com) didn’t match the email I registered with. (This was not the case; read further….) I gave up on trying to host it at AMO and continued using the same server the product had always used.

…Until a year later, when after some significant code rewrites I decided to try my luck again with AMO. I got the same error message and decided to investigate further. This time I discovered that it was actually because an add-on with the same ID already existed on AMO. I certainly didn’t remember uploading it before, and the previous project owner professed that he didn’t upload it on AMO either. I began writing a piece of software to download all the add-ons on AMO and check their IDs for conflicts, but I figured it would be a much more efficient use of my time to enlist some at Mozilla for help.

Enter Nick Nguyen, who helped me figure out that another user had uploaded an add-on with the same ID as mine. I was a bit disappointed; then I figured out that someone had taken NAMFox back in 2007, amended it to work with GameFAQs instead of Neoseeker, and then uploaded it to AMO with the same ID. I was flabbergasted. I had wasted so many hours trying to get NAMFox onto AMO, only to find this. Unfortunately all Nick and I could do was to email the original developer of that account to see if he would be willing to relinquish ownership of the project. Unfortunately I can’t just take it over, even though there is currently no download on the page, nor have there ever been (0 total downloads since 2007). If we didn’t hear back from the developer in a couple of months, I could perhaps take over the project then.

Well…

  1. We haven’t heard back from the developer, and it’s been about two weeks now.
  2. A couple of months is such a long period to go without a release or an update, especially since the add-on enhances a web site which is prone to change (and break NAMFox) without notice.

There was another alternative—I should change the ID of my add-on. Then I could easily upload it to AMO for people to use. There are a couple of problems with this:

  1. Firefox treats two different add-on files as the same add-on if they have the same ID and as two separate add-ons if they have different IDs. This means that if anyone who had the old NAMFox installed downloaded the new version with a different ID, their Firefox would have two instances of the add-on running, the old one and the new one.
  2. The same problem occurs with automatic updates. I can tell Firefox to download this updated NAMFox with a new ID, but now the customers that download the updates will have two instances of the add-on running.

In theory there is a way around this limitation, but I haven’t tried it…

The (Tentative) Solution

DISCLAIMER: I have not used the following techniques/code when releasing my add-on into the wild, so you should be careful if you’re going to adopt it.

UPDATE: Don’t try this! My Findings

The great thing about Firefox is that you can control just about every part of the browser, including the add-ons that the users have installed. If you’re like me, you’re probably thinking you can use this to get around the two limitations I listed above. In fact, I believe we can.

We’ve already identified the two scenarios that users can get your add-on with a new ID: through a direct download and through automatic updates.

If a user doesn’t have your add-on installed already, then we can agree that there is no problem.

If a user does have your old add-on installed already, then the new add-on must recognize this and uninstall it. This action is required regardless of whether the user used automatic updates to install the new add-on.

Turns out this is actually quite simple and only requires a few lines of code:

var oldId = "superfastoz@neoseeker.com";

var extensionManager = Components.classes["@mozilla.org/extensions/manager;1"].

    getService(Components.interfaces.nsIExtensionManager);

var oldAddon = extensionManager.getItemForID(oldId);

 

if (oldAddon) {

    extensionManager.uninstallItem(oldId);

 

    var nsIAppStartup = Components.interfaces.nsIAppStartup;

    var appManager = Components.classes["@mozilla.org/toolkit/app-startup;1"].

        getService(nsIAppStartup);

    appManager.quit(nsIAppStartup.eForceQuit | nsIAppStartup.eRestart);

}

This code exists in the new add-on; for me it runs in an event listener for the window’s load event. It tries to find the old add-on with a certain ID. If it exists, it’s uninstalled and Firefox is restarted to commit the change. When Firefox restarts, only the new add-on remains.

I’m planning to try this soon. I’ll update this post with the good, the bad, and the ugly after I do.

*The AMO team did a great job of cleaning this error message up. You’ll now see this message, which is much clearer.

image