December 23, 2008
@ 10:12 PM

I was trolling Digg tonight and ran across this article about finding rare songs on YouTube. I hadn't really thought about it but I had found some great Pink Floyd videos on there a while back. I've had a song (not Pink Floyd) knocking around in my brain for a while that I was never able to identify; all I could remember was "suckerpunch". So I typed "suckerpunch song" into the search on YouTube and bang, there it was. Actually, after hearing this again (besides realizing it was from around 1993) was how much this sounded like Nine Inch Nails, March of the Pigs (released around the same time). OK, it doesn't quite match up as well as it did in my head (or as well as this), but whatever. It's got the same sort of feel.

That got me thinking about when I saw Pink Floyd at the Pontiac Silverdome; I was trying to figure out what year I went. This, of course, got me thinking about the first concert I ever saw: Iron Maiden at Joe Louis Arena. That led me back to YouTube. And Guitar Hero.

The internets is cool. And a huge waste of time. But mostly cool.

Links:

http://digg.com
http://news.cnet.com/8301-13772_3-10125016-52.html?part=rss
http://www.youtube.com
http://www.youtube.com/watch?v=M_bvT-DGcWw
http://www.youtube.com/watch?v=plBna98XZNQ
http://www.youtube.com/watch?v=U2LwEQFK3qc
http://www.youtube.com/watch?v=7xNQZHuAQJ8
http://ourworld.compuserve.com/homepages/PFArchives/tourdate.htm
http://www.maidenfans.com/imc/index.php?url=tour06_sit/dates06_sit&link=tours&lang=deu
http://www.youtube.com/watch?v=AUOpUqni0_g
http://www.youtube.com/watch?v=e39D8VBQ4sw

Bonus Track (just because this is a great song):

http://www.youtube.com/watch?v=k5JkHBC5lDs


 
Categories: Other

I recently added a maintenance form to our website which allows a user to add and delete entries to a list of banner ads stored in a simple XML file. Previously we've just been maintaining them manually by editing the XML directly and copying the associated banner images into a folder on the website. When you delete an ad, I decided to NOT delete the associated image since they may be reused (and I wanted to avoid forcing the user to upload the image again). However, we'd still like to periodically clean out this folder and "archive" the images so they're not cluttering up the selection screen.

I basically needed some code which would get me a list of files in the banner images folder which were not referenced in my banner XML file. It can be kind of clunky to iterate through XML but they've made it much easier with the introduction of LINQ. I fired up LINQPad (which, BTW, is an AWESOME free tool for testing out LINQ code) and tried out a few ideas. As a side note, it looks like Intellisense is now available if you purchase a copy of LINQPad.

http://www.linqpad.net/

I started with querying the filesystem to get a list of files:

  

DirectoryInfo info = new DirectoryInfo(@"X:\inetpub\wwwroot\images\banner");

FileInfo[] files = info.GetFiles();

 

files.Dump();

 

.Dump() is an extension method available in LINQPad which dumps out the results of the query (we haven't actually used LINQ yet to do anything).

Here's what it looks like:

FileInfoLinq

You might notice that Directory contains a DirectyInfo element. If you click on the down arrow it will expand out these values as well.

So I now had a list of files, I wanted to then get a list of images referenced in my banner file. Here's the format of the XML file:

<News>   
   <NewsItem>
       <Title>Supertooth3-banner.gif</Title>
       <Image>/images/banner/Supertooth3-banner.gif</Image>
       <Height>183</Height>
       <Link>/PortalView.aspx?navto=/Supertooth3-banner.gif</Link>
       <Date>July, 19th, 2003</Date>
       <Target></Target>
   </NewsItem>   

  

 

I haven't really been using Title for anything besides the name of the image file, so I was able to take a bit of a shortcut here and use it for my comparison. To pull out the list of images used in the XML, I wrote this query:

XDocument doc = System.Xml.Linq.XDocument.Load(@"X:\inetpub\wwwroot\adv.xml");

doc.Dump();

 

var news = from item in doc.Descendants("Title")

           orderby item.Value

           select item.Value;

news.Dump();

 

Which produces this:

XmlTitlesLinq

I noticed that the banner images folder contained a bunch of other files that I really didn't want to consider for filtering, so I needed to narrow my query to files that had specific extensions. C# doesn't really have a direct equivalent for INLIST, but we can do this a slightly different way for the same effect.

First I define an array of valid extensions, then (inside the where clause of the LINQ query) I check to see if this list of file types contains the filetype of the file I'm currently evaluating. It's a bit backwards, but it's simple and it works.

string[] fileTypes = { ".jpg", ".gif", ".png" };

var imgFiles = from file in files

               where (fileTypes.Contains(file.Extension))

               orderby file.Name

               select file.Name;

 

imgFiles.Dump();

 

Now I've got a list of files from my XML and a list of files from the banner images folder. I want to get a list of files from the banner images folder that aren't in the XML list. I do this via a final query:

var extra = from singleFile in imgFiles

            where !(news.Contains(singleFile))

            select singleFile;

extra.Dump();   

This returns my extra files. Now I can just use this list to move my images into an archive folder periodically.

If I put it all together, I end up with this:

 

XDocument doc = System.Xml.Linq.XDocument.Load(@"X:\inetpub\wwwroot\adv.xml");

DirectoryInfo info = new DirectoryInfo(@"X:\inetpub\wwwroot\images\banner");

FileInfo[] files = info.GetFiles();

doc.Dump();

files.Dump();

 

var news = from item in doc.Descendants("Title")

           orderby item.Value

           select item.Value;

news.Dump();

string[] fileTypes = { ".jpg", ".gif", ".png" };

var imgFiles = from file in files

               where (fileTypes.Contains(file.Extension))

               orderby file.Name

               select file.Name;

 

imgFiles.Dump();

 

var extra = from singleFile in imgFiles

            where !(news.Contains(singleFile))

            select singleFile;

extra.Dump();   

Links:

http://www.linqpad.net/


 
Categories: .NET | C# | LINQ

November 26, 2008
@ 08:50 PM

I thought this was an interesting question that has come up a couple of times in the last few days over on the Universal Thread. In VFP you can create a subclass of a form, customize it, then configure VFP to use this subclass as the "template" for any new forms you create (by going into Tools > Options > Forms and setting the base class).

The question was, how do I do this in .NET?

It's pretty easy - create your form and add any controls, methods, properties, etc. that you want. Then go to File > Export Template. An Export Template Wizard will open. Change the selection to an "Item template" and click Next.

Export Template Wizard

 

On the next screen put a checkmark next to the item(s) you want included in the template. If you have other dependencies (like external classes) you can also select them here. Then click Next again.

Export Template Wizard Select Item to Export

Now you can select any external references you want to include. For example, if you created a project which includes a set of base controls that you used on your form, you may want to select them here (so when you use this template these references will automatically get added to your project). Click Next.

Export Template Wizard Select Item References

Finally you can select an icon that is associate with this template, enter the template name, description, and it's output location. I'd suggest leaving the "Automatically import the template into Visual Studio" option checked. If you don't like the output location, unfortunately you can't change it here. You have to change it via Tools > Options > Projects and Solutions. Finally click Finish.

Export Template Wizard Select Template Options

At this point, when you do an "Add New Item" you should be able to scroll to the bottom of the templates and see "My Templates" - the new template you created should exist there.

Although I showed this for a WinForms application, this can actually be used with any class (and for ASP.NET applications). For example, I have a base business object class I use. Every time I create a new one I just select a template based on a subclass of it to create the new one.


 
Categories: .NET | Visual Studio

I received this error on a page which had previously been working. That morning I had updated our site, so I was expecting some issues, but this error doesn't really tell you much. We have a maintenance screen which displays a list of records in a grid. When you click on an edit link the grid converts into an "in place" editor (a UI metaphor I thought I'd like when I initially put the form together but which, as it turns out, I really hate).

A user had reported that when they clicked on a button which saves this record that it wasn't closing and updating the list. We are using Telerik web controls for most of the site so I figured it had to do with them since I updated a number of pages, including our master page, to start using the RAD AJAX version of the controls instead of their previous version. It seems this error was being caused because I had added an instance of the RadAjaxManager control to the master page, and I already had an instance of a AjaxManager (older version) on the content page itself. Both managers were conflicting with one another, which caused this error.

All I needed to change was the "RadAjaxManager" markup code to a "RadAjaxManagerProxy" and that resolved this particular error.


 
Categories: ASP.NET

November 8, 2008
@ 10:19 AM

I needed to sign back up for online access to one of my bank accounts. I used to have this all automated through Quicken but at some point it broke and I never seemed to get around to fixing it. I couldn't remember the account password so I requested it to be changed. They had a cool step where they had me select which phone number I had registered with the account would be called. As soon as I hit "Next" they (well, their automated system) was calling. They wanted me to confirm the fact that I was expecting the call and to enter a code which was shown on the screen. As soon as I did that it allowed me to continue on to change my password.

That was pretty cool - a pretty painless way of ensuring I'm really who I say I am. Imagine how disappointed I was when I started typing in my new password:

Bank Password

Really? No special characters and I'm limited to 12 characters for access to my bank account? (sigh). My LinkedIn account has a more secure password than this. Lame.


 
Categories: Soapbox

I'm working on an application which is going to use WCF heavily for communications between a client side application and a server-based service application. One aspect of the application that I've been ignoring during testing was WCF authentication. During testing the application has been running on my local network. I had enabled connection based authentication (ex.SSL) and it is using TCP as it's binding since I do a lot of callback messaging. In the back of my mind I realized that when it's deployed the client will be on a different network, communicating over the internet, so there may be some issues with the current security model.

A week ago I finally got around to looking at it and it completely breaks when the client isn't local, since it was using Windows authentication to do it's authentication. Microsoft has a nice manual which walks through the various ways of configuring security in WCF, which you should choose (and why) under various scenarios. Scenarios, Patterns, and Implementation Guidance for Web Services Enhancements (WSE) 3.0 from their patterns & practices team. Based on a number of factors, it suggested I use SSL along with message based security. I'm not going to be hosting this in IIS, so HTTPS wasn't an option for the SSL connection. I also wasn't using Windows authentication and didn't want to deal with some of the issues of the other credential types (IssuedToken, Digest, etc.). So I decided on UserName.

I now had two things to fix - first, I needed to implement my own UserName authentication scheme (again, I like making my life hard so I wasn't using the default authentication provider in ASP.NET that used SQL Server). This turns out to be pretty simple by inheriting from the UserNamePasswordValidator class in the System.IndentityMode.Selectors namespace and overriding the Validate method:

using System;
using System.Collections.Generic;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.Linq;
using System.Text;

namespace MySampleApp.Server

{

    /// <summary>

    /// This class is responsible for validating the username/password

    /// used by the connecting WCF client.

    /// </summary>

    /// <remarks>

    /// <para>

    /// The server is configured to use this class in the Service Behavior

    /// serviceCredentials section of the config file.

    /// CustomUserNamePasswordValidatorType is set to SampleValidator

    /// UseNamePasswordValidationMode is set to Custom

    /// </para>

    /// </remarks>

    /// <developer>Paul Mrozowski</developer>

    /// <created>10/13/2008</created>

    public class SampleValidator : UserNamePasswordValidator

    {

        public override void Validate(string userName, string password)

        {

            // TODO: Finish, this is just here to test out the idea.

            if (userName != "user" || password != "pass")

                throw new SecurityTokenException("Unknown user.");

        }

    }

}

 

If the passed in username/password is invalid, throw an exception. Easy. I added this new class to my server project, then modified the app.config file to let WCF know to use this for authentication.

<bindings>
      <nettcpbinding>
          <binding name="StandardServerBinding" maxbuffersize="8192000" 
              maxreceivedmessagesize="8192000" listenbacklog="5000" maxconnections="1000"> 
              <readerquotas maxdepth="24" maxstringcontentlength="8192000" maxarraylength="8192000" 
               maxbytesperread="8192000" maxnametablecharcount="8192000" />
              <reliablesession inactivitytimeout="01:00:00" />
              <security mode="Message">                  
                  <message clientcredentialtype="UserName" />
              </security>
          </binding>
      </nettcpbinding>
  </bindings>
        <behaviors>
            <servicebehaviors>
                <behavior name="RegisterBehavior">					
                    <servicedebug includeexceptiondetailinfaults="true" />
                    <servicemetadata />
                    <servicethrottling maxconcurrentcalls="48" maxconcurrentsessions="5000" 
                      maxconcurrentinstances="5000" />
                    <servicecredentials>
                        <usernameauthentication usernamepasswordvalidationmode="Custom" 
          customusernamepasswordvalidatortype="MySampleApp.Server.SampleValidator,MySampleApp.Server" 
                          cachelogontokens="true" cachedlogontokenlifetime="01:00:00" />
                    </servicecredentials>					
                </behavior>
                <behavior name="ThrottlingBehavior">
                    <servicethrottling maxconcurrentcalls="48" maxconcurrentsessions="5000" 
                        maxconcurrentinstances="5000" />
                </behavior>
            </servicebehaviors>
        </behaviors>		
        <services>
            <service name="MySampleApp.ServerProcess" behaviorconfiguration="RegisterBehavior">
                <endpoint name="PrimaryEndpoint" contract="MySampleApp.Server.Contracts.IServer" 
                   binding="netTcpBinding" address="net.tcp://localhost" 
                   bindingconfiguration="StandardServerBinding" />
                <endpoint name="MEXEndpoint" contract="IMetadataExchange" 
                   binding="mexHttpBinding" address="http://localhost/MEX/" bindingconfiguration="" />
            </service>
        </services>

You add a <serviceCreditials\userNameAuthentication section to the ServiceBehavior and specify a Custom userNamePasswodValidationMode. I tell it to use the full name of the class, the second part of that after the comma tells it which assembly it's located in. In the netTcpBinding you can see that I've enabled security on the message, and told it to use the UserName authentication type.

        <bindings>
            <netTcpBinding>
                <binding name="NewBinding0" maxBufferSize="8192000" maxReceivedMessageSize="8192000">
                    <readerQuotas maxDepth="24" maxStringContentLength="8192000"
                        maxArrayLength="8192000" maxBytesPerRead="8192000" maxNameTableCharCount="8192000" />
                    <reliableSession inactivityTimeout="01:00:00" />
                    <security mode="Message">
                        <transport clientCredentialType="None" protectionLevel="None" />
                        <message clientCredentialType="UserName" />
                    </security>
                </binding>
            </netTcpBinding>

I've set the security mode to Message and clientCredentialType to UserName as well. Now I needed to somehow get my client application to pass the user name and password. I had used Visual Studio and let it build my client proxy, so I thought I'd be able to set this immediately after I created the proxy, ex:

InstanceContext context = new InstanceContext(this);

 

this.m_client = new ServerClient(context);

this.m_client.ClientCredentials.UserName.UserName = "user";

this.m_client.ClientCredentials.UserName.Password = "pass";

Unfortunately, that doesn't work - I was getting "Object is read only" when I attempted to do this. From some searching I was able to find out that this can occur when the connection has already been opened, but in my case I hadn't done that yet. I spent a bunch of time trying to figure out why it wouldn't work and finally ended up putting the code inside of the proxy class VS had generated by editing the constructor and setting things up there.

public ServerClient(System.ServiceModel.InstanceContext callbackInstance) :

        base(callbackInstance)

{       

    base.ClientCredentials.UserName.UserName = "user";

    base.ClientCredentials.UserName.Password = "pass";      

}

At this point, I thought I was all set to go. I fired things up and it immediately faulted the connection. I opened up the WCF Service Configuration Editor (C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\svcconfigeditor.exe) and enabled tracing on both the server and client and re-ran things so I could capture a trace. Once that was done I opened up the WCF Svc Trace Log Viewer (which is part of the Windows SDK, mine is located at: C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\SvcTraceViewer.exe). After looking through the log, it appeared to be failing with a "The security protocol cannot secure the outgoing message" error.

Again, I did a bunch of searching, reading, and testing and finally realized that it wanted to encrypt the connection but couldn't. I mentioned earlier that I decided to use SSL for the connection, but when I was working on this I didn't realize that it didn't magically decide how to do that. It looked like I could use an X509 certificate to enable encryption, but I wasn't at all thrilled with the prospect of trying to get that cert. install on client machines. From the couple of times I've seen it needed, it never seems to run particularly smoothly (when it works, it just works, but when it fails, you have no idea why it's failing). So I essentially wanted to be able to load my cert. at runtime instead of loading it into the Windows cert. store. I could buy a real cert., but for this app. it just wasn't necessary. VS includes some tools for self-signing certificates so I looked into that instead.

If you open a Visual Studio command prompt (hiding in the Visual Studio Tools subdirectory of Visual Studio on the Start menu), it sets up the pathing so you can use the various command line tools. The first thing I needed to do was create a key for the server:

makecert -r -pe -n "CN=RCS Solutions, Inc." -b 01/01/2008 -e 12/31/2099 -sky exchange 
Where:
Server.cer -sv Server.pvk Server.cer - the certificate (public key)
Server.pvk - the private key

When it runs, it prompts for a password used to encrypt the private key. You can leave it blank, but it's suggested you fill it in (so I did).

Next I decided to merge the public and private key into a single file with a PFX extension. It requires you to re-enter the password you used to encrypt the private key (or pass in in the command line, which is what I did).

pvk2pfx.exe -pvk Server.pvk -spc Server.cer -pfx Server.pfx -pi mycertpassword

At this point I added all three files to my project. I also needed to get my proxy to use this cert. Since it's not a real certificate (meaning there isn't a chain of trust established between my cert at some known/trusted provider) I had to disable certificate validation. Yeah, pretty secure stuff ;-)

           ServiceHost host = null;

           try

           {

               // Specifically not calling Dispose() on host since it also calls Close and if we've

               // disposed after calling Close that will cause errors.

               host = new ServiceHost(typeof(ServerProcess));

 

               string dir = System.IO.Directory.GetCurrentDirectory();

 

               X509Certificate2 cert = new X509Certificate2(dir + "\\Server.pfx", @"mycertpassword");

               host.Credentials.ClientCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.None;                               

               host.Credentials.ServiceCertificate.Certificate = cert;

 

               host.Open();

 

I repeated the above sequence for my client, creating it's own public/private key. I added the same code to configure the cert, except I put it in the pre-generated proxy constructor.

public ServerClient(System.ServiceModel.InstanceContext callbackInstance) :

        base(callbackInstance)

{       

    base.ClientCredentials.UserName.UserName = "1";

    base.ClientCredentials.UserName.Password = "2";

 

    // TODO: Fill in with correct credentials

 

    string dir = System.IO.Directory.GetCurrentDirectory();

    X509Certificate2 cert = new X509Certificate2(dir + "\\Client.pfx", @"mycertpassword");

    base.ClientCredentials.ClientCertificate.Certificate = cert;       

    base.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.None;         

 

}

 

I thought I was good to go, so I started everything back up. It failed with the same error as before. Yet more searching revealed that I needed to include a copy of the server's certificate inside of the client's app.config (I have no idea why, it seems like it should happen automatically as part of it's public key exchange). The problem was getting it in the format it required. I tried a few of the various ways that were suggested, but didn't have much luck with them. I finally regenerated the client side proxy using the svcutil command line app. and pointing it to a MEX (Metadata EXchange) endpoint and it magically created the certificate value I needed. I cut and pasted that code into my production proxy.

       <client>
            <endpoint address="net.tcp://localhost/" binding="netTcpBinding"
                bindingConfiguration="PrimaryEndpoint" contract="Server" name="PrimaryEndpoint">
                <identity>
			<certificate encodedValue="AwAAAA(long value removed)" />                    
                </identity>
            </endpoint>

When I restarted everything it was finally able to open a connection. I still need to figure out how to get my WCF service and IIS to share the same port, but I'm making some progress at least.


 
Categories: WCF

October 28, 2008
@ 07:38 PM

In case you missed it, it looks like Microsoft is making videos of the PDC sessions available on the Microsoft PDC site available within 24 hours of them occurring, which is pretty cool. It's not particularly easy to figure out where the heck the videos are hiding. They are being posted on the Channel 9 site but it's the same thing - good luck finding them all (at least it wasn't clear to me on how to find them).

However, if you go to the agenda timeline page you can click on a session. At the bottom there is a "View Session Details" link - click on that to view the session. They have one camera view on the speaker, the other on the slides.

Check it out.

Links:

http://www.microsoftpdc.com
http://channel9.msdn.com
http://sessions.microsoftpdc.com/public/timeline.aspx


 
Categories: Conference

October 28, 2008
@ 07:30 PM

I posted some info last night about how to move the TempDB database in SQL Server. That morning I had made the changes and just needed to restart SQL Server for the changes to take effect. This morning I restarted the SQL Server service and after it restarted, tried to log into our website (since it's the main interface to the data in SQL). I received a network connection error from ASP.NET which wasn't surprising, so I reset IIS (IISRESET from a DOS prompt). After it restarted, which only took 10 seconds or so, I tried hitting the site again. And nothing. The page sat there attempting to connect and finally timed out.

Uh oh, not good.

I still had Management Studio open so I tried a few test queries, which seemed to work OK. I opened up the Activity Monitor and didn't see any connections from ASP.NET. OK, so SQL Server is probably denying the connection or blocking for some reason. I took in a look in the new tempdb location and SQL had recreated the mdf/ldf files like I expected, so what's up?

I opened up the Event Viewer and saw a number of entries about the various databases starting up, which was good. Hey, what's that "Failure Audit" entry?

sqleventlog

"Login failed for user 'NT AUTHORITY\NETWORK SERVICE'. [CLIENT: 192.168.1.8]".

Hmmm...192.168.1.8 is the web server. What user does SQL Server run under? I opened up Services, found "SQL Server" and double clicked on it. Then I went to the "Log on" tab. It runs under NT AUTHORITY\NetworkService. Could it be??? I opened Explorer and right-clicked on the folder where tempdb was located and selected "Sharing and Security" then went to the Security tab. Sure enough, "NETWORK SERVICE" wasn't listed. I went ahead and added it to the folder level then test the site again.

Success!

What was really strange about this was that most of our SQL databases are (and have been) located in this same folder. I'm guessing the permissions for those was set on the file level, which is why they've been working OK. The other weird thing was that it was even able to create the tempdb files at all.


 
Categories: SQL

I run across this from time to time and can never remember the specifics, so I figured I'd document it here:

To move a SQL database from one drive to another drive (or just move it to a new folder):

Inside of Microsoft SQL Management Studio execute this script (of course, replace "NameOfDatabase" with the real database name):

  USE master
  GO
  sp_detach_db 'NameOfDatabase'
  GO

Copy the database (usually NameOfDatabase.MDF and NameOfDatabase_log.LDF, assuming they haven't been renamed) to the new location.

  NOTE: The paths are relative to the server SQL Server is running on.

  USE master
  GO
  sp_attach_db 'NameOfDatabase', 'E:\SQLServerDataFolder\NameOfDatabase.mdf', 'E:\SQLServerDataFolder\NameOfDatabase_log.ldf'
  GO

To move the TempDB system database to a new location:

USE master
  GO
  ALTER DATABASE tempdb modify file (name = tempdev, filename = 'E:\SQLServerDataFolder\tempdb.mdf')
  GO
  ALTER DATABASE tempdb modify file (name = templog, filename = 'E:\SQLServerDataFolder\templog.mdf')

Restart SQL Server for the changes to take effect. One it's restarted you can delete the tempdb.mdf and templog.mdf from the old location.

Some instructions for shrinking the tempdb database:

http://support.microsoft.com/kb/307487


 
Categories: SQL

October 21, 2008
@ 09:48 PM

I mentioned a number of default Intellisense scripts I normally set-up in VFP a while back. Since I just set VFP 9 back up on my Vista 64 machine I've been living without them for a while - I was too lazy to follow my own post and add them back in. It's been making me crazy tonight while I try to get some code written so I finally just wrote a quick PRG to add them in (so next time it's even more painless).

It's not particularly smart so don't run it more than once, it will duplicate them.

Download the Intellisense PRG.

Edit: Hmm...It looks like my download code isn't setting the correct filetype when you download it (it's saving as a "HTM" file when it's actually a PRG). Just rename it and I'll fix the download code at some point. This should be fixed.


 
Categories: VFP

October 12, 2008
@ 07:49 PM

How much money is here?

moneypic

 

No cheating! Scroll down when you're ready to guess.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

If you:

  1. Looked at the pile of money for about 15 seconds then said to yourself, "I don't know, maybe $10,000 - $15,000".

Then you're probably not a geek.

If you:

    1. Opened the picture up in a paint program so you could zoom in and see the denominations (or used one of the various magnifying glass apps).
    2. Then counted the number of piles.
    3. Tried to estimate how thick each pile of money was based off of some other aspect of the picture.
    4. Then did a Google search to figure out how thick a dollar bill is (or went old school and grabbed a micrometer and did some tests).
    5. Finally calculated the total.

Then you're a geek.

If you're still not sure whether you're a geek or not...

 

If you're annoyed that I haven't told you how much money is there, or you just don't care, then you're not a geek.

If you're annoyed that I haven't told you how much money is there because you want to know whether you're estimate was right or wrong, or if you just don't care because you've already answered the question, then you're a geek.


 
Categories: Other

September 27, 2008
@ 11:48 AM
One of the really killer features included in SQL Server 2005 was Common Table Expressions. One of the really nice uses for them is recursive queries. Imagine any kind of hierarchical set of date (org. chart, security which allows nested roles, parts/assemblies, etc.). You can use CTE's to walk up or down these trees to build it's result set. Let's look at a simple example of this. I'm going to create a table named "ItemGroups" which is nothing more than a listing of items which have a PK, Title, Description, and foreign key to a parent it may be a child of.
CREATE TABLE [dbo].[ItemGroups](
	[iid] [int] IDENTITY(1,1) NOT NULL,
	[Title] [varchar](60) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
	[Description] [text] COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
	[fk_ItemGroups] [int] NULL,
 CONSTRAINT [PK_ItemGroups] PRIMARY KEY CLUSTERED 
(
	[iid] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

Then, I'm going to add some sample data to this table:


iid     Title                   Description                     fk_ItemGroups
1       Root                    This is the root                NULL
2       Child 1                 This is a child of root         1
3       Child 2                 This is a child of root         1
4       Grandchild 1            This is a child of Child 1      2
5       Grandchild 2            This is a child of Child 2      3
6       Great Grandchild 1      This is a child of Grandchild 1 4

If we draw this out as a "tree", it would look something like this (note the modern looking ASCII art...)

- Root
  - Child 1
    - Grandchild 1
       - Great Grandchild 1
  - Child 2
    - Grandchild 2

OK, great - let's suppose we want to walk up or down this tree from a known starting point. How might we use a CTE to do that?

It might help to understand the basic format of a CTE:

WITH SomeTableName (List of resulting fields)
(
   SELECT -- Starting point or anchor of the query
    UNION ALL
   SELECT -- Recursive portion of the query
)
SELECT -- Final select from SomeTableName

We have the "WITH" portion which describes what our CTE cursor would look like (we can reference this in the recursive portion of the query and in the final SELECT). Then we have the first SELECT which selects the starting record(s) for the recursive portion of our query. It's basically the starting point. The second SELECT pulls in matching records which are children or parents of the record in the anchor portion.

Let's see what that would look like against our table, assuming we want to walk down the hierarchy - in this code, we're going to be starting with the root node.

DECLARE @startNode int
SET @startNode = 1; -- Note the semicolon - it's required for the command
                    -- immediately before the CTE

WITH Items (iid, Title, Description, fk_ItemGroups) AS
( -- This is the 'Anchor' or starting point of the recursive query
  SELECT ig.iid,
         ig.Title, 
         ig.Description,
         ig.fk_ItemGroups
    FROM ItemGroups ig
   WHERE ig.iid = @startNode
   UNION ALL -- This is the recursive portion of the query
  SELECT ig.iid,
         ig.Title, 
         ig.Description,
         ig.fk_ItemGroups
    FROM ItemGroups ig
   INNER JOIN Items -- Note the reference to CTE table name
      ON ig.fk_ItemGroups = Items.iid
)
SELECT *
  FROM Items

If we run this, here's our results (notice that the query automatically stops recursing when no more matches are found).


iid     Title                   Description                     fk_ItemGroups
1       Root                    This is the root                NULL
2       Child 1                 This is a child of root         1
3       Child 2                 This is a child of root         1
5       Grandchild 2            This is a child of Child 2      3
4       Grandchild 1            This is a child of Child 1      2
6       Great Grandchild 1      This is a child of Grandchild 1 4

If we change the starting node to 2 and rerun this, you'll see we only get Child 1 and it's children:

2       Child 1                 This is a child of root         1
4       Grandchild 1            This is a child of Child 1      2
6       Great Grandchild 1      This is a child of Grandchild 1 4

And if we change it to start at Grandchild 1, we get:

4       Grandchild 1            This is a child of Child 1      2
6       Great Grandchild 1      This is a child of Grandchild 1 4

What if we'd like to walk "up" the hierarchy instead? That's just as easy. In the recursive portion of the query, we need to change our join condition. The first query will return the record we want to start on (aliased as 'Item' in this example). To walk up the chain, our fk_ItemGroups will match our parents iid. So change the ON to: " Items.fk_ItemGroups = ig.iid".

Let's rerun the last query:

4       Grandchild 1    This is a child of Child 1      2
2       Child 1         This is a child of root         1
1       Root            This is the root                NULL

It might be useful to know how many levels deep of recursion were required to retrieve a row. We can modify our query to include this info by adding a new column, "Level". In our root query we set it to start at 0, and we increment it in the recursive portion of the query:

SET @startNode = 4; -- Note the semicolon - it's required for the command
                    -- immediately before the CTE

WITH Items (iid, Title, Description, fk_ItemGroups, [Level]) AS
( -- This is the 'Anchor' or starting point of the recursive query
  SELECT ig.iid,
         ig.Title, 
         ig.Description,
         ig.fk_ItemGroups,
         0 AS Level
    FROM ItemGroups ig
   WHERE ig.iid = @startNode
   UNION ALL -- This is the recursive portion of the query
  SELECT ig.iid,
         ig.Title, 
         ig.Description,
         ig.fk_ItemGroups,
         Items.Level + 1 
    FROM ItemGroups ig
   INNER JOIN Items -- Note the reference to CTE table name
      ON Items.fk_ItemGroups = ig.iid
)
SELECT *
  FROM Items

iid     Title           Description                     fk_ItemGroups   Level
4       Grandchild 1    This is a child of Child 1      2               0
2       Child 1         This is a child of root         1               1
1       Root            This is the root                NULL            2

I've mostly ignored the final SELECT * FROM Items, but in a "real" query you tend to use this portion of it to pull in all your detail from various supporting tables.

In a few cases I've found that I've actually needed to walk up and down a hierarchy from a given starting point. I've ended up just creating two CTEs - one to walk up and one to walk down the hierarchy. I insert the results of each of them into a temp. variable, then pull the final results.

DECLARE @curItems TABLE (iid int);

-- Walks up the hierarchy
WITH Items (iid]) AS
( -- This is the 'Anchor' or starting point of the recursive query
  SELECT ig.iid
    FROM ItemGroups ig
   WHERE ig.iid = @startNode
   UNION ALL -- This is the recursive portion of the query
  SELECT ig.iid
    FROM ItemGroups ig
   INNER JOIN Items -- Note the reference to CTE table name
      ON Items.fk_ItemGroups = ig.iid
)
INSERT INTO @curItems (iid) (SELECT iid FROM Items);

-- Walks down the hierarchy
WITH Items (iid]) AS
( -- This is the 'Anchor' or starting point of the recursive query
  SELECT ig.iid
    FROM ItemGroups ig
   WHERE ig.iid = @startNode
   UNION ALL -- This is the recursive portion of the query
  SELECT ig.iid
    FROM ItemGroups ig
   INNER JOIN Items -- Note the reference to CTE table name
      ON ig.fk_ItemGroups = Items.iid
)
INSERT INTO @curItems (iid) (SELECT iid FROM Items)

-- Code which does final select here

DECLARE @curItems TABLE (iid int);

-- Walks up the hierarchy
WITH Items (iid]) AS
( -- This is the 'Anchor' or starting point of the recursive query
  SELECT ig.iid
    FROM ItemGroups ig
   WHERE ig.iid = @startNode
   UNION ALL -- This is the recursive portion of the query
  SELECT ig.iid
    FROM ItemGroups ig
   INNER JOIN Items -- Note the reference to CTE table name
      ON Items.fk_ItemGroups = ig.iid
)
INSERT INTO @curItems (iid) (SELECT iid FROM Items);

-- Walks down the hierarchy
WITH Items (iid]) AS
( -- This is the 'Anchor' or starting point of the recursive query
  SELECT ig.iid
    FROM ItemGroups ig
   WHERE ig.iid = @startNode
   UNION ALL -- This is the recursive portion of the query
  SELECT ig.iid
    FROM ItemGroups ig
   INNER JOIN Items -- Note the reference to CTE table name
      ON ig.fk_ItemGroups = Items.iid
)
INSERT INTO @curItems (iid) (SELECT iid FROM Items)

-- Code which does final select here

As you can see, it's pretty simple to use CTE's. The syntax looks a little weird at first but once you've written one or two queries it's pretty straightforward.


 
Categories: SQL

September 25, 2008
@ 06:51 PM

One of the things missing from the GridSort control I posted about while back was a sample of how it's used. I was thinking of just including a sample form, but I decided to just walk through the steps of using it instead. Let's start off by creating a new form. Next, we'll drop a grid control onto the form and name it "grdSample". Now we need some data to fill in the form - let's use one of the sample tables included in VFP - Customer. Right-click on the form and edit the data environment. Click on Other and navigate to C:\Program Files\Microsoft Visual FoxPro 9\Samples\Data\ and select "Customer.dbf". Now close the data environment. Run the form.

I ended up stretching the grid out a bit to show more of the information and anchoring it so that if I stretched the form the grid would resize. Now we're going to add the rcsGridSort control to the form - I like to just use the class browser to open the class up, click on "gridsort" and then drag and drop the "shape" icon in the upper left hand side of the window onto the form. In the property sheet we're going to need to fill in the cGridEval property of the gridsort control. Enter: ThisForm.grdSample. Now run the form again.

Double-click on the various column headers: an arrow should appear and the column should be sorted. Double-click on the same column and the sort order will flip (if it was ascending it will change to descending or vice-versa). If the images are missing it's because VFP isn't finding them; either add the images to your path or include them in current directory. Or, you can set the pathing in the cSortAscendingGraphic/cSortDescendingGraphic properties of the control.

gs

Like I mentioned in my original post, this control uses the BINDEVENT command which I think was introduced in VFP 8. Therefore, the control requires VFP 8 or later.

Links:

http://www.rcs-solutions.com/blog/2008/07/31/SortingTheVFPGrid.aspx
http://www.rcs-solutions.com/downloads.aspx


 
Categories: VFP

September 24, 2008
@ 07:14 PM

I received a question regarding this post on WCF and what my handlers look like when a client disconnects (either because of a fault or the client connection is closed). It's fairly simple. Here's the code used to hook up the events:

IClientCallback remoteMachine = OperationContext.Current.GetCallbackChannel<IClientCallback>();

OperationContext.Current.Channel.Faulted += new EventHandler(ClientFaulted);

OperationContext.Current.Channel.Closed += new EventHandler(ClientClosed);

 

As a side note, I haven't quite gotten in the habit of using the new/shortened syntax for hooking up delegates. The code above can actually now be written as:

IClientCallback remoteMachine = OperationContext.Current.GetCallbackChannel<IClientCallback>();

OperationContext.Current.Channel.Faulted += ClientFaulted;

OperationContext.Current.Channel.Closed += ClientClosed;

 

At any rate, the code in both handlers is actually the same, so I'll just show ClientClosed:

/// <summary>

 /// Called whenever a client machine's connection is closed.

 /// Automatically removes them from our internal list of clients.

 /// </summary>

 /// <param name="sender"></param>

 /// <param name="e"></param>

 void ClientClosed(object sender, EventArgs e)

{

    IClientCallback remoteMachine = sender as IClientCallback;

 

    this.RemoveClientMachine(remoteMachine);           

}

 

All it does is cast the sender to the IClientCallback interface and call another method which actually removes it from my internal list. Here's what that code is doing (actually, I send out another notification in the real code to any other clients to let them know something has changed). It just locks the list then uses a lambda to find the client in the list, and if it's found, it's removed.

private void RemoveClientMachine(IClientCallback remoteMachine)

{

    if (remoteMachine != null)

    {

        RegisteredClient client;

 

        // Unregister them automatically

        lock (m_callbackList)

        {

            client = m_callbackList.Find(c => c.CallBack == remoteMachine);

 

            if (client != null)

                m_callbackList.Remove(client);                   

        }

 

One interesting failure scenario I found occurred when you had a large number of clients connected and something like your main network line goes down. In some cases I wouldn't receive a notification for every client to remove them from a list (I'm guessing it was firing so many events some of them were being lost). At any rate, the easiest way for me to address this was to include a watchdog timer which would periodically sweep through the connections and attempt to determine if they were still valid. Here's what that looks like:

public void CheckCallbackChannels()

{

    RegisteredClient[] clientList = new RegisteredClient[0];

 

    lock (m_callbackList)

    {

        clientList = new RegisteredClient[m_callbackList.Count];

        m_callbackList.CopyTo(clientList);

 

        foreach (RegisteredClient registeredClient in clientList)

        {

            ICommunicationObject callbackChannel = registeredClient.CallBack as ICommunicationObject;

 

            if (callbackChannel.State == CommunicationState.Closed || callbackChannel.State == CommunicationState.Faulted)

            {

                this.RemoveClientMachine(registeredClient.CallBack);                       

            }

        }

    }                       

}

 

Links:

 http://www.rcs-solutions.com/blog/2008/07/06/WCFNotificationOnDisconnect.aspx


 
Categories: WCF

ASP.NET makes it fairly simple to enable security on content hosted inside of ASP.NET (ASPX pages, ASHX, etc.) just by enabling form-based authentication. Any attempt to access those pages will automatically get redirected to a login page for authentication. However, static content (HTM or HTML pages) for example aren't passed through the ASP.NET pipeline, so anyone can access that content (unless you've set-up something like Basic Authentication) - your user's aren't required to authenticate before accessing the content.

It turns out it's pretty simple to get IIS to pass requests to ASP.NET and let it handle any permissions, logging, etc. that you may want to apply - in fact, this is how ASP.NET itself hooks into IIS. The first thing you need to do is open the IIS Manager. Right-click on the website and select properties. Click on the "Home Directory" tab, then click on the Configuration button. That will display a list of application extensions and the EXE/DLL that is responsible for those filetypes. We're going to handle any files with a .HTM extension, so click on Add. In the "Executable" field enter something like "C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll" - I just copied this from the ASPX extension and pasted it in. In the extension field enter ".HTM". Leave everything else as-is and click on OK. If you want to map HTML files as well, repeat this procedure and enter ".HTML" as the extension (w/o the quotes). Hit OK to all the prompts to close the various dialogs. At this point IIS will forward requests for files with either .HTM or .HTML files to ASP.NET. 

 

Properties

AddExtMap

Now we need to tell ASP.NET how to handle these file types by editing the web.config file to add an HTTP handler for these types. In the system.web section, we're going to map these types to use a built-in handler called "StaticFileHandler". The web.config will look something like this:

<system.web>
   <httpHandlers>
      <add path="*.htm" verb="*" type="System.Web.StaticFileHandler" />
      <add path="*.html" verb="*" type="System.Web.StaticFileHandler" />

Save the web.config. At this point requests for .HTM and .HTML will be passed through ASP.NET and it will pass the request to an instance of the StaticFileHandler class. If you have form-based security, you will automatically be redirected to log in as well before being able to view any HTM or HTML pages (assuming you've protected all pages). The main downside to pushing this through ASP.NET is that IIS can no longer handle accessing HTM/HTML pages, which will reduce the efficiency and scalability of the site. However, with most sites this isn't really much of an issue. The other issue I've noticed is that the StaticFileHandler class automatically sets the caching of the item served up to 1 day (regardless of what you've configured in IIS), and there isn't any way of overridding this behavior short of writing your own handler.

BTW - This can be used to secure other content as well, for example, PDF files, JPG's, GIF's, etc. You would just need to add an entry in IIS like we did for HTM/HTML files and make an associated entry in the web.config. In the example above I was specifically mapping each content type on an as-needed basis. I should also mention that you can also just use a wildcard mapping in IIS (option below the one we used) which will map any file types not in the first list. In that case you don't need to modify the web.config - it should be handled by the DefaultHttpHandler class (ASP.NET 2.0 and later - this isn't the case for 1.1).

If you're interesting in writing your own handler for static files, I did some searching and found this. He includes code for his own implementation of a static file handler which includes compression and caching.

 

Links

http://msmvps.com/blogs/omar/archive/2008/06/30/deploy-asp-net-mvc-on-iis-6-solve-404-compression-and-performance-problems.aspx
 
Categories: ASP.NET | IIS

August 20, 2008
@ 09:43 PM

I'm going to buy one of these just to freak out the neighbors.

 

Links:

http://www.bostondynamics.com/content/sec.php?section=BigDog


 
Categories: Other

CodeRush builds an internal source tree of your C# or VB.NET code. It also has code generating ability - that is, you can take a portion of the tree and generate C# or VB.NET code from it. That got me to thinking about a way of leveraging this ability to build a simple code translator so you could do something like open up a C# file and view the VB.NET equivalent in a tool window (or vice-versa). It turns out the code to do something like this is really straightforward; it's a single method call once you have a reference to the code elements. It doesn't always generate the correct set of code, but it's still really helpful. You basically move the cursor around and the window will display the translated code that is currently "in scope". That is, if you're inside of a method it will show you code for that method. If you move the cursor out to the class, it will show you that class.

To install it, just copy it to your plug-in directory (usually C:\Program Files\Developer Express Inc\DXCore for Visual Studio .NET\2.0\Bin\Plugins\). When you start up Visual Studio a new menu option will appear under DevExpress > Tool Windows > Translator. The window shows a number of different .NET languages, but in reality CR only supports VB.NET and C#.

CR_Translator

BTW - I've mentioned CodeRush/Refactor! and while that's a commercial product you can also download CodeRush by itself for free (and use any third party plug-ins or write your own). You just don't get any of the built-in templates or refactoring tools that the commercial product offers.

Oh, you can download the plug-in from here: http://www.rcs-solutions.com/Download.ashx?File=CR_Translator.zip or from the community plug-in site in a few days.

Links:

http://www.devexpress.com
http://code.google.com/p/dxcorecommunityplugins/


 
Categories: CodeRush | Visual Studio