November 15, 2007
@ 09:32 PM

It looks like XSource for VFP 9 SP2 is now up on the Microsoft website:

http://www.microsoft.com/downloads/details.aspx?familyid=320b6438-be76-48c7-a68e-1792e2aa3bf2&displaylang=en

The interesting thing about this is that it's now licensed under the Microsoft Permission License so you can now distribute it or your own custom modifications of any of the tools included in it. This is basically all the source code for things like the DataExplorer, the Report Builder/Output/Preview controls, the Task Pane, etc.

Reporting bug in VFP 9 SP2

Cathy Pountney has an article regarding a new bug introduced into the reporting engine in SP2 using grouping and group headers.

http://cathypountney.blogspot.com/2007/11/gotcha-serious-report-bug-with-data.html


 
Categories: VFP

November 14, 2007
@ 10:11 PM

This falls into the "hey, I didn't realize this was available" category. In the 2.0 version of the .NET framework, support was added for nullable types, I'm guessing primarily for improved interaction with databases. Previously, nulls were a real pain to deal with; now they're only a bit more work (the pains not completely gone, but it's not as bad anymore). Lets say you've got a date time field in your database, it can have an actual date filled in or it can be null. In prior versions, you had to rely on "magic" dates to do translations when loading and saving your data (eg. 1/1/1900 is really null). Now you can just define a nullable type by prefixing the type with a question mark ?

 

DateTime? activeStart = (DateTime?)row["ActiveStart"];

 

That's not the point of this post though. They've added a nice operator ?? - so let's suppose I've got a calendar control that returns null if no date has been selected. I'd like to default to the current date if that's the case. I can write code like this:

 

DateTime start = System.DateTime.Now;

if (this.startCalendar.SelectedDate != null)
   start = this.startCalendar.SelectedDate;

 

Or I can use the ?? operator:

 

DateTime start = this.startCalendar.SelectedDate ?? DateTime.Now;

 

That's much simpler code to understand (at least to me). I like this a bit better than the normal ternary "if" support, which I always find a bit difficult to read:

string sample = a==b ? "A=B" : "A<>B" 

Maybe it will grow on me eventually...
 
Categories: C#

November 6, 2007
@ 10:15 PM

The question shouldn't be, "Can we do this?" but, "Should we do this?".

I think companies sometime get so excited about the possibility of doing "X" that they forget to ask themselves if it's something that should even be done:

  • What are the risks?
  • Does the payoff justify the amount of work required?
  • Are there more important things that should be done first?
  • What is the simplest way of accomplishing our goal (it may not even include doing "X")?
  • Is this our core business or should we look to an outside source?
  • Does this even make sense?

 
Categories: Soapbox

November 6, 2007
@ 10:05 PM

I've been using Subversion along with TortoiseSVN (and VisualSVN inside of Visual Studio) for a few months now. It's been relatively painless, up until recently when I've attempted to really start using it for more than the standard "make changes, check it in". Unlike Visual SourceSafe where you check out a file and it gets locked until you check it back in (which is just the default; it can actually be changed to allow shared checkouts), Subversion lets everyone use all the files all the time, then merge on check in. I haven't attempted to try Subversion with any of my VFP projects yet, it's been strictly the .NET applications, so I haven't had to try to deal with merging the binary files created by VFP (eg. for forms, the SCX and SCT files, or for classes the VCX & VCT files). If you're enterprising, you can try out TwoFox from Christof Wollenhaupt. I'm leaving that hill for another day.

I ended up moving around my repository to support the more standard folder structure, since I didn't do this when I first started out. So my folders are now in this format:

branches
tags
trunk
  |--Project A
  |--Project B

The other suggested route was to put the branches/tags/trunk folder inside the project folders themselves, ex.

Project A
  |--branches
  |--tags
  |--trunk

This looked a little too confusing, especially since most of our projects are build into a single solution. To move everything around, I opened TortoiseSVN and went into the repository (right-click in Explorer, select Repo-Browser). Then I navigated to where I had my .NET development stuff, \Server\DotNetDev, right-clicked and selected Create Folder. I created the branches folder (which is meant, strangely enough, for any branches in the source code), then created the tags folder (this is equivalent to a label in VSS, although Subversion doesn't make a distinction between a branch and a tag), then the trunk folder (which is meant to hold the primary set of source code).

Then I dropped down to a command prompt and moved everything from it's original location to it's new location:

svn move -m "Relocating Project" svn://Server/DotNetDev/Project svn://Server/DotNetDev/trunk/Project

I kept the Repo-Browser open and hit F5 to make sure the change took place (it did, so far, so good). The rest of this didn't go so smoothly.

I needed to point my local copy of the source to the new repository location. It turns out there are two different options to do something like this: Relocate and Switch. I thought I might want Relocate, so inside of TortoiseSVN I right-clicked on my DotNetDev folder and selected TortoiseSVN > Relocate. Here are my notes of how this went:

- Immediately see scary message about me doing this, did some Googling, decided I really DO want to relocate.
- Get another error about it not being the root. Decided to try Switch instead.
- Get another error about one of my projects not being in the same repository.
- Realize I may have mistyped the new repo. name, try svn://Server/DotNetDev  to svn://Server/DotNetDev/trunk instead.
- Get errors about it not existing in that location for that revision.
- Great.
- Select the option to Update to Revision.
- Suddenly panic that I'm going to lose my changes that haven't been checked into source control yet. Wish I had thought to make a backup first.
- It doesn't seem to help.
- Realize I had one folder which still pointed to it's original source on the web (I'm using an open-source project that I had grabbed via TortoiseSVN, so it's still pointing to that website)
- Try to relocate it and get errors about relocating only changing the repository part of a URL (regardless of what I try to change)
- Get irritated and go to lunch.
- Get back from lunch.
- Delete the .svn folder that was pointing to the wrong server (the open source one).
- Make a backup of the directory where the .svn folder was.
- Get new version via SVN checkout for that specific folder.
- Try to use Switch.
- Get error about needing to cleanup the project.
- Cleanup.
- Try to use Switch again.
- Get a different error about some file not being able to be added to the repository because it already exists (it's the same folder I've been having problems with).
- Erase the entire folder including the .svn folder this time.
- Do a checkout on this folder again.
- Finally get around to making a backup of all my files, just in case.
- Try Switch again, it seems to do something and not give any errors.
- Ran a diff on the folder I had problems with to check to see if I lost anything and/or grab files from my backup.
- Fire up VS and force a rebuild to see what/if anything was broken.
- All good (!)

Relocate = Source is switched to a completely different server.
Switch = Source is on the same server in a different directory.

I'm not entirely sure why these are two different options.

Fast forward two weeks. I decide to test out the idea of creating a private branch where I can make all my daily changes (and do a check in at the end of the day). I basically don't want to check in stuff that isn't complete, may break things for other developers, etc. But I want to make sure it gets checked in daily so it's backed up. Branching seemed like a good way of handling this. The initial branch seemed to go smoothly so I made changes over the next week or so and kept checking them in. Today I decided to merge the branch back into the trunk so we could get a good copy of the files on another dev. machine. It seemed to go about as well as moving the repository (I'll spare you all the ugly details).

Ultimately, I think the problems were related to the fact that I had a completely different mental model of how merging a branch should work: I though you would just select the option to merge, then select the branch you want to merge from (my local copy) and select the branch you want to merge to (the trunk), let it merge and resolve any conflicts (if any). The UI even looks like this is how it should work. It doesn't.

I must have tried merging 20 different times, each time with different errors or issues. I read and re-read the docs and just didn't seem to understand how this was supposed to work. Here's what finally worked for me:

I had to check in all of my changes into the branch I had been working with, then use "Switch" to switch back to the trunk copy (which does some scary things like delete source code in your local copy). Once I had a copy of the trunk, I did the Merge again, pointed the "From" option in TortoiseSVN to the trunk, not my local copy. Pointed the "To" to the branched copy I had been working on. Then I selected the last revision before the branch in the trunk and all of the revisions in my branch, and it seemed to merge OK. Then I committed my local copy back into the trunk.

It appears you need to track the revision numbers manually (the check-in notes are probably the simplest) when merging branches back in; so, for example, if I had revisions 50-55 in a branch which I then merge into the main trunk, then make changes 56-60 in the branch again, I have to make sure I only merge revisions 56-60 into the trunk. Otherwise, I risk duplicating changes (essentially, Subversion doesn't track your merges). I looked at the roadmap and it appears that functionality is scheduled for build 1.5 (it's currently at build 1.4.5). That's definitely going to be a welcome enhancement.

One thing I noticed in the docs is that they mention that new Subversion users have problems with the From/To issue, and with picking the correct revision to merge against. It's pretty clear they're forcing users to deal with implementation details that I think should be hidden, so that the mental model most of us have of all this actually matches what we need to do in the UI to accomplish things. Having to mention that most developers make mistake "X" or "Y" should be a red flag that maybe things need to be changed.

The one saving grace of all this is that I haven't lost any changes yet. I'm starting to get in the habit of checking all my files in before I attempt to do anything out of the ordinary (which seems to be a good practice to get into).

 

Links

http://subversion.tigris.org
http://tortoisesvn.tigris.org
http://www.visualsvn.com
http://www.foxpert.com/downloads.htm


 
Categories: Source Control

November 2, 2007
@ 08:31 PM

One thing I've learned while using SQL Server is (whenever possible), perform as much as you can on the server. That means either stored procedures or SQL commands passed up to the server. It also means thinking about problems as sets, as opposed to a more procedural approach. Visual FoxPro makes it easy to blur the lines between SQL commands and normal procedural code (mixing and matching as you'd like), so you tend to start getting locked into thinking about data on those terms. While some of this translates directly to SQL Server (obviously, the SQL language itself), other things don't.

I recently decided to add a new foreign key to a table; the structural specifics aren't particularly important so I won't go into too much detail about them. Essentially, I had a parent (A) and child table (B). There is a posting process which takes a snapshot (C) of some or all of the records in the child table for a specific parent. This snapshot includes a foreign key back to the original child record but didn't include a foreign key back to the parent (since it could be derived by joining to this child table). I began to realize that, because of the number of records involved (it could be as many as 250,000 records in the child (B) for a single parent record (A), which meant up to 250,000 records in our snapshot table) that most of the queries were really slow because of the extra hop involved in resolving this parent key. So, I decided to go ahead and add a foreign key into the snapshot table (C) to the parent (A). That also meant I needed to go back and fill in the new foreign key in the Snapshot (C) table.

 

Table

 

In VFP, you might just open the tables, SCAN through the Snapshot (C) table, do a SEEK into the Child (B) table for the foreign key back to the Parent (A) table and save it into the Snapshot (C) table. That works OK if the tables are local, what about with SQL Server? I guess you could take the same approach; pull a copy of the records from the Snapshot (C) and Child (B) tables locally, index them, then run through the same SCAN loop (or if you're really old school, set a relation and just do a REPLACE). But how long will that take - remember we're talking about pulling down up to 250,000 records in the Child (B) table, plus another 250,000 records from the Snapshot (C) table, then updating all of the records in Snapshot (C) table. Multiply that by the number of master records (in my case, only 20-30 of the master records had anywhere near 250,000 records but we're still talking millions of records).

Suddenly the above approach doesn't look like such a good idea. Thankfully, SQL includes provisions in the UPDATE command to do exactly what I wanted to. It would still take a while to run (I clocked it around 25 minutes), but it was a significantly better solution than a SCAN loop. Here's what the query ends up looking like:

UPDATE Snapshot
   SET Snapshot.fk_Parent = Child.fk_Parent
  FROM Child
INNER JOIN Snapshot
    ON Snapshot.fk_Child = Child.PrimaryKey

 

So what does this actually mean? We're basically saying, inside of our Snapshot (C) table set our new foreign key equal to the Parent (A) foreign key in the Child (B) table. The FROM/INNER JOIN defines how to match the record in the Snapshot (C) to the one in Child (B). Simple and fast.


 
Categories: SQL | VFP

October 29, 2007
@ 08:48 PM

Here's a link to an article about keys in SQL. Make sure you read the comments. I wonder where most people fall on this subject and the split (if any) between developers and DBA’s (and people have to fill both roles)?

Personally, I don’t give a crap about whether identity keys are “exposed physical locators”. How about GUIDs or Stored Procedures which generate an incrementing number instead? I’ll be honest, I’ve never used them to identify the physical location. It’s just that I’ve found that “natural keys aren’t” (I think I first heard that from Jim Booth http://www.jamesbooth.com ) and having a system generated key greatly simplifies any code I’ve needed to write against a SQL backend (VFP or SQL Server). I’ve worked with systems that exclusively used natural keys and invariably what seemed like a reasonable natural key suddenly wasn’t any more. This usually meant we now had to use multiple columns to uniquely identify the record (this usually ends up happening more than once on the same table). No thanks. I’m sure some people would argue that changes like this just means we didn’t do a proper design. Yeah, that could be the case. But I’d much rather use a style of development that was more flexible to change and more forgiving of developer errors than one that leads to nightmarish spaghetti code just because someone didn’t take something into account during whatever design process there was. The other way may lead to the most elegant, clear code ever written, but if it’s dependent on people never making any kinds of mistakes (or incorrect judgment calls), I’ll skip it, thanks.

 

If you’re interested, Joe has a blog where he answers questions about SQL in his own, abrasive, insulting (if sometimes funny) style.

 

http://joecelkothesqlapprentice.blogspot.com/

 

How many of his “rules” have you broken to actually deliver usable software (within the confines of the tools you’re using)? Why is there so much support built into these tools to do things the “wrong way”? Right now, I’m specifically thinking about Crystal Reports: where are you doing most of your data manipulation – in SQL Server or in CR? I guess it could be argued that the limitations of these various development tools doesn’t mean that the various practices they (sometimes) force on you are correct. Could be, but ultimately most of us Just-Need-to-Get-it-Done. “In theory” is nice, but “in practice” pays the bills. Do you think your customers would accept the excuse that you wouldn’t generate some report for them because the reporting tool doesn’t have the ability to do what they wanted and putting it into SQL isn’t the correct way to do it?

 

As a side note, it seems like this is a distinct personality trait with some “computer” people in general. You know the type: really smart, could be really helpful to have on the team but they just don’t play well with others. Fanboys (and girls) of them will usually say things like, “I know online he can be rough, but it personal he’s great”. In other words, that just means he’s an asshole. Being blunt (as it’s sometimes described) is OK as long as it’s tempered with the understanding that it’s not an excuse to be an asshat, online or in person.

 

(I just hope the above isn’t taken as some argument for writing crappy code). Write the best code you can for the given problem and time constraints. Refactor mercilessly.

 

Links

http://www.intelligententerprise.com/channels/bi/showArticle.jhtml?articleID=201806814
http://joecelkothesqlapprentice.blogspot.com/
http://www.jamesbooth.com


 
Categories: Soapbox | SQL

October 29, 2007
@ 08:40 PM

There have been a lot of great sessions done over the years about Intellisense, builders, etc. (usually by Rick Schummer or Doug Hennig). Every time I've been able to sit in on these sessions, I end up leaving all geeked up to write some cool routine or code that will make my life easier. A few days go by and reality sets in - I don't really have the time to actually write any of that stuff. 6 months go by, someone shows some other slick utility they wrote and I get all excited about it again.

With that in mind, here are a few easy changes you can make (that won't take more than a few minutes) that can help save you some time. Just think how many times a day you type some of these things:

Tools > Intellisense Manager > Custom

IntellisenseManager

CreateObject

In the Replace field, enter CO in the With field, enter CREATEOBJECT. Change the Type combo to Function. Then click Add.

MessageBox

Now type MB in the Replace field and MESSAGEBOX in the With field, again make sure the type combo is set to Function.

Now close the Intellisense Manager. At the command window enter:

USE (_foxcode) IN 0 AGAIN SHARED
SELECT FoxCode
APPEND BLANK
BROWSE

IF ENDIF

In the new record enter:

Type: U
Abbrev: IF
Cmd: {stmthandler}
Data: Double click on it and enter:
IF ~
<<lcSpace>>ENDIF


Save: T
The timestamp & UniqueID are optional.

SCAN ENDSCAN

APPEND BLANK

Type: U
Abbrev: SCAN
Cmd: {stmthandler}
Data: Double click on it and enter:
SCAN ~
<<lcSpace>>ENDSCAN


FOR ENDFOR

APPEND BLANK

Type: U
Abbrev: FOR
Cmd: {stmthandler}
Data: Double click on it and enter:
FOR ~
<<lcSpace>>ENDFOR


WITH

(can you guess?)

APPEND BLANK

Type: U
Abbrev: WITH
Cmd: {stmthandler}
Data: Double click on it and enter:
WITH ~
<<lcSpace>>ENDWITH

TRY CATCH

APPEND BLANK

Type: U
Abbrev: TC
Cmd: {stmthandler}
Data: Double click on it and enter:
TRY
   ~
CATCH TO loEx
ENDTRY

TRY CATCH FINALLY

APPEND BLANK

Type: U
Abbrev: TCF
Cmd: {stmthandler}
Data: Double click on it and enter:
TRY
   ~
CATCH TO loEx
FINALLY
ENDTRY

Close the browse window and the foxcode table.

Hotkeys:

This one will reset the dev. environment for you.

Select Tools > Macros. Click on New. Hit F4 as the key, enter "ClearIt" as the macro name. Paste this into the macro contents:

_genscrn=_foxcode{ENTER}
_foxcode{SPACEBAR}={SPACEBAR}""{ENTER}
set{SPACEBAR}development{SPACEBAR}on{ENTER}
execscript("do{SPACEBAR}while{SPACEBAR}txnlevel(0{BACKSPACE})>0"+chr(13)+"rollback"+chr(13)+"enddo"){ENTER}
clear{SPACEBAR}all{ENTER}
release{SPACEBAR}all{ENTER}
clear{SPACEBAR}program{ENTER}
set{SPACEBAR}procedure{SPACEBAR}to{ENTER}
set{SPACEBAR}classlib{SPACEBAR}to{ENTER}
clear{ENTER}
_foxcode=_genscrn{ENTER}

 

Hit OK.


Now try everything out - open a program window:

CO(
MB(


Hit the spacebar after these:

IF
SCAN
FOR
WITH
TC
TCF

 

Links

http://rickschummer.com/blog
http://doughennig.blogspot.com


 
Categories: VFP

October 27, 2007
@ 04:07 PM

Imagine this scenario: You want to create a simple user control that you can use in a few web pages. It incorporates a few JavaScript functions that change the UI slightly during a mouse click; pretty standard stuff. The script isn't particularly useful in other controls, so putting it into a common library doesn't make much sense. It could be broken out into it's own library, which can at least be cached by a browser. But that introduces another dependency in the hosting page - you have to remember to include a reference to this JS library before things will work. The easiest thing to do would be to just include the JS code inside the user control itself (I'm skipping the option of embedding it as a resource; maybe another day). Including the code in the control is pretty simple and seamless. Until you drop two instances of the same control on page. What you'll find (like I did) is that your JS code is now rendered one time for each control. That's not very cool. In fact, you run into this same issue even if you decide to move the code into it's own library - you end up with more than one reference to the library.

 

A relatively simple way around all this is the ClientScriptManager class that is available hanging off the Page object as ClientScript. It lets you register JS code to be rendered into the main page. You can also check for the existence of the registered code (to keep from registering it more than once, regardless of the number of controls on the page). In my case, since I'm just embedding the code right in the page, there are only two methods required for this: IsClientScriptBlockRegistered and RegisterClientScriptBlock.

 

The routines use the object type and the passed in name as a key; if you don't pass it into IsClientScriptBlockRegistered (or the other variants) the routines will use the Page's type. I'd suggest always passing in something besides the page type (for example, the user control type). This will keep you from running into conflicts if you happen to use the same registration name (although things will still break if you picked the same JS function names).

 

Here's what that might look like:


if (!Page.ClientScript.IsClientScriptBlockRegistered(typeof(WebmailAddressBookBizObj), "ToggleCheck"))

{

    string js = @"

                  function ToggleCheck(node)

                  {

                    if (node.Checked)

                        node.UnCheck();

                    else

                        node.Check();

                  }";

 

    Page.ClientScript.RegisterClientScriptBlock(typeof(WebmailAddressBookBizObj), "ToggleCheck", js, true);

}


 
Categories: ASP.NET | Javascript

October 24, 2007
@ 09:10 PM

Coding Horror just had an article about moving his site to a new host running as a virtual server. I’ve got to say, I’m starting to become a big fan of this. When I was looking for a web host, as soon as I realized it was even an option, I started looking exclusively for a virtual private server. I get almost all of the benefits of a dedicated server without the price (although, I must say, there are some really good deals available for dedicated server hosting as well). I can install any software I want, configure it any way I’d like, and not have to go through a “control panel” app. to do it. It includes a Win 2003 Server license, so I don’t even have that expense.

 

The biggest limitation I’ve found is hard drive space. Most VPS solutions just don’t give you very much space. I’ve got 6 GB available on my site. While that’s not too bad, I would have preferred quite a bit more (15 GB would have been great). Once you start adding space, though, the monthly price starts ratcheting up (I think it’s a bit out of line for the value – thankfully 6 GB is something I can live with). I get 200 GB/month of bandwidth and 3 dedicated IP addresses. It runs $40/month, which isn’t too horrible. I’m guessing they must be hoping to get 5-10 VM’s per machine. That would seem to fit for a 72 GB drive w/RAID and the min. dedicated stats of 384 Megs of memory and 200 MHz of CPU.  A fully loaded box that all had a lot of traffic might be a little sluggish – thankfully I don’t think the box I’m currently on is anywhere near that (at least yet).

 

I’ve found the performance to be pretty reasonable. Of course, the VM for my site is idle most of the day so I’m not really pushing it <g>. Jeff’s site generates a huge amount of traffic; he had more visitors in a few seconds than I’ll get in a day (take a look at his traffic stats which are available as a link off the main Coding Horror page). When I was looking it appeared most VPS solutions are limited by the bandwidth per month available, 200-300 GB/month on average (a few sites offered more but had terrible reviews). At his level (maybe 2000GB/month based on the estimate someone posted of 5.7 Mbit/sec of traffic) I’d be tempted to want to just go with unmetered hosting (and once you hit that level, you’re really going to want to be on a dedicated box). There seems to be a bit of a margin here from various hosts (unmetered vs metered), so I guess YMMV.

 

Of course, there is nothing keeping you from then running a virtual server on your dedicated box. There is something really attractive about being able to just take a snapshot of the VM image and drop it on a new box if you need to scale performance (or run multiple copies of the image on a few boxes). Or, imagine your host running into connectivity issues. You could just take the VM image and move it to a backup host really quickly.

 

One huge thing to consider is the price of virtualization on performance. I found a really interesting article that went more in depth about it. Cliff notes version – he saw an approx. 40% performance hit on the total requests that a machine can handle. That may kill any fantasies about virtualizing a server for some of you <g>. But, like most things, there are tradeoffs which may still make it an attractive alternative.

 

 

Links

 
http://www.codinghorror.com/blog/archives/000984.html
http://www.webperformanceinc.com/library/reports/LoadTestingVirtualizationPerformance/
http://www.galaxyvisions.com


 
Categories:

October 22, 2007
@ 10:23 PM

I've had these notes about optimizing VFP code hanging around a long time (and added a few a little more recently). According to the date on it, most of this is from 8/1/2002. Wow!

Most of this came out of a data conversion project that would take 3-4 hours each time it was run (and during development we needed to run this a LOT so 3-4 hours would just kill productivity).

  • () evals are much faster than macro substitution:

    REPLACE (lcField) WITH 1

    instead of

    REPLACE &lcField WITH 1

    2.5 - 3X faster

    Directly accessing a field:

    REPLACE field WITH 1

    is 8-10X faster than the REPLACE (lcField) WITH 1 code.

  • Do multiple field updates in a single REPLACE.

    For example:

    REPLACE field WITH 1, field2 WITH 2, field3 WITH 3 IN TableName

    Instead of:

    REPLACE field1 WITH 1 IN TableName
    REPLACE field2 WITH 2 IN TableName
    REPLACE field3 WITH 3 IN TableName


  • If you are doing COM interop, use WITH/ENDWITH instead of directly accessing object references - this keeps VFP from having to traverse the object hierarchy on each line. This can be significantly faster.

  • SCATTER MEMVAR is up to 10X faster than SCATTER NAME, although SCATTER NAME provides better encapsulation. If you are doing this for a large number of records, SCATTER MEMVAR may be a much better choice.

  • If you are doing a SELECT ... WHERE IN (SELECT ...) style query, check to see if it's functionally equivalent to an INNER JOIN. This can make a HUGE difference.

  • CREATEOBJECT() is much faster than NEWOBJECT().

  • Opening and closing tables is one of the slowest thing you can do. In the data conversion app. we had some code that opened a lookup table at the beginning of the function, then closed it at the end. The data conversion process was spending approx. 45 minutes of time in this routine alone. By opening the tables only once (and leaving them open), the processing time dropped to under 3 minutes (It was so much faster that I had to test this a few times to make sure I hadn't broken it).

 

OK, nothing really earth shattering here. But hopefully you'll find one or two of the above helpful at some point.


 
Categories: VFP

October 18, 2007
@ 10:47 PM

Once in a while I’ll get lucky and find a good sample of a solution to exactly the problem I’m having. Last Friday was my day for this. I’ve been working on a portal-style web application which includes security and logging on everything. I make some logging calls from within my global.asax file. Running ASP.NET web apps under development isn’t the fastest thing in the world and it’s always a bit of a problem to get a good feel for it’s speed in production. However, I had a few pages which felt particularly sluggish.

 

I’ve played around with various .NET code profilers and while they’ve all been OK, I haven’t committed to buying any of them. This time around I downloaded and installed ANTS Profiler from Red Gate. Once limitation that most profilers seem to have is that they require me to debug under IIS, which means I have to screw around with the code a bit to get it ready for deployment before I can look into any performance issues (although you can usually force them to fire up Cassini and profile it that way, it’s usually not very intuitive). So it was kind of nice to be able to profile an app. directly from ANTS.

 

After a quick profile test it was pretty clear the problem was in the logging code. There isn’t much going on here, grab some info. about the request (IP Address, page, time, user ID, etc.) create a connection, then create and run the insert SQL code. It was adding around 250ms to every page hit. I couldn’t do much to optimize this code, but I thought I might be able to create a new thread to run this on so the user wouldn’t have to wait while it ran. I found an excellent example of the exact issue by Peter Bromberg on the Egghead Café site: http://www.eggheadcafe.com/tutorials/aspnet/aa5ff306-16a1-4014-a51d-6ffde0894a0d/aspnet-request-logging-w.aspx

 

I didn’t need his connection or logging code, so I just snagged the thread class. Then I created a delegate for the logging method call.

 

My delegate code:

public delegate void SqlExecDelegate(SqlCommand command);

Then I just wrapped the call and passed in the SqlCommand variable I needed:

ThreadUtil.FireAndForget(new SqlExecDelegate(SqlExec), Command);

Here’s the code I grabbed from Peter – his article explains how it works in detail:

using System;
using System.Runtime.CompilerServices; 

namespace WirelessToyz.Utility
{
    /// <summary>
    /// Downloaded this class from:
    /// http://www.eggheadcafe.com/tutorials/aspnet/aa5ff306-16a1-4014-a51d-6ffde0894a0d/aspnet-request-logging-w.aspx
    /// </summary>
    public class ThreadUtil
    {
        private static AsyncCallback callback = new AsyncCallback(ThreadUtil.EndWrapperInvoke);
        private static DelegateWrapper wrapperInstance = new DelegateWrapper(ThreadUtil.InvokeWrappedDelegate);
 
        private static void EndWrapperInvoke(IAsyncResult ar)
        {
            wrapperInstance.EndInvoke(ar);
            ar.AsyncWaitHandle.Close();
        }
 
        public static void FireAndForget(Delegate d, params object[] args)
        {
            wrapperInstance.BeginInvoke(d, args, callback, null);
        } 

        private static void InvokeWrappedDelegate(Delegate d, object[] args)
        {
            d.DynamicInvoke(args);
        } 

        private delegate void DelegateWrapper(Delegate d, object[] args);
    }
}

I immediately saw a difference in my app. The pages felt a noticeable snappier. While it still takes 250ms to run my logging code, I no longer have to make the user wait for it.

Links

http://www.eggheadcafe.com/tutorials/aspnet/aa5ff306-16a1-4014-a51d-6ffde0894a0d/aspnet-request-logging-w.aspx


 
Categories: ASP.NET

This is one of those things that's really easy to do, but not particularly clear the first time you run across it (well, it wasn't clear to me at least). There are times when you need to access ASP.NET created controls from within your JavaScript code. In the ASPX page you may have set the ID of a button control to "btnAdd". However, when you run the page the control name gets mangled into something like ctl00_ContentPlaceHolder1_btnAdd. In order to reference it in your JavaScript, you need that mangled name. To get it, just embed something like this in your JavaScript code:

function DoSomething()
{
    var addButton = document.getElementById('<%= btnAdd.ClientID %>');
    if (addButton)
        alert('<%= btnAdd.ClientID %>');
}

 

When the page is run, the ASP.NET eval engine will replace the ID with the correct one.


 
Categories: ASP.NET | Javascript

October 17, 2007
@ 10:49 PM

I’ve been trying to get the (highly regarded) CopySourceAsHtml add-in to work under Visual Studio to make it easier to post code samples. Every time I tried loading it, it was failing with:

The Add-in 'CopySourceAsHtml' failed to load or caused an exception.
Would you like to remove this Add-in?
If you choose yes, the file it was loaded from, '\\wtz-srv-dc01\Users\paul.mrozowski\My Documents\Visual Studio 2005\Addins\CopySourceAsHtml.AddIn', will be renamed.
Error Message:
Error number: 8013150a

I tried downloading the source, recompiling, making some suggested changes to the control references. Nothing seemed to help. As soon as I attempted to activate the add-in, it was failing. Then it suddenly occurred to me: what if it’s related to the fact that “My Documents” is on a network drive? To test this idea, I created a new local folder, then when into Tools > Options > Environment > Add-in/Macros Security. I added a new local folder which had a copy of the add-in and exited VS and restarted. Finally, I when to Tools > Add-ins and reselected the CopySourceAsHtml add-in. No errors!

options

I copied and pasted some code (and selected Override for the font to add Courier New as a secondary font). (random code shown below).

 

        /// <summary>

        /// Process events from the grid

        /// </summary>

        /// <param name="source"></param>

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

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

        /// <created>10/16/2007</created>

        protected void grdTemplates_ItemCommand(object source, Telerik.WebControls.GridCommandEventArgs e)

        {           

            if (e.CommandName == "Edit")

                this.EditRecord(e.Item);

            else if (e.CommandName == "Cancel")

                this.grdTemplates.EditIndexes.Clear();

            else if (e.CommandName == "Add")

                this.AddRecord();

            else if (e.CommandName == "Test")

                this.TestQuery(e);

            else if (e.CommandName == "Update")

                this.UpdateRoles(e);

        }

 

Here's a screenshot as a comparison:

 

htmlComparison

 

 

Links:

CopySourceAsHtml

http://www.jtleigh.com/people/colin/software/CopySourceAsHtml/

Getting it to run under VS 2008 Beta 2

 http://diditwith.net/2007/08/16/CopySourceAsHtmlInVisualStudio2008.aspx


 
Categories: Developer Tools | Visual Studio

There is a ton of great .NET content available on the web; everything from simple code snippets to full blown apps. I really appreciate that people put the time into this stuff and make it available. But I have one request: would it kill you to include the namespace references in your sample code? There are thousands of classes in .NET - I hate having to try and figure out where these classes are hiding in order to get my code to compile (esp. since I’m normally looking at this code because I'm not familiar with the class or classes required to do whatever it is I'm trying to accomplish. Having said that, I never realized VS would actually help resolve these references for me. If you right-click on a type (in this case, I right-clicked on File), there is a Resolve Namespace option on the content menu:

 

namespace

Very nice!
 
Categories: .NET

October 15, 2007
@ 09:25 PM
When I was thinking about putting up a blog, one of the things I really debated was if I wanted to create two different blogs: one which focused on VFP, other on .NET. I was (and still am) a bit worried that the VFP content will drive away .NET developers, and the .NET content will scare away VFP developers. Ultimately, I decided to just include both in the same blog. Since I’m using both on a daily basis, there is a lot of overlap for me between the two. Then I’d have to decide which blog a post was more applicable for. And where would I put content that might be applicable to both (for example, I’ve got some things about SQL Server I’m planning on talking about)? Hopefully I can make this interesting to both sets (and if not, that’s OK too). Well, I guess there are probably 3 or 4 people who are like me, using both VFP and .NET so this might be perfect for them .
 
Categories: Other

October 13, 2007
@ 09:58 PM
I sent myself a few code snippets and a couple of blog ideas from work (Outlook using Word integration). To keep the code formatting I just pasted the content into Live Writer and told it to keep the HTML formatting. That turned out to be a bad idea - I forgot what a horrible job Word does with HTML markup. I ended up leaving the code markup as-is, since I don't want to manually reformat it, but I did change the rest of it. Reminder to self: "Don't paste from Word".
 
Categories: Other

A while back I needed a routine which would display the first few hundred characters of a longer chunk of text. Obviously, it's easy enough to do that with the Substring method of a string. However, I wanted to do this on a word boundary (I didn't want to end up with half of the word being displayed). So I wrote a simple routine which broke up the string into an array (using Split()), then rebuilt the string (keeping track of the length along the way). Maybe 25 lines or code or so. It seemed to work OK, so I was good to go.

The other day I realized I needed to adjust the code to strip out HTML markup before displaying the text - you can imagine how "nice" that might look if I happened to chop off an ending tag somewhere. I knew I had a third party library to do this; there are a lot of really useful little routines hiding in the West Wind Web Store .NET 2.0. So I took a look through the library and found what I was looking for. While looking for it, I noticed another routine which appeared to do exactly the same thing I wanted. Except it was like 5 lines and much easier to understand. Doh!

30 seconds later I rewrote my routine. Here it is...

        public static string TruncateString(string source, int maxLength, string ending)
        {
            // Do we even have to truncate it?

            if (source.Length <= maxLength)
                return source;

            string text = source.Substring(0, maxLength);

            text = text.Substring(0, text.LastIndexOf(" ")); 

            return text;
        }


 
Categories: .NET

October 12, 2007
@ 07:56 PM

We had our monthly FoxPro user group meeting last night, and, like always, there were some cool new things shown and talked about that I’ve now got to play with. Cathy Pountney was kind enough to drive over from the Grand Rapids area to do a session on extending VFP 9’s report writer through extensions and hooking into the generation process with listeners. She demo’d some code which added a new print dialog box which allowed the user to pick/configure the printer before printing, being able to print a document range (ex. 1-10, 11), “Print to Fit” which resizes a report to fit on 1 page, duplex printing, booklet printing, and being able to print multiple pages per sheet of paper. Really cool stuff. She’ll be giving this same presentation at Southwest Fox next week, so if you’re planning on going, don’t miss this session.

One of the “hey, I didn’t know about that command” that came up was SYS(2600). This command lets you pass in a integer (pointer) and then returns the memory pointed to by the pointer as a string. It can also write data into the memory point. This is pretty slick since it allows you to easily interface with win32 API calls that require you to pass in pointers to structures, or use API calls that return pointers to structures. By setting/getting this memory as a string, you can then easily parse apart the structure to get the data back.

To be able to do anything useful with this, you’re also going to need the ability to allocate/de-allocate some memory (and get a pointer to it). The easiest way to do this is through a few API calls:

#DEFINE GMEM_ZEROINIT     0x40    && Init memory contents to zero

DECLARE INTEGER GlobalAlloc IN kernel32.dll ;
        INTEGER wFlags, ;
        INTEGER dwBytes       

DECLARE INTEGER GlobalFree IN kernel32.dll ;
        INTEGER hMem 

lnPointer = GlobalAlloc(GMEM_ZEROINIT, 110)

?lnPointer

IF lnPointer > 0

   * You should see a a bunch of squares (nulls)

   ?SYS(2600, lnPointer, 110)
   SYS(2600, lnPointer, 100, REPLICATE("X", 100))

   * Now you should see 100 X's followed by 10 nulls

   ?SYS(2600, lnPointer, 110) 

   GlobalFree(lnPointer)

ENDIF          

A side note: You may see some apps use LocalAlloc/LocalFree instead of GlobalAlloc/GlobalFree. They are essentially the same thing at this point (that is, there isn’t any difference between the two of them). This is one of those “backwards compatibility” things. If you want to know more: http://msdn2.microsoft.com/en-us/library/ms810603.aspx

There were a couple of other cool utilities/products that came up as well that I’ll be trying out/writing about.


 
Categories: VFP

  I was doing some searching the other day for pricing on MSDN Professional vs MSDN Premium editions, comparing what was included, etc. Pro seemed to be around $1000 vs upwards of $2000 for Premium (depending on the specific version picked). Here's a comparision of the differences. For me, I was really hoping to be able to get Vista, Outlook, and things like Expression. Unfortunately, those are only avail