Showing posts with label jQuery. Show all posts
Showing posts with label jQuery. Show all posts

Wednesday, October 30, 2013

Divorcing the UpdatePanel for Asynchronous Postbacks in Web Forms Isn't Easy

Point MVC vs. web forms here in the effort to make ajax calls to the server for asynchronous postbacks. This experience made me realize how nice MVC is from this respect I'm about to highlight.

For years I used ASP.NET UpdatePanels in web forms development and overall they have done the job. However, anyone that has used them on mildly complex pages knows, you can't just have 1 UpdatePanel. Nope, you need 2 with the 2nd one having a trigger pointing to the 1st so partial updates will work properly. The end result: a tangled web of UpdatePanel goo.

Recently I decided to use jQuery making AJAX calls to ASP.NET page methods instead as an alternative to UpdatePanels. The jQuery in it of itself may not be 100% cleaner but it works really well and is straight forward. So I create my ASP.NET page method, write some jQuery to make a call to my new page method asynchronously, and walla I have bypassed the UpdatePanel, yeah!

Not so fast...

Caveat #1: Page Method Must Be Static. This will by the way not be ideal for anyone doing Dependency Injection as you will have to Publicly resolve dependencies through methods like: Global.GetInstance()

Caveat #2: Can't see my server side controls 
from the static method called (because of #1 above and how it's called from the client).

Caveat #3: Normal postbacks later that access or post bound clientside elements complain of security and EnableEventValidation issue.

Caveat #4: Be prepared to query element values via Request.Form and not through control instances.

End result: A worse mess than using an UpdatePanel.

Recommendation: If you are doing ASP.NET web form development here are the (3) choices I recommend:

  1. Deal with full postbacks like it's 2002
  2. Use an UpdatePanel for asynchronous postbacks and don't mix client side data binding asynchronously with JavaScript.
  3. Ditch ASP.NET web forms and do MVC (hey no hater here, I stick up for web forms a lot - just know how the tool operates)
I want to show through an example all of what I just described. So 1st, lets look at a simple example to populate a ASP.NET ListBox server control's items using an AJAX call to the server to just see how this works. We'll make a DropDownList that contains the 'Periods' during a school day. Upon selection we want to have the classes populated in the ListBox. The goal is to do this without having a full postback or use an UpdatePanel.

First a simple set of server controls:

<form id="form1" runat="server">
    <div>
       <asp:DropDownList ID="DropDownList1" runat="server">
          <asp:ListItem Text="Please Select..." Value="0"></asp:ListItem>
          <asp:ListItem Text="1st Period" Value="1"></asp:ListItem>
          <asp:ListItem Text="2nd Period" Value="2"></asp:ListItem>
       </asp:DropDownList>
    </div>
    <div>
       <asp:ListBox ID="ListBox1" runat="server" Width="400px" SelectionMode="Multiple"></asp:ListBox>
    </div>
    <div>
       <asp:Label ID="Label1" runat="server" Text=""></asp:Label>
    </div>
    <div>
       <asp:Button ID="Button1" runat="server" Text="Submit" OnClick="Button1_Click" />
    </div>  
</form>


Next we need a Static server-side event with the [WebMethod] attribute added that our jQuery we will make can make an AJAX call to and will return a collection of 'School Classes' for the 'Period' selected:
public partial class Default : System.Web.UI.Page
{
   protected void Page_Load(object sender, EventArgs e)
   {
   }

   [WebMethod]
   public static List<SchoolClasses> DropDownList1_SelectedIndexChanged(string id)
   {
 if (id == "0")
 {
    return null;
 }

 //Generic code representing call probably to retrieve values from the database
 var classesPeriod1 = new List<SchoolClasses>()
    {
  new SchoolClasses(){ID = 1, Name = "Math 101"},
  new SchoolClasses(){ID = 2, Name = "Science 101"},
  new SchoolClasses(){ID = 3, Name = "Social Studies 101"},
  new SchoolClasses(){ID = 4, Name = "Spanish 101"}
    };
 var classesPeriod2 = new List<SchoolClasses>()
    {
  new SchoolClasses(){ID = 5, Name = "Art 101"},
  new SchoolClasses(){ID = 6, Name = "Music 101"},
  new SchoolClasses(){ID = 7, Name = "English 101"},
  new SchoolClasses(){ID = 8, Name = "Global Studies 101"}
    };

 List<SchoolClasses> results = null;
 if (id == "1")
 {
    results = classesPeriod1.ToList();
 }
 if (id == "2")
 {
    results = classesPeriod2.ToList();
 }

 //this.Label1.Text == "You selected a id = " + id;

    return results;
 }

   protected void Button1_Click(object sender, EventArgs e)
   {
      var selectedItems = this.ListBox1.Items.Cast<ListItem>().Where(li => li.Selected).ToList();
      //The collection is empty?? There were selected items though...
   }
}

public class SchoolClasses
{
   public int ID { get; set; }
   public string Name { get; set; }
}

Lastly, we need the jQuery that is wired up to the 'change' event of the DropDownList that will call our new Static event server-side and populate the ListBox with the results.
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
   <script type="text/javascript">

      $(document).ready(function () {
         DropDownList1_OnChangeHandler();
      });

      function DropDownList1_OnChangeHandler() {
         // Add the page method call as an change handler for DropDownList1.
         // This will call the static page method, and use the returned results to populate ListBox1.
         $('#<%=DropDownList1.ClientID %>').change(function () {
            var val = $(this).val();
            var text = $(this).children("option:selected").text();
            var $timePeriod = $('#<%=ListBox1.ClientID %>');
            $timePeriod.attr('disabled', 'disabled');
            $timePeriod.empty();
            $timePeriod.append('<option value="0">< Loading Please Wait... ></option>');
            $.ajax({
               type: "POST",
               url: "Default.aspx/DropDownList1_SelectedIndexChanged",
               contentType: "application/json; charset=utf-8",
               dataType: "json",
               data: "{'id':'" + val + "'}",
               success: function (schoolClasses) {
                  $timePeriod.removeAttr('disabled');
                  $timePeriod.empty();
                  if (schoolClasses.d == null) {
                     return;
                  }
                  $.each(schoolClasses.d, function (i, schoolClass) {
                     $timePeriod.append('<option value="' + schoolClass.ID + '">' + schoolClass.Name + '</option>');
                  });
               },
               error: function () {
                  $timePeriod.empty();
                  $timePeriod.append('<option value="0">< Error Loading classes ></option>');
                  alert('Failed to retrieve classes.');
               }
            });
         });

      }
   </script>


If you built this example and ran it, the ListBox would populate as we would like. In fact it's really fast too. Originally I thought, "Home Run!" So what's the problem? Well if we have a simple example this might work, but as we need to add to the page, we begin to run into hurdles.



Let's try to add in (2) more features to highlight what I'm talking about:

  1. Update a label to state which 'Period' we selected when calling server-side Static method.
  2. Iterate through the 'Selected Items' collection on submittal of the form
OK, I know what you might be thinking thus far; EASY! I've done this a million times. Well, let's dig in and look at some of the challenges I listed at the beginning of this post.

First, add this line to our Static server-side method:
this.Label1.Text == "You selected a id = " + id;

Next, add this code to the server-side button click event to iterate through the selected items.
protected void Button1_Click(object sender, EventArgs e)
{
  var selectedItems = this.ListBox1.Items.Cast<listitem>().Where(li => li.Selected).ToList();
  //The collection is empty?? There were selected items though...
}


Now try to run this code. There is already an error with trying to access the Label's .Text property. Why? See #1 and #2 in the original list of issues. In a static method on the page, I can not see or have access to the Page's instance. Makes sense actually. I making a quick call to the server, but not in the context of how ASP.NET would expect, but rather from the client. Still completely legal to have the Static method, just will not behave as we might have hoped.

Comment out the the code to change the Label's .Text property. Now run the page, select some values in the dropdown and press the 'Submit' button. This time we have another issue; a hard error stating: "Invalid postback or callback argument." Why? See #2, #3, and #4. The server did not originally send the data to populate the ListBox, so it is unaware of it. This will cause a security error unless the page's EnableEventValidation="false" property is set. However I don't like to do this because it opens up a security risk and would not be something I recommend in production applications.

However if we do set to false EnableEventValidation, then we still can't see the values from our control, because the Server did not provide them and has no knowledge of them. How do we get them? Well you could use the Request.Form values and manually dig through the posted form to scrape out the elements. At this point, I'm thinking... Yuck.

So in order to make my AJAX call work to interact with the server using asynchronous postbacks and avoiding the UpdatePanel I have to deal with the following:
  • Reduce the page's security by setting EnableEventValidation="false"
  • Not be able to see any of the control values I manipulated through their server-side properties.
  • Have to dig through Request.Form to manually find updated client values
  • Not be able to use Dependency Injection as intended
  • Have a disorganized mismatch between the client and server state since the right-hand doesn't know what the left is doing.
At this point I think, is an UpdatePanel so bad compared to all of what I just discovered? Let's review the UpdatePanel code again:
<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">
    <ContentTemplate>
 <asp:ListBox ID="ListBox1" runat="server" Width="300px" SelectionMode="Multiple" AutoPostBack="True" OnSelectedIndexChanged="ListBox1_SelectedIndexChanged" />
    </ContentTemplate>
</asp:UpdatePanel>

Nope, that actually was not so bad. So the end result for a web forms applications was I stuck with the Update Panels and everything worked well in regards to asynchronous postbacks. As I mentioned at the beginning, I think this is where MVC really shines, because there is no state being tracked and updated by the server and causing at least a portion of this mess because of the mismatch between the client updates happening independent of the server knowing about it.

Hopefully you try all of this out and see the caveats of trying to do AJAX asynchronous postbacks to the server via jQuery before going to far down the road on a new project hoping this will work smoothly. There are as I've shown several 'pitfalls' to be aware of so just make sure you factor them in if going down this route.

Friday, December 14, 2012

Visual Studio LIVE! Orlando Day 5 - All about MVC 4


1 more day to go and I'm looking forward to a full day on MVC! I have become a big fan of MVC over the last year, previously being a web forms developer for the past 9 or so years. The agenda looks great (this is not some intro course) so let's get to it!

Mastering ASP.NET MVC4 in Just One Day
Tiberiu Covaci, Microsoft MVP and Senior Trainer & Mentor


I'm looking at the agenda for today and it looks like we will be covering soup to nuts on MVC. Obviously there are a lot of web forms developers in attendance that are seeking out knowledge on MVC, so there has to be a general introduction to 'Model-View-Controller' As always the typical diagram displaying the general interaction between the parts is displayed below:

If you are new to MVC there are some goals, even say 'advantages', to this framework. Tibi mentioned testability, tight control over markup leverage the benefits of ASP.NET 4, and  conventions and guidance. I actually spoke in depth to some of these in a post I did a couple of years ago and you can look at it here: ASP.NET Web Forms vs. ASP.NET MVC 

One of the other main features of MVC is its extensibility. ASP.NET MVC is actually open source, so modifying it to your needs presents an endless about of customizations  For example, you don't have to use just the .aspx or Razor View engines. Others are available like Spark if you want to use it. Personally I'm a big fan of the Razor view engine out of the box and it fits my needs. However it's nice to have that flexibility if needed. Now with NuGet, you can get a slew of different packages to help with development. In fact if you strictly work with blinders on from only using out of the box MVC functionality, you are probably missing out on something that will make developing your application easier, How do you find out about the packages available  Blogs, conferences, magazine article, word of mouth from experience, etc.

It's always tough when explaining to those new to MVC which piece to explain 1st (Model-View-Controller). Starting with the Model is a good idea as you can really think of these as your old business logic layer classes. On this note a few of the architectural patterns were brought up that can used in MVC: Repository, DDD (big fan), and service pattern. If you are not familiar with DDD, in a nutshell the focus of the architecture is on the business domain, and after all this should be the focus of the application. Someday I would like to see a MVC implementation using DDD, but today we will be using the Repository pattern which I am also a big proponent.

The example used today had a single model class named 'Course'. This is where the getters/ setters are for the object and any method calls not directly related to persistence  The repository pattern is responsible for the interaction with the persistence layer (i.e. Entity Framework) and wraps the basic CRUD methods. To keep the focus on MVC, there was not any true implementation in the repository to fetch data, and the data was loaded from static data in a mock layer named 'TibiPublicSite.MockRepository'. The CourseRepository implemented ICourseRepository, and returned hardcoded/mocked up data. Works perfectly because this is not a session on EF or other ORM.

One thing I should mention that was being done in the solution referenced throughout the day was the layering of the application. You will notice by default that an MVC project will have all of the pieces in a single solution. This is ok and I recommend for beginners to just stick with this until they get the hang of the method calls and flow. However, MVC can be considered a and architecture for a UI only and this is why separating out the additional layers is a good idea. Here are the layers from today's application:

  • TibiPublicSite (MVC)
  • TibiPublicSite.MockRepository
  • TibiPublicSite.Models (contains repository interfaces and model classes)

The next key piece of the MVC architecture are the 'Controllers'. These are the server side classes, and each MVC request maps to a Action method in a Controller class  Remember, MVC uses routing, and the URL route is dictating an action on the controller to be executed. The classes here implement 'IController'.

An Action is a method that returns a ActionResult. ActionResult is the base class, but there are other result types as well like ViewResult, RedirectResult, and JsonResult to name a few. When you add a controller, you can actual scaffold it based on an existing class (make sure to build your project 1st so they are available for selection  This will create many of the action methods on the controller already needed based on the model class selected.


Using Dependency Injection on the Controller is a great was to loosely couple the persistence mechanisms from the controller itself. When the controller knows too much about the actual persistence details, you create tightly coupled code that is less reusable and more difficult to maintain in the future. Having the controller have intimate knowledge and making direct calls to the database is not a good idea. The repository is injected into the interface on the controller and is accessible by the methods on the class. It's better to call _repository.Save(myObject), rather than having to know how to directly work with the Entity Framework context, ADO.NET code, or other persistence mechanisms. You might be thinking that you could still push persistence details down to the model, but by injecting the repository into the Controller, you set yourself up much better for unit testing.

However to take this a step further in reality there is not a 1 to 1 relationship between the View and the Model. Therefore we will be using customized classes that contains a combination of data that may span > 1 model class. These classes are named 'ViewModels'.

HTTP verbs are decorated on controller actions in the form of attributes. The HttpGet is not required as it is the default. You can still add it for consistency and to be explicit. If you try and call an action with the incorrect verb it will obviously not get called.

It is important in controller actions to check if (ModelState.IsValid) to ensure model binding worked correctly.

Routing is a part of ASP.NET System.Web.Routing. /Bikes/MountainBike is much better that Products.aspx?Item=MountainBike  Routing is defined in Application_Start() and contains one default route {controller}/{action}/{id}. The ordering it is listed in the class is important. The routing process is to trickle down top to bottom to find the 1st match. If you have a match that was unintended, you might get the wrong controller action.

You know sandwiched here in the middle I have to say how exciting some of these technologies are to work with. Take a look at the following line of code:

var model = _repository.GetByCriteria( c=> c.Title.Contains(searchTerm));

MVC, Lambdas, Dependency Injection, entity framework, dynamic typing. One line of code that does so much would have taken many times the effort if not using these technologies. This is why it's so important to stay current, because otherwise you may be working 10x the effort when there is a better way.

Route debugging can be aided by a NuGet package created by Phil Haack named 'RouteDebugger'. This is a fantastic tool that when navigating to a URL, you will get the bottom portion of the browser breaking down the route and which patterns it matched and any constraints  This will help for what I discussed previously about trying to determine which route from Application_Start that the URL matched. Just remember to disable this in configuration before deployment or when not needed:

You know he started talking about web.config transformations and this is something I'm embarrassed to say I have not used. The result, over complication in code or going back and toggling values between test and production. Essentially is does a XSLT transformation on the web.config file for both 'Debug' and 'Release' scenarios. Need to start using this.

Views are another major component of MVC. They are as close to a page as it could get. Strongly typed views inherit from ViewPage for aspx pages. If using Razor, add @model directive for razor pages.

The Razor View Engine syntax is slick. I actually did not start using MVC until v3 so I never got into the .aspx engine syntax. In fact if you were driven away from MVC because it looked like classic ASP, give it another look and try out the Razor syntax. Razor syntax uses HTML helpers which can be accessed in the view via Intellisense by starting with the '@' symbol. Remember when adding a new Razor view, the difference between a 'Partial' view or not is the inclusion of the _Layout view. Partial views will not include it.

Tibi forgot to add the @RenderBody() helper to _Layout.cshtml (think of the _Layout view like a Master Page). This helper is required and is analogous to the ContentPlaceHolder server control in a MasterPage in web forms. The @RenderBody method indicates where view templates that are based on this master layout file should “fill in” the body content.

Make sure to declare the model directive at the top of the view like @model IEnumerable. This will allow strongly typed access to the 'Courses' data within the HTML helpers in the view. If you want to remove the redundant namespace declaration, add it to the web.config as an imported namespace. It can then be changed to @model IEnumerable. Even using a 'using' statement in the view at the top is acceptable as well.

The razor syntax HTML helpers flows very nicely in with existing HTML. With razor syntax JS is added in a section called @section scripts{} and CSS is in a section called @section styles{} You can also use the @RenderSection("scripts", false) helper in the _Layout view to dictate a section in other views. If you use JS or CSS on most views, this will keep it consistent in placing.

For those new to MVC and the Razor engine the HtmlActionLink will be your friend for making links on a page that route to controller actions. Remember there is no physical page on the server (like web forms). We have to route to an action on a controller to execute our code on the server. An example of this is the following: @Html.ActionLink("Start", "Start", "Courses"). This makes a link named 'Start', that will route to the 'Start' method on the 'Courses' controller. Remember MVC is based on convention and not configuration like web forms so you do not have to write 'CoursesController' because 'Controller' is redundant and known.

If you want to pass parameters you would pass them as object routeValues: @HtmlActionLink("Details", "Details", new{id = item.id}, new{@class  = "abc"}). Notice using '@' in front of class because 'class' is a reserved keyword.

The @using (Html.BeginForm()) wraps where the form starts and begins, and uses IDisposable to be disposed when not used anymore. 

Model Binding is probably my favorite thing in MVC. Remember the days of having to explicitly pull all form values server side to send off for manipulation (i.e. SaveValues() or something)? Not anymore. Model Binding will automatically send form values back to the controller and populate the object in the controller method parameter list. This saves on a lot of redundant code. I guess I get more excited about this because I did web forms for so many years and would sometimes have large methods to scrape values off controls into a business object. 

To add to how easy this all wires up, you can scaffold the View off a Model or ViewModel class and have all the markup created for you! Again, this was a lot of manual work in web forms (unless using a FormView or something but the server side operations had a heavy footprint and got messy quick). When scaffolding, you can select the class and type of View to create. The screen below shows how I scaffold a 'Create' View for 'Courses' model class:


Want even more streamlined? Use @Html.EditorForModel() on the view for the bound model. What you get from a single line is the entire form of fields to edit! One line, are you kidding me? Again, us old web forms folks appreciate this a a lot. By the way if you are wondering how you might exclude or control which fields show up on the form, because maybe you don't want SSN shown or at least disabled. You can control this by using data annotations on the model properties that the fields map to. Have a look at the System.ComponentModel.DataAnnotations namespace for the fields available. 

The annotations also work hand in hand with the jQuery validation scripts. Remember having to explicitly add all the RequiredFieldValidators or JS clientside? Not anymore, because ASP.NET will use these annotations to automatically do fields validation based on the presence of the jQuery validation scripts. In fact, these scripts will already be in your project upon creation, so you have to do very little to get it all working.

Annotations do (even with me) seem a bit like decorating values for UI purposes a few layers down which might be viewed as improper. An alternative is to create a MetaData class that abstracts away the annotations. So I might create a class called 'public class CourseMetadata' and decorate the model class with [MetadataType(typeof(CourseMetadata))]. Just remember the annotations are good for EF as well if doing a 'Code First' approach as it will define the constraints on the fields in the database. I do admit abstracting away the annotations into a MetaData class is pretty nice and probably something I will implement in future applications. The only downside might be developers coming behind not initially seeing the annotations where they expect them (on the Model class). This is why commenting never hurts. Just add a little note in the XML block on the class.

If you happen to being doing mobile apps, you could add separate versions of the View using the nomenclature 'index.mobile.cshtml'. This is a nice way to differentiate views. I would not be surprised in the next 2-5 years that a feature is available to streamline this duplication of Views between desktop and mobile Views. Today you still have to create separate views. Remember (from the other day), the user-agent is pulled when accessing the site, and ASP.NET knows how to serve up which version of the views. Check out "Framework64\v4..0.30319\Config\Browsers\" to get a list of the user-agent browser files. You can open these files in VS.NET if you want to inspect them. After the proper user agent is matched, then the view name by convention (index.mobile.cshtml) is rendered.

"How many want to hear about Web API?" The entire room raised their hands! How appropriate to finish the day and conference with a technology at the top of my list in interest in learning. It allows the creation of REST based services.

Now here is something to wrap your head around. Just learning MVC? Well there is a camp of folks that are writing application by making all calls to the controller via Web API. One of the goals here is to push more functionality to the client and prevent expensive server calls. Libraries like Knockout.js have to be leveraged to help in this. By doing this you make lean responsive applications. It's a balance between what goes on the client and what's on the server. One thing at a time for now...

Tibi added a folder to the existing MVC app named 'api' under the existing 'Controllers' folder. The 'api' will match the convention for the Web API URIs. This folder will contain the controller actions for 'api' calls. Notice in the routing (Application_Start) that no 'Action' is defined. This is because the Http verb infers this for us in API calls.

Using Fiddler is the easiest way to make API calls. Just click on the 'Composer' tab and select the proper Http verb along with the URL to call. You can also modify the request headers or body from here as well. This is a nice place to define the user-agent if you are testing that functionality. To build up the body, create JSON like {"Title": "My New Course", "Duration": "1"}. Make sure in the header to include "Content-type: application/json" (without quotes).

He finished off with a quick demo on a Dependency Resolver named Unity (Unity.MVC or Unity.WebAPI; there are different versions for different technologies). Unity is a lightweight, extensible dependency injection container that supports interception, constructor injection, property injection, and method call injection. You can get Unity from NuGet and add it as an installed package to your application. This way you do not have to manually instantiate the model type on the repository for each class. In Application_Start() you make a call to Bootstrapper.Initialize() to register the containers that map the Types to their concrete equivalent.

container.RegisterType();

Whew, that was a great day with a lot of information on MVC. Nice job Tibi!

Wrap Up Day 5 - That's a wrap!


Well it's the last day of the conference, and the 1st day the sun has been out since prior to conference registration! Sorry to the people that came here from the north and expected to see the 'sunshine state' along with 75 degree weather.

It's bittersweet at the end of the conference because it's sad it's over but it will be nice to go home. The comradery among attendees and networking that occurs is an awesome byproduct of the conference. My brain is absolutely jam packed with good information. I applaud all of the presenters and conference organizers and rank this as one of the best Visual Studio LIVE! conferences I've attended. If you have never attended a conference like this, I highly recommend breaking out of your cultural "bubble" at work, home, etc. and attend to see where the community is headed. These conferences doing a job second to none to validating and presenting a sort of "what's good and what's maybe not so good" based on community feedback (presenters don't pitch anything and just state the facts).

I am already looking forward to next year's conference, but in the meantime I feel I have the information needed to make great decisions on leading technology for the upcoming 2013 year. Hope everyone has a great trip home and see you next year! #Live360


Wednesday, August 31, 2011

Help! My ASP.NET page is generating a JavaScript "Object Expected" Error Now That I Am Using jQuery, Plus A Little On URLs In ASP.NET

So you are all excited and just got into this jQuery thing! You build your code, run the ASP.NET site, and IT IS SO Cool...wait. You look down and see a JavaScript error in the status bar of the browser indicating the following:

"Object Expected"

Well let's begin by stating this is about a generic error message as they come and there could be 1 million different reasons for it. However if you just got into writing jQuery and are running into this error, then odds are you have not properly referenced the jQuery scripts for your application to use. I hope at least you know there are scripts that must be incuded. If you are thinking "No I didn't know", then you need to go back to jQuery 101 videos on setup.

However getting the scripts properly added to your ASP.NET application does have some nuances which make it easy to mess them up. The most common pitfall is if you have a ASP.NET site that uses a MasterPage. If this is the case, then your content pages are probably not adding the scripts individually, and the most appropriate place to register them in in the MasterPage's <Head> block.

You have (2) main options for referencing the jQuery scripts: download the scripts and include them with your project or from a CDN (Content Delivery Network) (i.e. Google or Microsoft). The upside to using a CDN is you do not have to worry about downloading the scripts into a folder and then finding that perfect path in the MasterPage to reference them. The downside is if you were working in an Intranet environment and outside access was limited or denied, then you would not be able to dynamically access those scripts.

Regardless, the 1st step in seeing if the original error described above is caused by improperly added scripts, let's go ahead and reference the jQuery scripts from Microsoft's CDN like displayed below:
<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.5.1.js" type="text/javascript"></script>
If using the script reference from the CDN above fixed the issue then great! If you want, you can leave it like that and be done. If you still want to download the scripts and have them within your project to be referenced, then we have to dig a bit deeper as to the referenced path you use for the scripts.

Odds are your jQuery scripts are in a folder named 'Scripts', your pages in another folder named 'Pages' and maybe you have multiple levels of organizing your code files making it difficult sometimes for the proper path to be resolved. One of the BIGGEST pitfalls I see is using the Intellisense dialog when pressing the "=" sign after 'src' in a tag and navigating to the files/scripts manually. Well you would think VS.NET would give you the proper path, correct? Not always. In fact it will use a dot-dot notation (../Path/File) which seems proper, but at runtime does not resolve correctly.

In VS.NET there are going to be a few ways you can reference a path to a file and this is where things get confusing sometimes. Let's look at the (4) main ways to reference a path from the page's source:

(1.) use a relative path: using a series of backslashes "\" you can provide a path in reference or relative to your current location.
<script src="/Scripts/jquery-1.4.4.js" type="text/javascript"></script>
(2.) 'dot-dot' notation: this indicates, "Navigate up to the parent directory" from the path provided.
<script src="../Scripts/jquery-1.4.4.js" type="text/javascript"></script>
(3.) tilde (~) character: this represents the root directory of the application in ASP.NET. The caveat is it can only be used in conjunction with server controls *not* script tags (without some server-side code help shown below). This method is not going to work well by default.
<script src="~/Scripts/jquery-1.4.4.js" type="text/javascript"></script>
(4.) ResolveURL (System.Web.UI) method: This is a server-side command to convert a URL into one that is usable on the requesting client. Notice the use of the server-side code escape tags so this method can be called. This is the method I prefer and dynamically resolves the URL to the proper path. I recommend using it if you are going to reference local project script files.
<script src='<%# ResolveUrl("~/Scripts/jquery-1.4.4.min.js") %>' type="text/javascript"></script>
My recommendation is to either reference the scripts from a reputable CDN like Google or Microsoft, or use Option # 3 above with the 'ResolveUrl' method. This will ensure your custom JavaScript and jQuery files are properly registered with your ASP.NET application.

There are some good resources for explaining how to resolve path's in MasterPages and in traditional pages. Below I have included some links if you would like to investigate further or bookmark for reference.

Avoiding problems with relative and absolute URLs in ASP.NET

URLs in Master Pages

Directory path names

Microsoft Ajax Content Delivery Network