Welcome to AspAdvice Sign in | Join | Help

Moving Blog to RichOnSoftware.com

Please join me as I move my blog to my own URL, RichOnSoftware.com. I'll be posting there from now on and at a much higher frequency. My new blog is using an ASP.NET/C# open source blogging solution: BlogEngine.NET, which I've tinkered with and consequently offers me a high degree of customization over the site layout and features.
Posted by richc | 0 Comments

First Indigo Book Released

Don and Sam beat me to it.  The first Indigo book has hit the shelves: Programming Indigo : Code Name for the Unified Framework for Building Service-Oriented Applications on the Microsoft Windows Platform Beta Edition by David Pallmann.  I'm ordering my copy today ;)  Also keep an eye out for: Inside "Indigo"--Infrastructure for Web Services and Connected Applications by Jason Clark.  It looks as though it should be coming out soon as well.

Happy reading!

Posted by richc | 0 Comments
Filed under:

TechEd Presentations Available on www.msteched.com

For those of you who attended TechEd, all the recorded presentations are now available for viewing online.  Goto http://www.msteched.com and enter your TechEd Id and Password that came on your badge.  There were too many good sessions to choose from and many time slot conflicts. I have a lot of sessions I still want to see.  You know what I'll be doing for the next three days ;) 
Posted by richc | 1 Comments
Filed under:

Adding Functionality to a Typed DataSet using Partial Classes

In continuing with my experimentation with generics... I've noticed that there is no way to get the Typed DataSet code generator to generate a DataRow object, which contains Nullable Types.  There are a number of column properties that seem to elude that the Typed DataSet is capable of doing this type of functionality, however none produce the expected results.  I've spoke with the ADO.NET team during TechEd about this and they said that the feature was cut from the final product due to time concerns, so you may think that you're always stuck with:

public bool IsShippedDateNull() {
    return this.IsNull(this.tableOrders.ShippedDateColumn);
}

public void SetShippedDateNull() {
    this[this.tableOrders.ShippedDateColumn] = System.Convert.DBNull;
}

public System.DateTime ShippedDate {
    get {
        try {
            return ((System.DateTime)(this[this.tableOrders.ShippedDateColumn]));
        }
        catch (System.InvalidCastException e) {
            throw new System.Data.StrongTypingException("The value for column \'ShippedDate\' in table \'Orders\' is DBNull.", e);
        }
    }
    set {
        this[this.tableOrders.ShippedDateColumn] = value;
    }
}

Right?  Wrong! Although the Typed DataSet designer isn't capable of producing code that takes advantage of Nullable (except in the TableAdaptor), doesn't mean you can't do it yourself.  Now you're probably thinking that I must have lost my mind, clearly if you make changes to the Typed DataSet's code file it'll get overwritten the next time it's regenerated. Although that continues to be the case, you may have noticed that Typed DataSets are now partial classes, meaning that we can add and sometimes override most functionality that we want in a separate file without the Typed DataSet code generator destroying our changes.

In my case the Typed DataSet I'm working with has a class name of OrdersDataSet and a Typed Row class named OrdersRow.  In the code below I've added a single nullable column to the partial class, which is just a wrapper for an existing column.

public partial class OrdersDataSet : System.Data.DataSet
{
 public partial class OrdersRow : System.Data.DataRow
 {
  public Nullable MyShippedDate
  {
   get
   {
    object o = this[this.tableOrders.ShippedDateColumn];
    if (o is DateTime)
    {
     return (DateTime)o;
    }
    return new Nullable();
   }
   set
   {
    if (value.HasValue)
    {
     this[this.tableOrders.ShippedDateColumn] = (DateTime)value;
    }
    else
    {
     this[this.tableOrders.ShippedDateColumn] = DBNull.Value;
    }
   }
  }
 }
}

You may be asking what this actually does for you.  First off, here's what it doesn't do... you can't bind to it in the front end, since technically a column named MyShippedDate doesn't exist.  I'm sure we could get the functionality if we really needed it, but that's beyond the scope of this blog entry. Also notice that we're forced to have a different name since we can't conflict with column names from the other partial class generated by the Typed DataSet generator. Coding a column as a Nullable in this manner allows you to work with it better in code. Let's say that you wanted to display Order Date in a label on a page. In the old way you'd have to run the IsShippedDateNull() method to determine whether or not you're capable of calling the property without an exception.  By adding a Nullable implementation you can now just call ToString() on the MyShippedDateDate and receive no Exceptions.  If there is a date, then ToString() will be called on that, otherwise you get an empty string.

Old Way:

OrdersDataSet.OrdersRow or = (OrdersDataSet.OrdersRow)ds.Tables[0].Rows[0];
if (!or.IsShippedDateNull())
{
 lblShippedDate.Text = or.ShippedDate.ToString("MM/dd/yy hh:mm tt");
}

New Way

lblShippedDate.Text = ((OrdersDataSet.OrdersRow)ds.Tables[0].Rows[0]).MyShippedDate.ToString("MM/dd/yy hh:mm tt");

Although the new way is only three lines of code shorter, imagine that applied of twenty different columns. That could drastically affect the readability of code interacting with a Typed DataSet. What would be really nice is to have a Code Generator of some sort that would generate either the Partial Class definition or generate an entirely new Typed DataSet, which has the option of supporting Nullable Columns.

Posted by richc | 3 Comments
Filed under: ,

Asynchronous Reverse DNS Lookup in ASP.NET Beta 2 and 1.1

A common problem when implementing either a hit counter or a download tracker is retrieving the DNS name of the client connecting to your Web Site.  Although there is a property specifically meant for this task on the Request object called UserHostName, most of the time it just returns the IP address.  At this point you may think to yourself that this is a bug in ASP.NET; however it isn't actually ASP.NET's fault.  You have to enable DNS resolution at the server level in order for UserHostName to work as described in this Microsoft KB Article: http://support.microsoft.com/default.aspx?scid=kb;en-us;297795. Problem solved right?  Not at all, if you look at the fine print (the Warnings Section) you'll see this:

Enabling reverse DNS on your IIS server can affect the performance of your Web server and DNS servers. Some examples are:

Resources such as CPU utilization and network bandwidth may be taken up.
Client requests can take longer to process.
Client requests can be blocked if IP restrictions by DNS domain names are used.

Now that's a pretty big deal breaker if you ask me. After seeing those warnings I'd be surprised if anyone went though and actually enable that feature. So what do you do if you need/want to get the domain name but don't want to incur the issue above.  ASP.NET to the rescue!!  Both ASP.NET and ASP.NET 2.0 provide functionality to retrieve the DNS name, however ASP.NET 2.0's syntax is slightly different.  ASP.NET provides Asynchronous methods to retrieve the DNS information.  This is a lot better than enabling for the entire server, since we now have the ability to specify specific pages which will make use of this functionality. Also, by using Asynchronous methods we don't have to worry about the slowness of Reverse DNS Lookup affecting the user experience on page(s) that use them.  

Coding Asynchronous DNS lookups is surprisingly easy.  In order to begin a look you might have something like the code below:

protected void Page_Load(object sender, EventArgs e)
{
 if (!this.IsPostBack)
 {
  //The session is used so that a hit is only counted once.
  if (((string)Session["HitCounted"]) != "1")
  {
   //Use Asynchronous BeginGetHostEntry to retrieve the dns record for this individual
   Session["HitCounted"] = "1";
   //ASP.NET 2.0
   System.Net.Dns.BeginGetHostEntry(Request.UserHostAddress, new AsyncCallback(GetHostAddressCallBack), Request.UserHostAddress);
   //ASP.NET 1.1
   System.Net.Dns.BeginGetHostByName(Request.UserHostAddress, new AsyncCallback(GetHostAddressCallBack), Request.UserHostAddress);
  }
 }
}

The only syntatical different between 2.0 and 1.1 is the Method Name.  The BeginGetHostByName method is marked Obsolete in ASP.NET 2.0.  GetHostAddressCallBack is specified as the AsyncCallback delegate to be called.  Here's that method's implementation:

public void GetHostAddressCallBack(IAsyncResult asyncResult)
{
 string userHostAddress = (string)asyncResult.AsyncState;
 //ASP.NET 2.0
 System.Net.IPHostEntry hostEntry = System.Net.Dns.EndGetHostEntry(asyncResult);
 //ASP.NET 1.1
 System.Net.IPHostEntry hostEntry = System.Net.Dns.EndGetHostByName(asyncResult);

 //Log this:: userHostAddress, hostEntry.HostName);
}

Again the methods only differ by name.  Once in the GetHostAddressCallBack method you can retrieve the hostEntry by calling the EndGetHostEntry method (in 2.0) or the EndGetHostByName method in 1.1.  As with BeginGetHostByName, EndGetHostByName has been marked obsolete in 2.0.  Once you have the HostEntry, you can retrieve the DNS name by reading the HostName property. That's all there is to it.

 

Posted by richc | 0 Comments
Filed under: ,

New ASP.NET 2.0 Starter Kit: WebJots

I've been working on my ASP.NET 2.0 Site WebJots for about a month and some odd weeks now.  A few weeks ago, after the first phase release, Brian Goldfarb suggested that if I wanted to open up the source code to WebJots that the best option would be to make it into a Starter Kit. 

Well after a few weeks of bug fixes, code cleaning, refactoring, and enhancements it's ready for download!

Download the Starter Kit here: WebJots WebSite Starter Kit for ASP.NET Beta 2
See it live here: WebJots (login as user “demo“ pass “demo“)

The starter kit is compatible with Visual Web Developer 2005, Visual Studio 2005 Standard, Professional, and Team Suite Editions. If you have any ideas, issues, criticisms, etc... at all, please let me know.  If you like it, give me shout out ;)

Update: In my excitement I neglected to mention what WebJots is all about, thanks for the heads up Jim.  Well... here it is:

WebJots is an easy to use website, which allows you to quickly save, retrieve, and organize your notes and ideas (jots), using a hierarchical categorization system and FreeTextBox for Rich Text Jot entry.

Posted by richc | 2 Comments
Filed under: ,

Nullable Support in ADO.NET

If you've been experimenting with generics lately, specifically the Nullable<T> generic, you may have noticed the following pitfall in their implementation.

You can't pass a Nullable<T> as the value parameter of a SqlParameter

Using Something like the following code...

public DataSet GetOrders(DateTime? StartOrderDate, DateTime? EndOrderDate)
{
 using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["NorthwindConnectionString"].ConnectionString))
 {
  DataSet ds = new DataSet();

  SqlCommand cmd = new SqlCommand("select * from orders " +
   "where (@StartOrderDate is null or OrderDate > @StartOrderDate) " +
   "and (@EndOrderDate is null or OrderDate < @EndOrderDate) ", conn);
  cmd.Parameters.AddWithValue("@StartOrderDate", StartOrderDate);
  cmd.Parameters.AddWithValue("@EndOrderDate", EndOrderDate);

  SqlDataAdapter da = new SqlDataAdapter(cmd);

  da.Fill(ds);
  return ds;
 }
}

will produce an ArgumentException with a message "No mapping exists from object type System.Nullable`1[[System.DateTime, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] to a known managed provider native type." will be thrown.  This certainly isn't the expected outcome. One would hope that it would just accept the value and continue along its merry way, however that just isn't the case.

While at TechEd 2005 last week, I talked to some of the ADO.NET team and they said that the functionality had been cut from the product for performance and time considerations.  Although I can see how performance could be an issue, due to testing for INullable on every parameter passed in, I think the user experience would be improved by allowing the Nullable<T> to be passed in.  Since the main reason it was excluded is due to performance considerations, maybe adding overloads to the cmd.Parameters.AddWithValue  method taking each individual nullable type as a parameter would fix things up, without producing a performance hit.  So you could still add parameters normally, however if you're using a Nullable<T> then and only then you would incur a minor performance hit, since with overloads you wouldn't need to use the “is“ operator.  Otherwise developers are either going to not realize there's an issue or develop the checking themselves, in which case they'd incur the performance hit anyway.  Here's the method i use in order to create parameters containing Nullable<T> values.  Note: By no means is this as performat as creating a overload for each Nullable<T> such as int?, DateTime?, etc, but it'll do for my small little app.

public System.Data.IDataParameter GetDataParameter(string Name, object Value)
{
 if (Value is INullableValue)
 {
  INullableValue nullableValue = (INullableValue)Value;
  if (nullableValue.HasValue)
  {
   return new SqlParameter(Name, nullableValue.Value);
  }
  else
  {
   return new SqlParameter(Name, DBNull.Value);
  }
 }
 else if (Value == null)
 {
  return new SqlParameter(Name, DBNull.Value);
 }
 return new SqlParameter(Name, Value);
}

If anybody has anything better than this, I'd be interested in hearing from you.

 

Posted by richc | 2 Comments
Filed under: ,

Pre-Conference - The Zen of ASP.NET 2.0 with Jeff Prosise

I had the pleasure of attending The Zen of ASP.NET 2.0 with Jeff Prosise today from 1 to 6 PM.  Jeff did an excellent job, pretty much going through most of the new 2.0 stuff in about 5 hours. Unfortunately Jeff's presentation ran over so we missed out on Web Parts and Localization.

I've been working with ASP.NET 2.0 a lot lately, developing WebJots, and I still learned quite a bit from Jeff's session.  Although not an all inclusive list of the things in Jeff's presentation, here are a few things I learned while in there.

  • The Web Configuration Tools only works locally now due to security concerns
  • The aspnet_compiler.exe can be used to precompile as opposed to the IDE
  • You can view the partial classes ASP.NET generates for you in .NET's system folders
  • Two different controls referencing the same data source control will cause the datasource control to generate 2 requests to the database.  So... it's important to enable caching in this circumstance.
  • DataSources only expose the first table in a return set to binding
  • You can have multiple contentplaceholders in a page using a master page (for some reason I thought you could only have one contentplaceholders in a page)
  • When caching is enabled in DataSources it automatically differs based on the parameter values
  • The SqlDataSource control can talk to any database supported by ADO.NET
  • SQL Cache is natively supported in SQL 2005, but in earlier versions of SQL ASP.NET polls the database.
  • To Programmatically change master pages at run time you must handle the create_init of the page, since this has to be done even before page_init occurs.
  • The LoginView can be used to display different templates based on the logged in state of the current user.
  • Membership services are available on win forms
  • Use the CacheRolesInCookie attribute in the web.config to prevent round trips to the database checking for roles.
  • You can add custom types to be stored in a user's profile.
  • Profiles aren't enabled for anonymous users by default.
  • Profile properties by default use the XmlSerializer, which can't serialize non-public properties.  Using the SerializeAs=Binary on the profile property resolves this issue.
  • The navigation controls only ship with an xml provider; in Jeff's opinion a Sql Provider should be shipped as well.
  • Config API allows writing to the Web.Config during run time without necessarily requiring a restart of the app domain (I've been waiting for this one for a while)
  • The Config API can encrypt sections of the Web.Config out of the box.
  • DES encryption uses DPAPI, which uses machine info to encrypt/decrypt the file.  A config file using DES encryption can only be decrypted on the machine that encrypted it.
  • ASP.NET 2.0 Supports Asynchronous pages by setting the Async attribute to true in the @Page declaration.

As you can see I learned quite a bit ;) In my book, this was a very impressive presentation.  Good job Jeff and thanks for the session!!!

Posted by richc | 0 Comments
Filed under: ,

TechEd 2005 First Impressions

I arrived at TechEd today.  Being a TechEd newbie I have to say I'm pretty amazed at the whole set up.  Microsoft really did an excellent job setting this event up.  From the free shuttles buses, to the food tables filled with twinkies, ho ho's and various fruits, to Comm Net terminals, everthing seems very well planned.  I mean where else can you find twinkies, ho ho's, and fruits together on the same buffet table?  That in itself makes it all worth the while.

Plus there's all the goodies you get when you check in.  I think I have enough development magazines to read through for the next year, honestly I think I have 20.  There's also plenty of free software to play with: Visual Studio 2005 Beta 2 with Sql Server 2005, Windows Server 2003 Enterprise x64 Edition, Partner Resource DVD, Virtual Server 2005 Service Pack 1 Beta Enterprise Edition, and Microsoft System Center Data Protection Manager to name a few.

There are way too many good sessions and labs to pick from as well, even now I'm still reorganizing my schedule to fit more in.  I'm going to be hitting the Convention Center from 7 AM to 10 PM everyday. 

Looks like it's going to be a great week!

Posted by richc | 0 Comments
Filed under:

Making ASP.NET Buttons Look Better using CSS Rollovers

CSS Rollover Buttons have been around for quite some time.  Basically, using CSS you can easily create roll over buttons, which previously had to been done in Javascript. CSS Rollover buttons also allow you to overlay text on top of a generic button image.  No more photoshopping 30 buttons all with different text. The same techniques used to create these fancy CSS buttons can also be applied to .NET Buttons for a sharper looking interface.

Take a look at the login button at http://www.webjots.com for an example of what a CSS Rollover Button could look like.

Not all CSS Rollovers are Created Equal...

There are several different ways to implement CSS buttons.  Techniques swapping the images exclusively in the CSS file end up flickering when viewed in the browser.  The best technique for creating CSS style buttons involves setting the background of a div to the hover image, overlaying the div with the button image and then hiding the overlayed image during rollover, this creates a seamless transition from one button state to another.

All methods revolve around the a:hover attribute provided in CSS. Buttons using this technique must ultimately be rendered to the client as an anchor <a> tag, meaning that you'll need to use a Link Button in ASP.NET.

First off let's take a look at the html code

<div class="SmallButton">
 <asp:LinkButton ID=lbSaveButton runat=server OnClick="lbSaveButton_Click">
  <img src="../Images/SmallButton.gif" border=0 alt="Save Category" />
  <span>Save</span>
 </asp:LinkButton>
</div>

So... it does require more lines of code than a regular button, but it looks reaaaalllly good, you'll see ;)  The class SmallButton is where all the work is done.  Here's the CSS classes for Small Button.

.SmallButton
{
 position: relative;
 background-image: url(Images/SmallButtonHover.gif);
 background-repeat: no-repeat;
 white-space: nowrap;
 display: block;
 width: 79px;
 height: 28px;
 margin: 0;
 padding: 0;
}

.SmallButton a
{
 display: block;
 color: #000000;
 font-size: 11px;
 width: 79px;
 height: 28px;
 display: block;
 float: left;
 color: black;
 text-decoration: none;
}

.SmallButton img
{
 width: 79px;
 height: 28px;
 border: 0;
}

* html a:hover
{
 visibility:visible
}

.SmallButton a:hover img
{
 visibility:hidden
}

.SmallButton span
{
 padding-right: 0px;
 padding-left: 0px;
 left: 0px;
 padding-bottom: 0px;
 margin: 0px;
 cursor: pointer;
 padding-top: 0px;
 position: absolute;
 top: 5px;
 width: 79px;
 text-align: center;
 color: DarkBlue;
}

Notice the background image url for small button is the hover image, which is intentional since this image is overlayed with the normal state button image.  In the “* html a:hover” class you can see that globally hovering over an image embedded in an anchor will hide that image, so be careful.  Also, take note that the CSS classes for Small Button are displayed in block mode.  Block mode is necessary to get the button to display correctly.  The one caveat of block mode though, is that if you want to put two of these buttons next to each other you're forced to use a table.  The rest of the classes are just there to style the button.

For more information about the CSS Rollover Buttons check the following links:
http://www.search-this.com/website_design/css_rollover_buttons.aspx
http://sophie-g.net/jobs/css/e_buttons.htm

Posted by richc | 2 Comments
Filed under:

Quick N' Easy Health Monitoring in ASP.NET Beta 2

Traditionaly in ASP.NET 1.1 if you wanted to be notified through email of an unhandled exception, you'd have to add code to your Global.asax.cs handling the Application_Error event and subsequently log the error or send yourself an email through smtp.  In .NET 2.0 this functionality is already built in. All you have to do is add a few dozen config lines to the Web.Config and you can have it working in under 10 mins. 

Here's the first set of configuration options you have to add to the Web.Config in the <system.web> section.

  <healthMonitoring enabled="true">
   <providers>
    <add name="CriticalMailEventProvider"
      type="System.Web.Management.SimpleMailWebEventProvider"
         from="
from@somewhere.com"
         to="
to@somewhere.com"
         bodyHeader="Warning!"
         bodyFooter="Investigate ASAP."
         subjectPrefix="Exception Notification."
         buffer="true"
         bufferMode="Critical Notification"
         maxEventLength="4096"
         maxMessagesPerNotification="1"/>
    <remove name="SqlWebEventProvider"></remove>
    <add connectionStringName="Sql2005"
         maxEventDetailsLength="1073741823"
         buffer="false"
         bufferMode="Notification"
         name="SqlWebEventProvider"
         type="System.Web.Management.SqlWebEventProvider,System.Web"/>
   </providers>
   <rules>
    <remove name="All Errors Default"/>
    <remove name="Failure Audits Default"/>
    <add name="All Errors Default" 
         eventName="All Errors"
         provider="SqlWebEventProvider"
         profile="Default"
         minInterval="00:00:30"/>
    <add name="Request Processing Errors"
         eventName="Request Processing Errors"
         provider="CriticalMailEventProvider"
         profile="Default"/>
   </rules>
  </healthMonitoring>

In the Providers section listed above, there are two providers listed: CriticalMailEventProvider and SqlWebEventProvider. The CriticalMailEventProvider sends emails upon errors, while the SqlWebEventProvider creates table entries in the table: aspnet_WebEvent_Events, which is created using aspnet_regsql.exe.

Customization of the CriticalMailEventProvider is pretty straight forward, except for where the mail server is entered. This brings us to the 2nd section required in the Web.Config. Add the following section under the <Configuration> element.

<system.net>
    <mailSettings>
        <smtp deliveryMethod="Network">
            <network host="mail.yourhost.com"
                port="25"
                from="
webmaster@yourwebsite.com"/>
        </smtp>
    </mailSettings>
</system.net>

Configuring the smtp host is also straight forward. With this section added, the CriticalMailEventProvider configuration is complete.  The SqlWebEventProvider really only requires you to enter your database name in order to function properly.  In my case, I'm using Sql2005 as my database name.

    <add connectionStringName="Sql2005"
         maxEventDetailsLength="1073741823"
         buffer="false"
         bufferMode="Notification"
         name="SqlWebEventProvider"
         type="System.Web.Management.SqlWebEventProvider,System.Web"/>

You should change the connectionStringName attribute to whatever your connection string is named.  Once done this section's configuration is also complete.  Notice how I have a remove node before the configuration of the SqlWebEventProvider “<remove name="SqlWebEventProvider"></remove>“. This is because .NET provides a default implementation of this provider which uses a default name for the database “LocalSqlServer“.  The remove node allows me to remove that default configuration.

The rules section specifies when a certain provider should be used. 

   <rules>
    <remove name="All Errors Default"/>
    <remove name="Failure Audits Default"/>
    <add name="All Errors Default"
         eventName="All Errors"
         provider="SqlWebEventProvider"
         profile="Default"
         minInterval="00:00:30"/>
    <add name="Request Processing Errors"
         eventName="Request Processing Errors"
         provider="CriticalMailEventProvider"
         profile="Default"/>
   </rules>

looking at the configuration above you can tell that the SqlWebEventProvider is used for “All Errors“, while the CriticalMailEventProvider is only used for “Request Processing Errors.“  By default ASP.NET is configured to log eventName “All Errors” to the EventLog.  Also by default, ASP.NET handles an additional eventName “Failure Audits“ which is again sent to the EventLog.  Since I'm hosted on a shared server I don't get to see the EventLog entries, so there is no point to logging them.  Just as before I've used the remove nodes to remove the default configuration provided by ASP.NET.  I've listed the default configuration for the rules section below:

<rules>
    <add name="All Errors Default"
        eventName="All Errors"
        provider="EventLogProvider"
        profile="Default"
        minInstances="1"
        maxLimit="Infinite"
        minInterval="00:01:00"
        custom="" />
    <add name="Failure Audits Default"
        eventName="Failure Audits"
        provider="EventLogProvider"
        profile="Default"
        minInstances="1"
        maxLimit="Infinite"
        minInterval="00:01:00"
        custom="" />
</rules>

You can get all the default Web.Config by taking a look at the Web.Config stored in C:\WINDOWS\Microsoft.NET\Framework\v2.0.50215\CONFIG.

For even more details about the HealthMonitoring section take a look at Fredrik Normén's blog post How to raise events in ASP.Net 2.0 with the health monitoring feature.

 

Posted by richc | 2 Comments
Filed under: