# Wednesday, July 01, 2009

Ajax History - a how to - Part 2 - a basic example

Part 1 - Introduction
Part 2 - Basic Example
Part 3 - Complex Example
Part 4 - Final Notes
Bonus - Ajax History and the Memento Pattern
Extra Bonus - Issues with Opera

Handling Ajax history has been a critical issue since Ajax first started. With the advent of .net 3.5 the ability to easily deal with that is rolled into the code. This is part 2, here we will look at a simple example of handling Ajax history to give a basic understanding of the process. If you have not read Part 1, I suggest you do that now. Part 3 will expand on handling Ajax history when the data is more complex.
For simplicity sake we are only going to look at an example of very, very basic history. We are going to look at a page that displays changes made to the time.

A basic assumption is that you are fairly familiar with Ajax (http://ajax.asp.net). We are going to be using the ScriptManager (of course) and an UpdatePanel.
Our basic page looks like this

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="AjaxHistory.aspx.cs" Inherits="AjaxHistory" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager runat="server" ID="sm1" EnablePartialRendering="true" />
    <div>
        <asp:UpdatePanel runat="server" ID="upME">
            <ContentTemplate>
                <asp:Button runat="server" ID="uxChangeTime"
                    OnClick="uxChangeTime_Click" Text="-- change:" />
                <asp:Label runat="server" ID="lblTime" />
            </ContentTemplate>
            <Triggers>
                <asp:AsyncPostBackTrigger ControlID="uxChangeTime"
                     EventName="Click" />
            </Triggers>
        </asp:UpdatePanel>
    </div>
    </form>
</body>
</html>

The codebehind sets the label text to be the current date/time on every click:

(c# code)
protected void uxChangeTime_Click(object sender, EventArgs e)
{
    string _currentDateTime = DateTime.Now.ToString("F");
    lblTime.Text = _currentDateTime;
}

(vb code)
Protected Sub uxChangeTime_Click(ByVal sender As Object, &_
  ByVal e As System.EventArgs) Handles uxChangeTime.Click
    Dim _currentDateTime As String = DateTime.Now.ToString("F")
    lblTime.Text = _currentDateTime
End Sub

As you can see this is fairly straightforward. Clicking on the button displays the current DateTime in the label. However, there is no history. Clicking back will not display the last DateTime displayed but either be grayed out (if this was the first page loaded) or take you back to the page before this one.

To enable history is very, very simple in 3.5

  1. modify the ScriptManager, setting a property or two and registering an event (Navigate) for the ScriptManager
  2. modify the Click handler to notify the ScriptManager to record a History Point
  3. handle the event called by the ScriptManager to rollback, when required, the page

Step 1: Modify the ScriptManager

The modifications are very simple - set EnableHistory equal to true and specify what should handle the Navigate event. You can also specify if you want the HistoryState to be 'secure' by setting EnableSecurityHistoryState equal to true, but this is optional.

Your ScriptManager should now look like this:

<asp:ScriptManager runat="server" ID="sm1"
     EnableHistory="true" OnNavigate="sm1_Navigate"
     EnableSecureHistoryState="true"
/>

Step 2: Add an AsyncPostBackTrigger for the Navigate event of the ScriptManager

<asp:UpdatePanel ...>
    ...
    <Triggers>
         <asp:AsyncPostBackTrigger ControlID="uxChangeTime"
                     EventName="Click" />
         <asp:AsyncPostBackTrigger ControlID="sm1" EventName="Navigate" />
    </Triggers>
</asp:UpdatePanel>

Step 3: Modify the Click handler (or whatever event it is that would trigger a HistoryPoint to be recorded

What you are doing here is notifying the ScriptManager what information to store as a HistoryPoint by calling its AddHistoryPoint method. There are three possible overloads and we will look at the other two next blog, but for now we will use one of the simplest, a single key/value string pair to store the current DateTime with the key "myTime":

(c# code)
protected void uxChangeTime_Click(object sender, EventArgs e)
{
    string _currentDateTime = DateTime.Now.ToString("F");
    lblTime.Text = _currentDateTime;
    ScriptManager.GetCurrent(this).AddHistoryPoint("myTime", _currentDateTime);
}

(vb code)
Protected Sub uxChangeTime_Click(ByVal sender As Object, &_
  ByVal e As System.EventArgs) Handles uxChangeTime.Click
    Dim _currentDateTime As String = DateTime.Now.ToString("F")
    lblTime.Text = _currentDateTime
    ScriptManager.GetCurrent(Me).AddHistoryPoint("myTime", _currentDateTime)
End Sub

Step 4: handle the ScriptManager Navigate event

In this event we retrive the State property of the HistoryEventArgs and use that to set the page to be what we want it to be. In this very simple example we will use it to set the value of the label to be the DateTime that that particular page displayed:

(c# code)
protected void sm1_Navigate(object sender, HistoryEventArgs e)
{
    string _historyValue = e.State["myTime"];
    if (_historyValue != null)
    {
        lblTime.Text = _historyValue;
    }
}

(vb code)
Protected Sub sm1_Navigate(ByVal sender As Object, &_
  ByVal e As System.HistoryEventArgs) Handles sm1.Navigate
    Dim _historyValue As String = e.State["myTime"]
    if(_historyValue <> Nothing)
    {
       lblTime.Text = _historyValue
    }
End Sub

Voila! We now have a functional Ajax page with history. Try it!

What has happened is that we are using History Points. When we click the button it determines the current DateTime, displays it, and stores it as a history point. When we click the "Back" button it retrieves the previous history point (which was the current DateTime in string format) and displays that (see chart below).

Next blog: a look at more intensive use of History Points...
btw, for prep, take a looksee at my blog on the Memento pattern and Ajax history to understand my basic approach: http://www.myfriedmind.com/techBlog/2009/05/20/AjaxHistoryAndTheMementoPattern.aspx

# Tuesday, June 30, 2009

Ajax History - a how to - Part 1

Part 1 - Introduction
Part 2 - Basic Example
Part 3 - Complex Example
Part 4 - Final Notes
Bonus - Ajax History and the Memento Pattern
Extra Bonus - Issues with Opera

One of the most exciting prospects with .net 3.5 is the ability to handle Ajax History.

Ajax, for those of you who may be only vaguely familiar, is a way to do partial postbacks of web pages. This allows you to modify only a portion of the screen rather than updating the entire transaction. This increases the potential speed exponentially. One form are CallBacks, another used by the UpdatePanel is a full PostBack but only a redrawing of the appropriate sections of the page.

However, there are three main problems with Ajax when using controls like the UpdatePanel:

  1. Hitting the “Back” button loads the page that was loaded BEFORE the ajax page
  2. Refreshing the page has the effect of losing whatever Ajax calls have been made (ie the page itself is reloaded from the start).
  3. There is no effective method to ‘link’ to the page resulting from ajax calls. All links lose any Ajax calls because the URL does not display the necessary information to recreate the page at the appropriate place.

All of these can be resolved (as we shall see) by using Ajax History.

This first blog entry will lay out the problems in greater detail and the general means by which Ajax History resolves them. Later entries will go more into detail on how to do Ajax History (yes, there is some additional work, but not very much).

Hitting the “Back” button loads the page that was loaded BEFORE the ajax page

The issue here is caused by two factors:

  1. The "Back" button loads the last entry in url history
  2. Ajax does not automatically change the url history

An example of what I am talking about can be seen in my (non-artistic) image below.

Hitting the "Back" button after doing regular Postbacks will take you to the most recent page of information. However, hitting the "Back" button after doing CallBacks takes you all the way back to the page that was loaded BEFORE you even got to the Ajax page.

The reason for this lies in the way that the href property of the Html Document Object Model is handled. By default whenever a full PostBack occurs or a modification to the hash in the URI (such as an internal link), the URI of the new entry is stored in the location history. This is handled by the browser behind the scenes and has been since the early days. However, since Ajax calls are not full PostBacks the browser mechanism does not, by default, record their entry.

To get around this, a rather ingenious method is used. Because modifications to the URI’s hash portion is included in the history but DOES NOT require full PostBack (think internal anchor links) .net 3.5 writes to the hash portion of the page to trigger the entry being recorded, by the browser, in the href history. Pretty slick, hunh?

For those of you unfamiliar with the hash concept of the URI, consider the following quick example:

<html>
<head></head>
<body>
<a name=’Up’></a>
<a href=’upsidedown.html#Down’>Go Down</a>
<p>&nbsp;</p>
<a name=’Down’></a>
<a href=’upsidedown.html#Up’>Go Up</a>
</body>
</html>

I have posted this basic page to my site. Note that there are anchor tags (<a…) that have names and others that have a hash sign (#) in them that refer to that name. These are commonly used to enable users to just to a particular portion of the page without having to scroll and are known as Internal Links (because they link to an internal portion of the page and not to an external page).

When the page first loads you can see that the URI is simply http://www.myfriedmind.com/updown.html. There is one link visible (the other would be visible if you scrolled but I shrunk the browser window to give an idea of what internal links are generally used for.

Once the link is clicked it moves the page down to where the “named” anchor is. In other words, clicking on the anchor tag that references #Down (<a href=’upsidedown.html#Down’>Go Down</a>) moves the page to the anchor that contains the name that matches the one after the hash sign – so you are moved WITHIN the page (to <a name=’Down’></a>). This is very important – note that the URI has changed, it is now showing the hash. Also critical to note is that THE PAGE DID NOT POSTBACK!

Clicking on the link that has the hash entry for Up (<a href=’upsidedown.html#Up’>Go Up</a>) results in moving to the anchor tag that has the name (Up) that matches what was after the hash mark. Again, the URI is updated to a new hash display and THE PAGE DID NOT POSTBACK!!!

It is critical that you understand that when this takes place the browser records the change in its href history. In other words, modifications to the hash are RECORDED in the browser’s href history. Thus hitting the “Back” button means that we move back to the previous entry, which is NOT http://www.myfriedmind.com/updown.html, but http://www.myfriedmind.com/updown.html#Down (see below)

Thus the elegant solution – to store Ajax postbacks, record the entries in the hash of the URI (like you are doing an internal link) and the updated URI will be recorded in the browser history for you. Any clicks on the “Back” button will load the previous entry. See the basic example below. Note that the change after CallBack to the URL (adding a hash entry).

I must note here that the adding to the hash is not automatic. You do need to do code but very, very little. Your code mostly deals with how to effectively package the info and unpackage when required. If you are curious take a looksee at my (fascinating) blog on how this is similar to the Memento pattern ->  http://www.myfriedmind.com/techBlog/2009/05/20/AjaxHistoryAndTheMementoPattern.aspx.

Once you grasp this concept – that the Ajax History is stored in the URI’s hash, the solutions to both of problems #2 and #3 (page refresh and linking) are resolved.

  • Refreshing  the page refreshes the URI, INCLUDING the hash. Thus the page that gets returned is the page that has had the Ajax modifications done
  • Sending a link to this page, INCLUDES the URI’s hash, and thus the page will be able to return the point in its Ajax modifications that a person wants

There are a number of very useful examples of how this is used today (Ajax History is not new with 3.5 but it DOES make it a whole lot simpler). The most commonly used are mapping websites. One can hunt around (using Ajax) until one gets to the exact display one desires and then save/send/whatever that display. Without the hash entry one would always end up at the very beginning and the desired display would be lost. I can use bing to zoom into the street view of Dunn Bros (the BEST coffee) by my old alma mater in Saint Paul and shoot that url to a friend.( shortened for display - http://www.bing.com/maps/#JnE9eX...zMTI1)

But this is not limited to mapping sites. Imagine that you have a Ajax based site that enables users to filter/sort/etc on a GridView display customer accounts. “Ah ha,” says the manager (they say things like that when no one is listening) “this is precisely the display I am looking for. I will just save it into my favorites. And I will also email it to the big boss.” Without the hash they would be exceedingly frustrated as would the big boss. With proper Ajax history coding the sun shines, the wind blows, and gas prices drop. Everyone is happy.

;)

Next blog: What exactly is involved in doing Ajax History, the concepts….

# Monday, June 22, 2009

CascadingDropDownLists, GridViews, and Dynamically selecting the appropriate ListItem during Edit...

okay, I apologize. I promised to do this weeks ago and am only finally getting it up. Hopefully it will be worth the wait...

If you are planning on simply using a DDL (and not Cascading), look here -> http://www.myfriedmind.com/techBlog/2009/06/04/DropDownListGridViewAndDynamicallySelectingTheAppropriateListItemDuringEdit.aspx

Note that this example uses a remote webservice (rather than a PageMethod) for the CascadingDropDownList calls and I have not included them to simplify the amount of code on this page, but they are fairly straightforward -> http://www.asp.net/AJAX/AjaxControlToolkit/Samples/CascadingDropDown/CascadingDropDown.aspx

Sample DataObject (in c#)

class MyObject
{
   public int ObjId { get; set; }
   public string StateAbbreviation { get; set; }
   public string ZipCode { get; set; }
}

Sample DataObject (in vb.net)

Class MyObject
   Public ObjId As Integer
   Public StateAbbreviation As String
   Public ZipCode As String
End Class

Entry on SampleGridView.aspx page (as simple as you can get it)

<asp:GridView runat="server" ID="lstMyData" DataKeyNames="ObjId"
    AutoGenerateColumns="false" EnableViewState="true"
    AutoGenerateEditButton="true"
    OnRowDataBound="lstMyData_RowDataBound" >
    <Columns>
        <asp:TemplateField>
            <ItemTemplate>
                <asp:Label runat="server" ID="lblState"    
                    Text='<%# Eval("StateAbbreviation") %>' />
            </ItemTemplate>
            <EditItemTemplate>
                <asp:DropDownList runat="server" ID="uxState" />
                <aKit:CascadingDropDown ID="ajxState" runat="server"
                    TargetControlID="uxState" Category="State" 
                    ServiceMethod="GetStates" />
            </EditItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField>
            <ItemTemplate>
                <asp:Label runat="server" ID="lblZipCode"  
                   Text='<%# Eval("ZipCode") %>' />
            </ItemTemplate>
            <EditItemTemplate>
                <asp:DropDownList runat="server" ID="uxZipCode" />
                <aKit:CascadingDropDown ID="ajxZipCode" runat="server"
                    TargetControlID="uxZipCode" Category="ZipCode"
                    LoadingText="[Loading Zipcode...]"
                    ServicePath="WebServiceToReturnData.asmx" 
                    ServiceMethod="GetZipCode"
                  ParentControlID="uxState" />
            </EditItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>

Codebehind (in c#) SampleGridView.apx.cs (note this does NOT include the code to return the values)

protected void lstMyData_RowDataBound(object sender,
        GridViewRowEventArgs e)
{
    // verify this is a DataRow, not a Header, Footer, etc...
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        // in case you want to do something else here....
       
        // see if this is the Row used for Editing
        if ((e.Row.RowState & DataControlRowState.Edit) != 0)
        {
            // get the DataItem that is bound
            // and cast it accordingly
            MyObject _myObject = (MyObject)e.Row.DataItem;

            // get the CascadingDropDowns
            CascadingDropDown _ajxState =
                (CascadingDropDown) e.Row.FindControl("ajxState");
            CascadingDropDown _ajxZipCode =
                (CascadingDropDown) e.Row.FindControl("ajxZipCode");

            // the data is automatically updated via the Ajax,
            // so we merely need to select the item

            _ajxState.SelectedValue = _myObject.StateAbbreviation
            // _ajxState.ContextKey = this can be set here if you are using it;

            _ajxZipCode.SelectedValue = myObject.ZipCode;
        }
    }
}

Codebehind (in vb.net) SampleGridView.apx.cs (note this does NOT include the code to return the values)

Protected Sub lstMyData_RowDataBound(ByVal sender As Object, _
    ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) _
    Handles lstMyData.RowDataBound
    If e.Row.RowType = DataControlRowType.DataRow Then

            ' in case you want to do something else here....

            ' see if this is the Row used for Editing
            If (e.Row.RowState And DataControlRowState.Edit) <> 0 Then


                ' get the DataItem that is bound
                ' and cast it accordingly
                Dim _myObject As MyObject = _
                    DirectCast(e.Row.DataItem, MyObject)

                ' get the CascadingDropDowns
                Dim _ajxState As CascadingDropDown = _
                    DirectCast(e.Row.FindControl("ajxState"), CascadingDropDown)
                Dim _ajxZipcode As CascadingDropDown = _
                    DirectCast(e.Row.FindControl("ajxZipCode"), CascadingDropDown)

                ' ddl population should be handled by the Ajax
                ' ServiceMethod of the CascadingDropDown

                ' select the appropriate item
                _ajxState.SelectedValue = _myObject.StateAbbreviation
                _ajxZipCode.SelectedValue = _myObject.ZipCode

            End If
     End If
End Sub

# Thursday, June 11, 2009

Installation Error if Diagnostics Log location moved...

I was installing a new Sharepoint Server in the farm, mostly because the currently had was too small.

I ran into errors with the Office Server Web Services (it kept saying it could not find the path, although the path to that website was fine (I checked it three times). The Root virtual directory could not be found...

I tried many things. Finally I ran the Office wizard to remove the new server from the farm. I then ran it again to reinstall it (after making sure that various dbs had the Sharepoint Server as a user - see previous log). And I got the error below:

The error in the log informed me that I it was missing the location of the Diagnostics Logs that I had moved to the E: drive on the old, slow server. So I moved the Diagnostics logs to the D: drive before running the wizard to try to join the farm again. No dice. Same error. I moved the logs back to there original locale in the 12-spot. No dice.
 
I try renaming the D: drive to E: - it refuses.
 
On the point of uninstalling/reinstalling MOSS on the new server I hesitate...
 
Searching through Regedit in the NEW server (not currently on the Farm) I find an entry in HKLM\Software\Microsoft\Shared Tools\Web Server Extensions\12.0\WSS called LogDir. And guess what is in it? You got it - a reference to the entry on the E: drive...
 
Manually changed the regedit entry to match to the new d: location (on both servers) and VOILA!!!
 
So be warned - if you move the Diagnostics Logs location in a farm, a folder must be available on ALL servers (makes sense).
 
And the Office Server IIS site is gone. Probably a carryover from before...

# Monday, June 08, 2009

Microsoft.Exchange.Data.Storage.StoragePermanentException - There was a problem accessing Active Directory

Recently we added some new hires to the system only to discover that when one of them tried to use OWA that an error was triggered - notably that there was an error accessing Active Directory and that the InnerException was Microsoft.Exchange.Data.Directory.InvalidADObjectOperatorException (see below)

Now this only happened with new users. Old users, ones we migrated from x03 to x07 could use OWA with no problem. I should note (and this is critical) that we still had our old x03 up, even though we had migrated all the old user mailboxes. This was because of our unified messaging needing to still run (for a wee bit) on a legacy system. A quick check on the hub server looking at the mailboxes verified my fears - the mailboxes had been created as 'Legacy' mailboxes.

How could this have happened? I checked with my cohort and he confirmed that when we created the boxes in AD he specified a storage group that was on the x07 server. A quick check verified this. Obviously when a new mailbox is created in a mixed environment (at least in ours) it defaults to the least-common-denominator - in this case x03.

The solution was fairly straightforward - move the mailbox. But how? It already WAS on an x07 storage group. Simple - move to a different storage group - voila!

Except.... it is still a problem...

DOH!!!

I peek into AD using ADSI Edit and discover that the mxExchVersion for the problem people IS NOT SET!!! A quick search brings me here -> http://support.microsoft.com/kb/941146.

Running the Set-Mailbox User_name -ApplyMandatoryProperties is the final step and everything is beautiful. OWA functions and the sun comes out.

On to the next quirk......

 

# Thursday, June 04, 2009

DropDownList, GridView, and Dynamically selecting the appropriate ListItem during Edit...

I have seen a number of questions regarding how to specify what the selected ListItem is on a DropDownList used in an EditRow of A GridView. So I thought it might make a good blog.

Note: for an example that uses Ajax, specifically the CascadingDropDown, look here http://www.myfriedmind.com/techBlog/2009/06/22/CascadingDropDownListsGridViewsAndDynamicallySelectingTheAppropriateListItemDuringEdit.aspx.

Basically what people are trying to do is use a DropDownList as one of the input controls on a DataRow when edit is selected for that Row. This, of course, limits what people can enter which is a good thing. For example, what if you wanted to create a DropDownList that specified which state someone was from, or what time (using 15-minute increments)?

Creating the DropDownList is fairly straightforward, as is populating it. What is confusing people is how to specify what the selected value is. To do that you need to handle the OnRowDataBound event, snag the DataItem and select the ListItem that matches the appropriate property. Like so:

Sample DataObject (in c#)

class MyObject
{
   public int ObjId {get; set;}
   public string StateAbbreviation {get; set; }
}

Sample DataObject (in vb.net)

Class MyObject
    Public ObjId As Integer
    Public StateAbbreviation As String
End Class


Entry on the SampleGridview.aspx page (as simple as you can get it )

<asp:GridView runat="server" ID="lstMyData" DataKeyNames="ObjId"
    AutoGenerateColumns="false" EnableViewState="true"
    AutoGenerateEditButton="true"
    OnRowDataBound="lstMyData_RowDataBound" >
    <Columns>
        <asp:TemplateField>
            <ItemTemplate>
                <asp:Label runat="server" ID="lblState" Text='<%# Eval("StateAbbreviation") %>' />
            </ItemTemplate>
            <EditItemTemplate>
                <asp:DropDownList runat="server" ID="uxState" />
            </EditItemTemplate>
        </asp:TemplateField>
    </Columns>
</asp:GridView>

Codebehind (in c#) SampleGridview.aspx.cs

protected void lstMyData_RowDataBound(object sender,
        GridViewRowEventArgs e)
{
    // verify this is a DataRow, not a Header, Footer, etc...
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        // in case you want to do something else here....
       
        // see if this is the Row used for Editing
        if ((e.Row.RowState & DataControlRowState.Edit) != 0)
        {
            // get the DataItem that is bound
            // and cast it accordingly
            MyObject _myObject = (MyObject)e.Row.DataItem;

            // get the dropdownlist
            DropDownList _uxState =
                (DropDownList) e.Row.FindControl("uxState");

            // dynamically create the DDL for demo...
            // this could also be done statically in the aspx page
            _uxState.Items.Add(new ListItem("Hawaii", "HI"));
            _uxState.Items.Add(new ListItem("Iowa", "IA"));
            _uxState.Items.Add(new ListItem("Minnesota", "MN"));
            _uxState.Items.Add(new ListItem("Wisconsin", "WI"));

            // find the matching ListItem and Select it
            ListItem _matchingItem =
                _uxState.Items.FindByValue(_myObject.StateAbbreviation);
            if (_matchingItem == null)
            {
                // Houston we have a problem, no match...
                // handle this as you see fit
            }
            else
            {
                _matchingItem.Selected = true;
            }
        }
    }
}

Protected Sub lstMyData_RowDataBound(ByVal sender As Object, _
    ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) _
    Handles lstMyData.RowDataBound
    If e.Row.RowType = DataControlRowType.DataRow Then

            ' in case you want to do something else here....

            ' see if this is the Row used for Editing
            If (e.Row.RowState And DataControlRowState.Edit) <> 0 Then


                ' get the DataItem that is bound
                ' and cast it accordingly
                Dim _myObject As MyObject = _
                    DirectCast(e.Row.DataItem, MyObject)

                ' get the dropdownlist
                Dim _uxState As DropDownList = _
                    DirectCast(e.Row.FindControl("uxState"), DropDownList)

                ' dynamically create the DDL for demo...
                ' this could also be done statically in the aspx page
                _uxState.Items.Add(New ListItem("Hawaii", "HI"))
                _uxState.Items.Add(New ListItem("Iowa", "IA"))
                _uxState.Items.Add(New ListItem("Minnesota", "MN"))
                _uxState.Items.Add(New ListItem("Wisconsin", "WI"))

                ' find the matching ListItem and Select it
                Dim _matchingItem As ListItem = _
                    _uxState.Items.FindByValue(_myObject.StateAbbreviation)
                If _matchingItem Is Nothing Then
                    ' Houston we have a problem, no match...
                    ' handle this as you see fit
                Else
                    _matchingItem.Selected = True
                End If
            End If
     End If
End Sub

So basically what you are doing is getting a hold of both the value that you want selected (by using the e.Row.DataItem) and the DropDownList (by using e.Row.FindControl) and then simply doing a match...

Hope this helps...

# Wednesday, June 03, 2009

Error Code: 403 Forbidden. ISA Server is configured to block HTTP requests that require authentication. (12250)

When moving to a new IP address range I decided to clean up some of my ISA rules. I consolidated a number of websites into a new listener, but when I tried to access them I got:

Error Code: 403 Forbidden. ISA Server is configured to block HTTP requests that require authentication. (12250)

Now I was pretty sure I had setup the listener for No Authentication, and sure enough it was. But I have forgotten to specify that the client could allow authentication over HTTP (see image below). Once it was "allowed" everything was back to normal...

Edge Server unable to send or receive emails

Hint: it isn't ISA

We recently moved to a new set of IP addresses and a new router and while most everything was running fine our Edge Server was having trouble sending/receiving from the world at large.

Now I have the Edge in a DMZ in order to use a public address since ISA does not doing client NATing at all (curse you!) and initially I suspect there was something in my subnetting or some such that was the problem. Maybe it was the "Perimeter" network object that was the issue? Could it be something whacky with my ruleset?

However, I was able to connect from our hub server to the edge server (ie from in to out). I was able to connect back to the hub server. I could even get out from the hub server (using the default NAT address). I just could not get OUT from the edge server. No DNS lookup (using outside), no web browsing, and definitely no SMTP happening here...

I kept going over and over looking for what I had configured wrong on ISA or on the Edge server or in DNS...

So I enabled pinging on the ISA server for all Networks so I could check and sure enough, while I could ping the "External" Nic's ip addresses from the router, I could not ping the DMZ Nic, much less the Edge server on the other side of it.

There was no route manually specified for the particular IP address that the Edge server was running. DOH!!!! A wee bit o cisco magic and *voila* email flowing like there is no tomorrow.

Assumptions, assumptions, assumptions...

# Tuesday, June 02, 2009

Cannot open database requested in login 'SharedServicesDatabase'. Login fails. Login failed for user MYDOMAIN\MyServer$

You may get an error like:
 
Cannot open database requested in login 'SharedServicesDatabase'. Login fails.
Login failed for user ' MYDOMAIN\MyServer$'.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.Data.SqlClient.SqlException: Cannot open database requested in login 'SharedServicesDatabase'. Login fails.
Login failed for user ' MYDOMAIN\MyServer$'.
 
This is triggered by the odd fact (which I can not find documented anywhere) that the server that is housing the Sharepoint site must have its computer account listed as a user within the Shared Services database for the search to function.

# Monday, June 01, 2009

Note to self - watch out for ISA 2006 Listeners and Sharepoint AAM

I had setup AAM w/ Sharepoint and our ISA servers. What had not occured to me was that the Connection Verifier was tied to port 80 (I had not put it somewhere else). So when I removed that site *boom*.
 
Changed ISA Verifier to the correct port and it works fine...