# 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...

# Friday, May 29, 2009

Getting errors nicely via email from your website the vb.net version

For the c# version look here -> http://www.myfriedmind.com/techBlog/2009/05/29/GettingErrorsNicelyViaEmailFromYourWebsiteTheCVersion.aspx

It is always nice to get notified that there is an error on your website in some other way than a user on the other end of the phone.

One of the nicest ways I know of in .net is to use the Application_Error portion of Global.asax to shoot me an email listing the information that is needed. I have included some basic code below but you can feel free to modify it as you choose. For example you may prefer to include the System.Net.Mail info on the SmtpServer in the web.config so you can use it on other portions of the site rather than repeat it.

In the code I use reflection to roll through both the Request object and the Exception that is passed. You can modify it if you want to display more (such as innerexceptions, etc) or to exclude, but Reflection can be nice in pulling up all the data quickly.

=================
In vb.net

In Web.Config
        <customErrors mode="On" defaultRedirect="~/ErrorOnWebsite.aspx"/>

In Global.asax (don't do this and you get an endless loop!)
    Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
        Server.Transfer("~/ErrorOnWebsite.aspx")
    End Sub

On ErrorOnWebsite.aspx (you don't actually have to use a label, you can provide some other means of apology)
        <asp:Label runat="server" ID="lblApology"></asp:Label>

This is Error.OnWebsite.aspx.vb (I like code-behinds) - note the use of the HtmlTextWriter and Reflection!

Imports System
Imports System.IO
Imports System.Net.Mail
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports System.Reflection

Partial Class ErrorOnPage
    Inherits System.Web.UI.Page

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Dim exc As Exception = Server.GetLastError()

        If (exc Is Nothing) Then
            ' there is no error, why are they here?
            Response.Redirect("~/")
        Else
            SendNotificationEmail(exc)
            WriteApology()
            Server.ClearError()
        End If
    End Sub

    Private Sub WriteApology()
        lblApology.Text = "Oops, sorry, the master of light and sound has been notified..."
    End Sub

    Private Sub SendNotificationEmail(ByVal exc As Exception)
        ' Create the format holder for the data, I like tables...
        Dim _infoTable As Table = New Table()
        _infoTable.Attributes.Add("border", "1")
        _infoTable.Attributes.Add("cellpadding", "3")

        Dim _infoRow As TableRow
        Dim _infoNameCell As TableCell
        Dim _infoDataCell As TableCell

        _infoRow = New TableRow()
        _infoNameCell = New TableCell()
        _infoNameCell.Text = "Server Request Properties"
        _infoNameCell.BackColor = System.Drawing.Color.Gray
        _infoNameCell.ColumnSpan = 2
        _infoRow.Cells.Add(_infoNameCell)
        _infoTable.Rows.Add(_infoRow)

        ' I like to use reflection because I hate to type...
        Dim _requestType As Type = Request.GetType()
        Dim _pInfo As PropertyInfo

        For Each _pInfo In _requestType.GetProperties()

            If (Not _pInfo.Name = "Item") Then

                _infoRow = New TableRow()

                _infoNameCell = New TableCell()
                _infoNameCell.Text = _pInfo.Name
                _infoRow.Cells.Add(_infoNameCell)

                _infoDataCell = New TableCell()
                Dim _value As Object = _pInfo.GetValue(Request, Nothing)
                If _value Is Nothing Then
                    _infoDataCell.Text = ""
                Else
                    _infoDataCell.Text = Server.HtmlEncode(_value.ToString())
                End If
                _infoRow.Cells.Add(_infoDataCell)

                _infoTable.Rows.Add(_infoRow)
            End If
        Next

        _infoRow = New TableRow()
        _infoNameCell = New TableCell()
        _infoNameCell.Text = "Exception Properties"
        _infoNameCell.BackColor = System.Drawing.Color.Gray
        _infoNameCell.ColumnSpan = 2
        _infoRow.Cells.Add(_infoNameCell)
        _infoTable.Rows.Add(_infoRow)

        Dim _exceptionType As Type = exc.GetType()
        For Each _pInfo In _exceptionType.GetProperties()        
            _infoRow = New TableRow()

            _infoNameCell = New TableCell()
            _infoNameCell.Text = _pInfo.Name
            _infoRow.Cells.Add(_infoNameCell)

            _infoDataCell = New TableCell()
            Dim _value As Object = _pInfo.GetValue(exc, Nothing)
            If (_value Is Nothing) Then
                _infoDataCell.Text = ""
            Else
                _infoDataCell.Text = Server.HtmlEncode(_value.ToString())
            End If
            _infoRow.Cells.Add(_infoDataCell)

            _infoTable.Rows.Add(_infoRow)
        Next

        ' note the use of the HtmlTextWriter to render the table
        ' into a string
        Dim _bodyWriter As StringWriter = New StringWriter()
        Dim _writeToBody As HtmlTextWriter = New HtmlTextWriter(_bodyWriter)
        _infoTable.RenderControl(_writeToBody)

        Dim _message As MailMessage = New MailMessage("error@mysite.com", "myemail@mysite.com")
        _message.Subject = String.Format("Error on website - {0}", exc.Message)
        _message.Body = _bodyWriter.ToString()
        _message.IsBodyHtml = True

        Dim _mailClient As SmtpClient = New SmtpClient("mymailserver")
        _mailClient.Send(_message)

    End Sub

End Class

Getting errors nicely via email from your website the c# version

For the vb.net version - go here -> http://www.myfriedmind.com/techBlog/2009/05/29/GettingErrorsNicelyViaEmailFromYourWebsiteTheVbnetVersion.aspx

It is always nice to get notified that there is an error on your website in some other way than a user on the other end of the phone.

One of the nicest ways I know of in .net is to use the Application_Error portion of Global.asax to shoot me an email listing the information that is needed. I have included some basic code below but you can feel free to modify it as you choose. For example you may prefer to include the System.Net.Mail info on the SmtpServer in the web.config so you can use it on other portions of the site rather than repeat it.

In the code I use reflection to roll through both the Request object and the Exception that is passed. You can modify it if you want to display more (such as innerexceptions, etc) or to exclude, but Reflection can be nice in pulling up all the data quickly.

=================
In c#

In Web.Config
        <customErrors mode="On" defaultRedirect="~/ErrorOnWebsite.aspx"/>

In Global.asax (don't do this and you get an endless loop!)
    void Application_Error(object sender, EventArgs e)
    {
        Server.Transfer("~/ErrorOnWebsite.aspx");
    }

On ErrorOnWebsite.aspx (you don't actually have to use a label, you can provide some other means of apology)
        <asp:Label runat="server" ID="lblApology"></asp:Label>

This is Error.OnWebsite.aspx.cs (I like code-behinds) - note the use of the HtmlTextWriter and Reflection!

using System;
using System.IO;
using System.Net.Mail;
using System.Reflection;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class ErrorOnWebsite : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        Exception exc = Server.GetLastError();

        if (exc != null)
        {
            SendNotificationEmail(exc);
            WriteApology();
            Server.ClearError();
        }
        else
        {
            // if there is no error, why are they here?
            Response.Redirect("~/", true);
        }
    }

    private void WriteApology()
    {
        lblApology.Text = "Oops, sorry, the master of light and sound has been notified...";
    }

    private void SendNotificationEmail(Exception exc)
    {
        // Create the format holder for the data, I like tables...
        Table _infoTable = new Table();
        _infoTable.Attributes.Add("border", "1");
        _infoTable.Attributes.Add("cellpadding", "3");

        TableRow _infoRow;
        TableCell _infoNameCell;
        TableCell _infoDataCell;

        _infoRow = new TableRow();
        _infoNameCell = new TableCell();
        _infoNameCell.Text = "Server Request Properties";
        _infoNameCell.BackColor = System.Drawing.Color.Gray;
        _infoNameCell.ColumnSpan = 2;
        _infoRow.Cells.Add(_infoNameCell);
        _infoTable.Rows.Add(_infoRow);

        // I like to use reflection because I hate to type...
        Type _requestType = Request.GetType();
        foreach (PropertyInfo _pInfo in _requestType.GetProperties())
        {
            if (_pInfo.Name != "Item")
            {
                _infoRow = new TableRow();

                _infoNameCell = new TableCell();
                _infoNameCell.Text = _pInfo.Name;
                _infoRow.Cells.Add(_infoNameCell);

                _infoDataCell = new TableCell();
                object _value = _pInfo.GetValue(Request, null);
                if (_value != null)
                {
                    _infoDataCell.Text = Server.HtmlEncode(_value.ToString());
                }
                else
                {
                    _infoDataCell.Text = "";
                }
                _infoRow.Cells.Add(_infoDataCell);

                _infoTable.Rows.Add(_infoRow);
            }
        }

        _infoRow = new TableRow();
        _infoNameCell = new TableCell();
        _infoNameCell.Text = "Exception Properties";
        _infoNameCell.BackColor = System.Drawing.Color.Gray;
        _infoNameCell.ColumnSpan = 2;
        _infoRow.Cells.Add(_infoNameCell);
        _infoTable.Rows.Add(_infoRow);

        Type _exceptionType = exc.GetType();
        foreach (PropertyInfo _pInfo in _exceptionType.GetProperties())
        {
            _infoRow = new TableRow();

            _infoNameCell = new TableCell();
            _infoNameCell.Text = _pInfo.Name;
            _infoRow.Cells.Add(_infoNameCell);

            _infoDataCell = new TableCell();
            object _value = _pInfo.GetValue(exc, null);
            if (_value != null)
            {
                _infoDataCell.Text = Server.HtmlEncode(_value.ToString());
            }
            else
            {
                _infoDataCell.Text = "";
            }
            _infoRow.Cells.Add(_infoDataCell);

            _infoTable.Rows.Add(_infoRow);
        }

        // note the use of the HtmlTextWriter to render the table
        // into a string
        StringWriter _bodyWriter = new StringWriter();
        HtmlTextWriter _writeToBody = new HtmlTextWriter(_bodyWriter);
        _infoTable.RenderControl(_writeToBody);

        MailMessage _message = new MailMessage("error@mysite.com", "myemail@mysite.com");
        _message.Subject = String.Format("Error on website - {0}", exc.Message);
        _message.Body = _bodyWriter.ToString();
        _message.IsBodyHtml = true;

        lblApology.Text = _bodyWriter.ToString();

        SmtpClient _mailClient = new SmtpClient("mymailserver");
        _mailClient.Send(_message);
    }

Howto: load a text file quickly into a DataTable

If you will be developing for a 64-bit Windows 7 box, please see this post as well -> TheMicrosoftJetOLEDB40ProviderIsNotRegisteredOnTheLocalMachineErrorOnWindows7

Someone asked about a quick way to load a text file and since the code is a bit lengthy I have included it before.

Basically one of the quickest ways (rather than opening and reading it line by line by line by line) is to use the OleDb connection to quickly push it into a Dataset. You can do this with any delimiter...

There are two procedures below - the first one UpdateSchemaIni does what it says - it updates the Schema.Ini so that the OleDbConnection knows what to parse on. This is necessary for non-csv files. The second - LoadDataFile - loads up the file and sticks it into a table of your naming and then returns a DataSet for you to play with.

Enjoy!!!

============ code below

private char m_delimiter

/// <summary>
/// Required to write the Schema.Ini in case the delimiter for the files are not simply CSV
/// </summary>
/// <param name="m_delimiter">System.Char representing the delimter to use in this file</param>
/// <param name="filename">Filename to pass, helping to locate the Schema.Ini and give the header for the section</param>
private static void UpdateSchemaIni(char m_delimiter, string filename)
{
    string _schemaFile = String.Format(@"{0}\{1}", Path.GetDirectoryName(filename), "Schema.ini");
    string _filenameHeader = String.Format("[{0}]", Path.GetFileName(filename)).ToLower();
    string _delimiterLine = String.Format("Format=Delimited({0})", m_delimiter);

    StreamReader _reader;
    StreamWriter _writer;

    string _tempFile = String.Format("{0}.{1}", _schemaFile, DateTime.Now.Ticks);
    while (File.Exists(_tempFile))
    {
        _tempFile += "0";
    }

    if (!File.Exists(_schemaFile))
    {
        using (_writer = new StreamWriter(_schemaFile))
        {
            _writer.WriteLine(_filenameHeader);
            _writer.WriteLine(_delimiterLine);
            _writer.Close();
        }
    }
    else
    {
        bool _successfullyUpdated = false;
        using (_writer = new StreamWriter(_tempFile))
        {
            using (_reader = new StreamReader(_schemaFile))
            {
                bool _entryOnNextLine = false;
                while (_reader.Peek() > 0)
                {
                    string _currentLine = _reader.ReadLine();
                    if (_currentLine.Trim().ToLower() == _filenameHeader)
                    {
                        _writer.WriteLine(_currentLine);
                        _entryOnNextLine = true;
                    }
                    else if (_entryOnNextLine)
                    {
                        _writer.WriteLine(_delimiterLine);
                        _entryOnNextLine = false;
                        _successfullyUpdated = true;
                    }
                    else
                    {
                        _writer.WriteLine(_currentLine);
                    }
                }
                _reader.Close();
            }
            if (!_successfullyUpdated)
            {
                _writer.WriteLine(_filenameHeader);
                _writer.WriteLine(_delimiterLine);
                _successfullyUpdated = true;
            }
            _writer.Close();
        }
        if (_successfullyUpdated)
        {
            File.Copy(_tempFile, _schemaFile, true);
            File.Delete(_tempFile);
        }
        else
        {
            throw new ApplicationException("Unable to updated Schema.Ini file");
        }
    }
}

private static DataTable LoadDataFile(string filename, string tableName)
{
    if (File.Exists(filename))
    {
        UpdateSchemaIni(m_delimiter, filename);
        FileInfo _fileInfo = new FileInfo(filename);

        string _connectionString =
             String.Format(@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Extended Properties='Text;HDR=YES;FMT=Delimited({1})';",
            _fileInfo.DirectoryName, m_delimiter);
        OleDbConnection _connection;
        using (_connection = new OleDbConnection(_connectionString))
        {
            DataTable _dataTable = new DataTable(tableName);
            _connection.Open();
            OleDbCommand _selectCommand = new OleDbCommand(String.Format("SELECT * FROM [{0}]", _fileInfo.Name), _connection);
            OleDbDataAdapter _adapter = new OleDbDataAdapter();
            _adapter.SelectCommand = _selectCommand;
            try
            {
                _adapter.Fill(_dataTable);
            }
            catch (Exception exc)
            {
                throw new ApplicationException(String.Format("Error when trying to create a table from file {0}. {1}", filename, exc.Message));
            }
            return _memberTable;
        }
    }
    else
    {
        throw new ApplicationException(String.Format("Specified file does not exist, unable to load file - {0}", filename));
    }
}

The usefulness of the DateTime Date property

I have run across a number of questions in the asp.net forum that are easily solved with this little-known but very useful property of DateTime objects - namely the Date property. I figured it would be worth a quick post because where it is handy it is very handy...

All Date does is simply zero out the hours, leaving you with the Date set to 12 am (or 00:00:00). This makes it simple, for example to see if two different DateTime objects occur on the say day, regardless of the hour without having to do any fancy multi-property comparisons:

myFirstDateTime.Date.CompareTo(mySecondDateTime.Date)

You could also do the same with using the ToShortDateTimeString and using a string comparison, but besides the conversion (and the issues re strings objects in general) why not use something native to DateTime?

# Thursday, May 28, 2009

Unable to add selected web part(s). Cannot import this Web Part.

Recently ran across this trying to install a customized webpart. Starting going over everything with a fine tooth comb. Even mocked up a .wepbart xml to get it to run.
 
Finally ran it in VS2005 on a 'test' site - turns out I had forgotten to add PUBLIC to the class so that it could be accessed.
 
ooops. won't do that again. actually I probably will...
 
;-)
 
Once again, another verbose Sharepoint Message...