navigation
 Tuesday, January 30, 2007

First of all, that title was a mouthful, so let me describe what I meant. A custom DataSource is something that you can assign (or databind) to the DataSource property of any bindable control. In programming terms, this means that the DataSource must implement IListSource, IDataSource, or IEnumerable. Well, I don't know about you, but I don't really want to spend a lot of time implementing all the methods of any of those interfaces. That's where Iterators come in; C# 2.0 added a new feature called Iterator blocks that makes it extremely easy to implement the IEnumerator interface, which is the only thing that you must implement in order to fully implement IEnumerable. In this article, I'll solve a real-world problem using this technique.

In application development, it's very common to need to fill a list control such as a DropDownList with a list of valid items, plus a blank item which represents a null value in a data store. However, while it is very easy to simply set some properties on a DropDownList to databind its items to the contents of a DataTable, there is no simple way to add that blank value without dropping into the code-behind. This is no big deal, but it just doesn't feel very clean to fill some DropDownLists declaratively, and have to resort to imperative code for other DropDownLists. One way to solve this problem would be to create a custom control descending from each list control with properties that control the extra blank item, but first of all, you'd have to write a custom control for each list control, and secondly, if we did that, then we couldn't talk about Iterators, could we? So instead, I'm going to create a custom DataSource that returns the contents of a DataTable, plus a blank item to represent null.

Let's proceed with writing the custom DataSource. Using Iterators to implement IEnumerable is as easy as declaring a class that implements it and using the yield keyword to build the return value. Let's declare our ListSource class and let Visual Studio create the method stubs to implement the interface:

using System;
using System.Collections.Generic;
using System.Text;

namespace Falafel.ListData
{
  class ListDataSource : IEnumerable<ListData>
  {
    #region IEnumerable<ListData> Members

    IEnumerator<ListData> IEnumerable<ListData>.GetEnumerator()
    {
      throw new Exception( "The method or operation is not implemented." );
    }

    #endregion

    #region IEnumerable Members

    IEnumerator IEnumerable.GetEnumerator()
    {
      throw new Exception( "The method or operation is not implemented." );
    }

    #endregion
  }
}

Assume the ListData class is a simple data class with two string properties: Text and Value. The first thing to notice is that the parameter list for GetEnumerator is empty, so the class itself will need to be initialized with properties that control the output of GetEnumerator. Let's add some private fields and a constructor to initialize them. New code is in italics:

using System;
using System.Collections.Generic;
using System.Data;
using System.Text;

namespace Falafel.ListData
{
  class ListDataSource : IEnumerable<ListData>
  {
    private DataTable _Table;
    private string _TextField;
    private string _ValueField;

    public ListDataSource( DataTable table, string textField, string valueField )
    {
      _Table = table;
      _TextField = textField;
      _ValueField = valueField;
    }


    #region IEnumerable<ListData> Members

    IEnumerator<ListData> IEnumerable<ListData>.GetEnumerator()
    {
      throw new Exception( "The method or operation is not implemented." );
    }

    #endregion

    #region IEnumerable Members

    IEnumerator IEnumerable.GetEnumerator()
    {
      throw new Exception( "The method or operation is not implemented." );
    }

    #endregion
  }
}

Of course, you could add code to expose the private fields as properties, but since that's not the focus of the article, let's just assume everyone knows how to do that so we can get to the interesting part. Now that we can create an instance of the class with a reference to a DataTable and some field names, we can implement GetEnumerator:

using System;
using System.Collections.Generic;
using System.Data;
using System.Text;

namespace Falafel.ListData
{
  class ListDataSource : IEnumerable<ListData>
  {
    private DataTable _Table;
    private string _TextField;
    private string _ValueField;

    public ListDataSource( DataTable table, string textField, string valueField )
    {
      _Table = table;
      _TextField = textField;
      _ValueField = valueField;
    }

    #region IEnumerable<ListData> Members

    IEnumerator<ListData> IEnumerable<ListData>.GetEnumerator()
    {
      yield return new ListData( String.Empty, String.Empty );
      foreach ( DataRow row in _Table.Rows )
        yield return new ListData( (string) row[ _TextField ], (string) row[ _ValueField ] );

    }

    #endregion

    #region IEnumerable Members

    IEnumerator IEnumerable.GetEnumerator()
    {
      return ( (IEnumerable<ListData>) this ).GetEnumerator();
    }

    #endregion
  }
}

Let's pause for a minute and contemplate the power of what those two lines have. The return type of GetEnumerator is IEnumerator<ListData>, but nowhere are we implementing any of the methods of the IEnumerator interface. Instead, we are simply iterating through a collection and returning each value with yield return. The C# 2.0 compiler does the work of implementing the IEnumerable interface for you.

Well, you could add any bells and whistles to the class that you wanted to, of course. Maybe you want to be able to specify the text and value of the null item, or maybe you want access to those private members through properties. Let's fast-forward past all that and demonstrate the final application of this class: the ability to bind to the DataSource declaratively. To do so, open any aspx page in Source View, locate a list control such as a DropDownList, and add the following attributes to the opening tag:

DataSource='<%# new ListDataSource( ds.MyTable, "MyTextField", "MyValueField" ) %>' DataTextField="Text" DataValueField="Value"

Now, when the control is databound, it will bind to our custom DataSource and fill its items using the ListData objects yielded by GetEnumerator.

In this article, I took two concepts: that all it takes to implement a DataSource is to implement IEnumerable, and that all it takes to implement IEnumerable is to write a little code using the yield keyword. I combined these concepts to demonstrate how to quickly and easily create a custom DataSource that could implement any custom logic desired and still integrate declaratively with all existing ASP.NET controls. If you are in need of .NET training or consulting, please contact us here at Falafel Software.

posted on January 30, 2007  #    by Adam Anderson  Comments [0]
 Monday, January 29, 2007

A couple of days ago I tried to uninstall Telerik's r.a.d.controls 2006 SP3, and the uninstall ran forever before finally failing with an error code. I logged a support ticket and was told that the uninstaller wasn't yet compatible with Vista, but was given a workaround that you might want to know about...

Apparently there is a Microsoft Utility called the Windows Installer Cleanup Utility. You can download it here. It doesn't uninstall programs, but it cleans up their footprint in the installer database and registry. You can then delete the installed files manually and the offending software at least looks like it's gone. Of course, using this brute force method, some things don't get uninstalled, but at least it cleans up somewhat.

If you use this utility, run it before you delete the physical files. It seems that you cant do it the other way round.

posted on January 29, 2007  #    by John Waters  Comments [0]

I have been developing ASP.net Web Applications with Visual Studio 2005 on Windows Vista for a month or so now, and it kind of works. But I have been looking forward to the Visual Studio 2005 SP1, so I was excited to see that Windows Update had tried to install it last night. The install failed, but I set out to figure out what it would take to get it to work on Vista...

The Service Pack can be downloaded manually here. I fetched it and ran it to see what the problem was. It ended up showing me an error box, saying that if I had installed support for Web Application Projects, then I needed to uninstall it before installing the Service Pack, as Web Application Projects are now an integral part of VS 2005 development. I had indeed done that, so I went to the control panel, uninstalled Web Application Projects, and then tried again. Bingo! This time it installed.

Next, I fired up Visual Studio to see if my Web Application Projects would still work. However, I didn't get far - Visual Studio immediately informed me that I needed to get the Visual Studio 2005 Vista SP1. Here is how it is described on the page where you download the regular SP:

"For developers using Visual Studio 2005 on Windows Vista, Microsoft is in current development on an update to Service Pack 1 called the ‘Visual Studio 2005 SP1 Vista Refresh Beta’. This update builds on the improvements made in SP1 and delivers a first class experience for developers wanting to take advantages of the new features in Windows Vista. The Visual Studio 2005 SP1 Update for Windows Vista is expected to ship after the consumer availability of Windows Vista in Q1 of 2007 and is now available in beta."

Wel, I dont have the patience to wait for this to RTM, so I figured I would install the beta. You can get it here.

I went ahead and downloaded the beta. The installation instructions state "On Vista, ensure UAC is turned on". I have turned UAC off, it is the most annoying thing Microsoft created since the Office Paper Clip. But I didnt want to mess up the install, so I turned it back on (which requires a reboot), and then installed the beta (you need to run it as Administrator). That went smoothly, so I turned UAC off again, rebooted, and then started Visual Studio. Cool! No warning messages this time!

Now it was time to see if I could still load my Web Application Projects, so I selected a solution to open and.... it didnt work! Visual Studio told me that I had to install something called "IIS 6 Metabase and IIS 6 Configuration Compatibility". I googled that phrase and ended up on this page. It is a good resource that tells you all the steps needed to run Web Application Projects with IIS (not the built in Development Web Server). One of the steps is to turn on the above mentioned compatibility setting, which you do from the Control Panel, the procedure is well described in the page mentioned above. So, I turned on the compatibilty and tried again!

Lo and behold, this time it worked! As of now, I am back to developing, running and debugging Web Application Projects in Visual Studio against my local IIS, but now with SP1 and the Vista SP1 beta...

posted on January 29, 2007  #    by John Waters  Comments [0]
 Wednesday, January 24, 2007

.NET 3.0 has a number of command line utilities like the service utility (svcutil.exe) that can be awkward to run if you're already in Explorer, deep in a folder structure.  Phillip's svn blog reminded me of a trick with the registry to get the command prompt window populated with the current path.

30netprompt.gif

You can use this technique for any command line or batch file you want to attach to the Explorer context menu. 

  1. Add a key (any name and content appears to work) to HKEY_CLASSES_ROOT\Folder\shell.
  2. Below that add a key "command" and set the text to be whatever command you want executed.  I copied the command line from the .NET 3.0 SDK "CMD Shell" (see registry export listing below), but you could use any command line entry.  The nice thing about the .NET 3.0 cmd shell is that it sets the environment so you can access svcutil.exe and other 3.0 specific utilities.

Registry export listing:

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\Folder\shell\Command Prompt]
@=".NET 3.0 Command Prompt"

[HKEY_CLASSES_ROOT\Folder\shell\Command Prompt\command]
@="C:\\WINDOWS\\system32\\cmd.exe /E:ON /V:ON /T:0E /K \"C:\\Program Files\\Microsoft SDKs\\Windows\\v6.0\\Bin\\SetEnv.Cmd\""

Built in to Windows Vista

If you're running Vista then you're in luck, it's built-in!  Check out this article showing how to shift-right-click a folder to get the "Command Prompt Here" context menu item:

http://blogs.msdn.com/tims/archive/2006/09/18/windows-vista-secret-1-open-command-prompt-here.aspx

posted on January 24, 2007  #    by Noel Rice  Comments [0]

Dr. T takes you through a well-explained tour of C# 3.0's new features. http://blogs.msdn.com/madst/archive/2007/01/23/is-c-becoming-a-functional-language.aspx

posted on January 24, 2007  #    by Adam Anderson  Comments [0]
 Tuesday, January 23, 2007

While it's true that the .NET 3.0 doesn't directly add any ASP.NET functionality, it's also true that the .NET 3.0 install makes changes in your \Framework\v2.x directory.  Although the install introduction warned that a reboot might be necessary, it didn't turn out to be necessary and the install was relatively painless.  ...Untill we tried to install on a server running a 2.x webservice and received an error indicating that assembly ServiceModel.DLL couldn't be found. 

The primary assembly for Windows Communication Foundation (WCF) services is ServiceModel.DLL. Another seemingly unrelated fact is that hosting a WCF service in IIS requires a service file with a ".svc" extension in a virtual directory.  How is the svc file processed?  An Http handler for the "*.svc" extension is associated with ServiceModel.DLL.  And how does the .NET 2.0 based ASP.NET know to suddenly start looking for this service file?  The 3.0 install adds a new http handler to the web.config file in the \WINDOWS\Microsoft.NET\Framework\v2.0.50727\CONFIG directory.

<configuration>
  <system.web>
  ...
    <httpHandlers>
    ...
 <add
           path="*.svc"
           verb="*"
           type="System.ServiceModel.Activation.HttpHandler,
           System.ServiceModel, Version=3.0.0.0, ...

An IIS restart allowed the web service to resume without error.

posted on January 23, 2007  #    by Noel Rice  Comments [0]
 Monday, January 22, 2007
Confused as to how you set up synchronization with your PDA in Vista? I was... here is what I found.
posted on January 22, 2007  #    by John Waters  Comments [0]
 Wednesday, January 10, 2007
Find out about a new network protocol from Microsoft that draws neat maps of your network on your Vista machine, and how to make it work play nicely with XP machines on your network…
posted on January 10, 2007  #    by John Waters  Comments [0]
 Tuesday, January 09, 2007

I've written and revised a reasonable number of DotNetNuke modules, as well as Falafel's course material for DNN, and I think I can probably create binary Private Assemblies in my sleep.

I was a little unnerved, then, when I was getting something ready for an important customer this morning, and discovered some very weird and different behavior in my test environment than in the development environment. Specifically, I had written a module which collected some extended profile information from Joe User when he was logged in, which I didn't want to collect for the host user or for members of the Administrator group. So I'd upload the new module (logged in as host), then log out and go register a new user name, going immediately to the new module page, where the first thing I needed to do was enter the profile information. Dutifully, I would enter the information and submit it. And nothing would get updated. Yet this worked perfectly in my development environment.

Did I say it didn't get updated? That's not exactly true. I was feeling pretty confused, so when Lino the Wise walkd past my office, I grabbed him and said "Boss, I need some help". He listened patiently as I explained the problem, then turned to my keayboard to demonstrate.

And it worked just like it should. Lino laughed, and said it was because he was standing there that the problem got scared and went away. Then so did Lino. And the problem came right back. After a while longer, I got another coworker to come in. And again I explained the problem. And again, the program performed correctly. These two times, out of perhaps a hundred attempts.

I tried lots of things. Rebooting, of course, and iisreset. Clearing out the temporary files. Creating a fresh virtual site with the same name as the old one, then a new virtual site with a different name. Nothing worked.

I finally noticed (while doing some further testing, with a second user name) that the information from a previously logged in user was being displayed. I had been in the habit of logging in as host, uploading the new module, then logging out and immediately registering a new user. I resorted to brute force: I displayed the current user id on the form. When I'd enter and submit the new user's information, I checked and found that DNN's this.ModuleId function said I was userId=1 (host). Since I was disabling profile update through this particular page for host and administrators, my updates were getting thrown away. Apparently the couple of times it did update were when I was explaining the issue to co-workers - thus taking longer, letting the cache time out (??) and by the time I loaded the offending page, everything behaved properly.

I loaded DNN 4.4.0, and the problem went away. I had been developing and testing in DNN 4.3.5, because this was a custom job and that's what the customer was using. And the heck of it is, they (or actually, their users) will probably not ever encounter the problem because of the way the module will be used, one user at a time from computers all around the country. This particular bug hatched to frustrate only the developer. Too bad I didn't find that out more quickly...

posted on January 9, 2007  #    by Rick Miller  Comments [0]