this.Blog.Find(entry => entry.IsHelpful);
 Tuesday, April 22, 2008
Getting the SysInternals Suite, Ruby style

John Robbins at Wintellect put out a nice blog post about using a PowerShell script to download and install the excellent suite of tools provided by Sysinternals.  I absolutely love the *concept* of PowerShell and the ability to harness the power of .Net at the command prompt.  The one downside to it is that I find the syntax to be a bit too verbose than what I normally expect out of a scripting language.  YMMV.

Anyway, in an effort to stay on top of my (limited) Ruby skills, I have ported his implementation to Ruby:

#!/usr/bin/env ruby

require 'net/http'
require 'zip/zip'

def usage
puts ""
puts "Downloads and extracts all the tools from Sysinternals"
puts ""
puts "Usage: ruby download_sysinternals.rb <extract-directory> [<save-directory>]"
puts "Required Parameter :"
puts " <extract-directory> : The directory where the SysinternalsSuite.zip"
puts " tools are extracted."
puts "Optional Parameters :"
puts " <save-directory> : Saves SysinternalsSuite.zip to the specified"
puts " directory. If not specified, the .ZIP file "
puts " is not saved."
puts " -? : Display this usage information"
puts ""
puts ""
Kernel#exit
end

def create_directory_if_needed(dir)
File.makedirs(dir) unless File.exist?(dir)
end

def download_sysinternals_zip_file(download_dir)
puts "Downloading SysinternalsSuite.zip file"
url = URI.parse('http://download.sysinternals.com/Files/SysinternalsSuite.zip')
req = Net::HTTP::Get.new(url.path)
resp = Net::HTTP.start(url.host, url.port) { |http| http.request(req) }
puts "Saving SysinternalsSuite.zip to: #{download_dir}"

file_name = "#{download_dir}/SysinternalsSuite.zip"
delete_file_if_exists(file_name)
open(file_name, "wb") { |file| file.write(resp.body) }

puts "Finished downloading"
end

def extract_zip_file(zip_file, extract_dir)
puts "Extracting zip file to: #{extract_dir}"
Zip::ZipFile.open(zip_file) do |zip|
zip.each do |entry|
file_name = "#{extract_dir}/#{entry.to_s}"
puts "Extracting #{file_name}"
# you will need to run this as an admin if
# you have sysinternals stuff in "Program Files"
delete_file_if_exists(file_name)
entry.extract(file_name)
end
end

puts "Finished extracting zip file"
end

def delete_file_if_exists(file_name)
File.delete(file_name) if File.exist?(file_name)
end

# here we go!!!
usage if ( ARGV.empty? || ARGV[0] == "-?" )

extract_dir = ARGV[0]
save_dir = ARGV.length > 1 ? ARGV[1] : ENV["TEMP"]

# download the file
create_directory_if_needed(save_dir)
download_sysinternals_zip_file(save_dir)

# extract the zip file
create_directory_if_needed(extract_dir)
zip_file = "#{save_dir}/SysinternalsSuite.zip"
extract_zip_file(zip_file, extract_dir)

# clean up after ourselves
delete_file_if_exists(zip_file) if ARGV.length < 1

Kick it on DotNetKicks.com
Tuesday, April 22, 2008 6:59:08 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  Powershell | Ruby | Sysinternals

 Wednesday, April 16, 2008
It's funny because it's true!

I just received this from Scott Hanselman's twitter feed (where he is attending the Microsoft MVP conference):

twitter: shanselman: "The UpdatePanel is like crack." - #mvp08

:-)


Kick it on DotNetKicks.com
Wednesday, April 16, 2008 12:37:53 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  .Net | Ajax | ASP.Net | Conferences | Humor

 Monday, April 14, 2008
New Release of Miado out on CodePlex

I have uploaded a new version of Miado out on CodePlex.  I am still calling this a beta.  There are a few features I would like to add before I officially proclaim a 1.0 version (ability to use SqlServer-type syntax with an ODBC provider and integrated caching with the IDbStatement are two at the top of my list).

For this latest version, I have moved the DbProvider and vendor specific stuff to the Miado.Configuration namespace.  I have added an IParameterParser interface which is used in the SimpleSql class to determine the parameter name declarations in a SQL statement based on whether you are using the ODBC-type syntax or newer syntax for variable declaration. For example,

MyParam = ?
vs.
MyParam = @MyParam

This week I am going to try and come up with a good set of DAOs that use the AdventureWorks DB so people can see samples of how the API should be used.


Kick it on DotNetKicks.com
Monday, April 14, 2008 6:52:06 AM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  .Net | Miado

 Thursday, April 10, 2008
Starting out with ASP.Net Ajax and the Ajax Control Toolkit

So only javascript gurus can make slick Ajax-enabled web pages, right? Wrong! Want to know the dirty little secret?  It's actually really easy!

If you are not using Ajax already on your ASP.Net pages, here's a great place to start.  It's always bothered me how on something as simple as paging and sorting on a GridView caused a full postback. Wrap that sucker in an UpdatePanel and you’ve instantly Ajax-enabled your page! By including the declaration ChildrenAsTriggers="true" (or not declaring it at all – it’s the default option), any postback event triggered in the UpdatePanel will automatically cause the page to partially re-render its contents. That’s it! No javascript needed. Well, actually there’s a ton of javascript involved – just none of it written by you. ;-)

First off, you need a couple of resources to get started. If you are using the 3.5 version of the framework, ASP.Net Ajax is already included by default. If you are using .Net 2.0, you will need to download the ASP.Net Ajax Extensions 1.0 (could they make it *any* more confusing?).

I am using the Ajax Control Toolkit to perform the animations during the partial post-back. The Ajax Control Toolkit has a nice set of tools to use to help you build rich web UIs, and as Clark Howard would say, "Hey, it's free!!!!" In the example below, I am using the UpdatePanelAnimationExtender to declaratively set a FadeOut animation to occur on the div container surrounding the UpdatePanel as the page posts back, and a FadeIn animation as it re-renders.

OK, so I lied. I am actually using a little javascript to dynamically show a spinning loading image centered on the grid during the postback.  But, again, the ASP.Net Ajax library to the rescue!  It makes our life a lot easier by providing all sorts of nice javascript shortcuts that are cross-browser compatible (like the Sys.UI.DomElement.getBounds() call I’m using).

Oh, you need to check out this site that will generate a custom loading image for you. Make sure you select a transparent background when you build your image.

Here is the code:

<!-- loading image is hidden by default -->
<img id="imgLoading" src="images/ajax-loader.gif" alt="Loading..." width="30" height="30"
style="display: none;" />
<div id="container">
<asp:UpdatePanel ID="updPnl" runat="server" ChildrenAsTriggers="true">
<ContentTemplate>
<asp:GridView ID="gvw" runat="server" CssClass="grid"
HeaderStyle-CssClass="header" AutoGenerateColumns="false" RowStyle-CssClass="odd"
AllowPaging="true" AllowSorting="true" AlternatingRowStyle-CssClass="even">
<Columns>
<asp:BoundField SortExpression="ID" HeaderText="ID" DataField="ID" />
<asp:BoundField SortExpression="Name" HeaderText="Name" DataField="Name" />
<asp:BoundField SortExpression="BirthDate"
HeaderText="Date Of Birth" DataField="BirthDate" />
</Columns>
</asp:GridView>
</ContentTemplate>
</asp:UpdatePanel>
</div>
<ajaxToolkit:UpdatePanelAnimationExtender ID="animator" runat="server" TargetControlID="updPnl">
<Animations>
<OnUpdating>
<Sequence>
<!-- shows the loading image in the center of our grid -->
<ScriptAction Script="showInProcessImage($get('imgLoading'), $get('container'));" />
<Parallel duration=".10" Fps="30">
<FadeOut AnimationTarget="container" minimumOpacity="0" />
</Parallel>
</Sequence>
</OnUpdating>
<OnUpdated>
<Sequence>
<Parallel duration=".25" Fps="30">
<FadeIn AnimationTarget="container" minimumOpacity="0" />
</Parallel>
<!-- hides the loading image -->
<ScriptAction Script="hideInProcessImage($get('imgLoading'));" />
</Sequence>
</OnUpdated>
</Animations>
</ajaxToolkit:UpdatePanelAnimationExtender>

And the javascript:

<script language="javascript" type="text/javascript">

// show the image in the center of the container
function showInProcessImage(img, container) {
// get the bounds of both the container and the image
var containerBounds = Sys.UI.DomElement.getBounds(container);
var imgBounds = Sys.UI.DomElement.getBounds(img);

// figure out where to position the element (the center of the container)
var x = containerBounds.x +
Math.round(containerBounds.width / 2) - Math.round(imgBounds.width / 2);
var y = containerBounds.y +
Math.round(containerBounds.height / 2) - Math.round(imgBounds.height / 2);

// set the position of the in progress image
Sys.UI.DomElement.setLocation(img, x, y);

// finally un-hide it
img.style.display = '';
}

// hide the image
function hideInProcessImage(img) {
img.style.display = 'none';
}

</script>

I've left out the details of actually handling the paging and sorting events on the GridView.  I assume you are doing that already (or know how to do it).

One more thing to keep in mind - even though you are partially rendering the screen, you are still hitting the server (and your code-behind page).  On your partial post-backs, don't perform any extra work in the code-behind that's not going to be rendered in the update panel (e.g. hitting the DB, populating drop-down lists, setting headings, etc.). And don’t be fooled into thinking that Ajax-enabled web apps take a significant burden off the server.  If they are designed to be too "chatty", they can even make things worse!


Kick it on DotNetKicks.com
Thursday, April 10, 2008 9:42:23 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  .Net | Ajax | Ajax Control Toolkit | ASP.Net

 Monday, April 07, 2008
Miado API Documentation

I have published the API documentation for Miado.  The IDbStatement and SimpleSql objects are the two main components of the API and both contain examples of how it should be used.

I have to give a shout out to the Sandcastle Help File Builder project on CodePlex.  It made my life 1000x better in using Sandcastle to generate the API documentation.


Kick it on DotNetKicks.com
Monday, April 07, 2008 7:07:02 PM (Eastern Standard Time, UTC-05:00)  #    Comments [0]  .Net | API | Miado | Sandcastle

 Sunday, April 06, 2008
Miado is out on CodePlex

I have been refining some data access code that I have used on a bunch of my projects to the point where I've made it fairly generic.  I decided to publish it as a project on CodePlex.  I just put the initial release out today.  If you are interested, please take a moment to check it out and give me any feedback.  Keep in mind, I wrote this specifically for those people who have (or want) to use straight SQL and ADO.Net in their data access layer.  So if you want to use LINQ, Nhibernate, Subsonic, etc... they are absolutely great tools, but they try to hide/remove the SQL from your code, whereas this project just makes it much easier for those who use SQL.

Here is an example of how to use it to create a collection of custom business objects:

// ideally this declaration would be buried in your base DAO class
// or injected via an IoC container
IDbConfigurable dbConfig = new DbConfiguration(SqlClientFactory.Instance, connString);
IDbStatement dbStmt = new DbStatement(dbConfig);

ICollection<Address> addresses = 
    dbStmt.LoadSql("SELECT AddressId, Address1, Address2, City, State, ZipCode " + 
                   "FROM Address WHERE City = @City")
          .AddParameter("City", "Atlanta")
          .QueryForResults<Address>(
              row => new Address() 
                  {
                      AddressId = row.GetValue<int>("AddressId"), 
                      Address1 = row.GetValue<string>("Address1"),
                      Address2 = row.GetValue<string>("Address2"),
                      City = row.GetValue<string>("City"),
                      State = row.GetValue<string>("State"),
                      ZipCode = row.GetValue<string>("ZipCode")
                  });

Notice how it removes all the usual boiler plate code from a normal ADO.Net implementation.  The only things you really need to do are provide the SQL, set the parameters, and then map the result set to properties on your business object.  I'm using lambda expressions in these examples, but if you want to re-use the result set-to business object mappings, I normally create a method in my DAOs to use as a delegate:

ICollection<Address> addresses = 
dbStmt.LoadSql("SELECT AddressId, Address1, Address2, City, State, ZipCode " +
"FROM Address WHERE City = @City")
.AddParameter("City", "Atlanta")
.QueryForResults<Address>(CreateAddress);
...
private Address CreateAddress(IResultSetRow row)
{
// full details not provided
return new Address() { AddressId = row.AddressId };
}

If you embed the creation of the IDbStatement interface in a base class (or use the one provided in Miado.Integration), you can see how the code reads even more like a Domain-Specific-Language:

// CreateSqlStatment() method is provided in Miado.Integration.MiadoDao
ICollection<Product> products = 
    this.CreateSqlStatement("SELECT ProductId, Name, Description, Color, Quantity " +
                            "FROM Product WHERE Color = @Color and Quantity > @Qty")
        .AddParameter("Color", "Blue")
        .AddParameter("Qty", 20)
        .QueryForResults<Product>(
              row => new Product() 
                  {
                      ProductId = row.GetValue<int>("ProductId"), 
                      Name = row.GetValue<string>("Name"),
                      Description = row.GetValue<string>("Description"),
                      Color = row.GetValue<string>("Color"),
                      Quantity = row.GetValue<int>("Quantity")
                  });

There is also a SimpleSql object that can be used similar to IDbStatement but the syntax is a little more terse since you don't have to map the parameters - they are merely replaced in the order you provide them:

IDbConfigurable dbConfig = new DbConfiguration(SqlClientFactory.Instance, connString);
SimpleSql sql = new SimpleSql(dbConfig);
sql.ExecuteNonQuery("UPDATE Person SET FirstName = @FirstName, " + 
                    "LastName = @LastName " + 
                    "WHERE PersonId = @PersonId", 
                    "John", "Smith", 1001);
// again, I like to embed this in a base class so it reads:
// this.CreateSimpleSql().ExecuteNonQuery(sql, param1, param2);

For this first release, SimpleSql is not supported for ODBC SQL statements (since it uses pattern matching to substitute the SQL parameters).

Here's another example where you can see how easy it is to get a count:

int count = 
    this.CreateSimpleSql()
        .QueryForOne<int>("SELECT count(*) FROM Employee WHERE YearsOfService > @YearsOfSvc",
                           new object[] { 15 },
                           row => row.GetValue<int>(0));

And yes, it supports the creation of DataSets and DataTables:

DataSet ds = 
    this.CreateSimpleSql()
        .QueryForDataSet("SELECT * FROM ProductType WHERE Price > @Price", 10.0);

This is the first code drop, so treat it like a beta release.


Kick it on DotNetKicks.com
Sunday, April 06, 2008 9:35:57 AM (Eastern Standard Time, UTC-05:00)  #    Comments [1]  .Net | Miado