:::: MENU ::::

Monday, September 27, 2010

Most projects I work on need a list of countries at some point so I put together a snippet of SQL that I could reuse to create and populate a countries table in the database with all countries as given in ISO 3166-1. After recently writing a utility class to populate list controls with world currencies according to ISO 4217 it got me wondering if I could also do the same for countries using only the .Net Framework. And so I came up with the following utility class to do the job.

        /// <summary>
        /// Populates the list control with countries as given by ISO 4217.
        /// </summary>
        /// <param name="ctrl">The list control to populate.</param>
        public static void FillWithISOCountries(ListControl ctrl)
        {
            foreach (CultureInfo cultureInfo in CultureInfo.GetCultures(CultureTypes.SpecificCultures))
            {
                RegionInfo regionInfo = new RegionInfo(cultureInfo.LCID);
                if (ctrl.Items.FindByValue(regionInfo.TwoLetterISORegionName) == null)
                {
                    ctrl.Items.Add(new ListItem(regionInfo.EnglishName, regionInfo.TwoLetterISORegionName));
                }
            }
 
            RegionInfo currentRegionInfo = new RegionInfo(CultureInfo.CurrentCulture.LCID);
 
            //- Default the selection to the current cultures country
            if (ctrl.Items.FindByValue(currentRegionInfo.TwoLetterISORegionName) != null)
            {
                ctrl.Items.FindByValue(currentRegionInfo.TwoLetterISORegionName).Selected = true;
            }
        }

More Info

Working with SQL Server over the years has made me realize just how much learning I have to do before I can classify myself a database guru.  Along the way, I have learned a lot of cool things from various sources, but still have plenty more areas to study.  Most of the things that I've learned about SQL Server, as well as many other things, have come from a need for a solution to a particular situation.  What I will be discussing today came from one of those situations.

The problem I had to solve involved querying the database with several different parameter options.  The same data was to be retrieved with each query, but it was the filtering of the data that changed.  My initial reaction was to write a stored procedure for each of the different parameter permutations.  The problems with that approach are numerous, so let's just say it was too much of a waste of time.  I started looking for alternatives and found this blog post discussing the basics of just what I needed to do.  Since I'm using this blog as a way for me to archive some of the cool things I've learned, I'll put my own spin on the topic here.

The basics are this: make a single stored procedure that will take in all the possible parameters, giving each one a default value of NULL in case it is not needed, and return the necessary data filtered accordingly.  Sound simple?  Actually, it really is.  Here is an example of a stored proc that does just that:

CREATE PROCEDURE GetCrap
    @CategoryID int = NULL,
    @MinPrice decimal(4,2) = NULL,
    @MaxPrice decimal(4,2) = NULL
AS
BEGIN
    SELECT *
    FROM TableWithCrap T
    WHERE (@CategoryID IS NULL OR T.CategoryID = @CategoryID) AND
          (@MinPrice IS NULL OR T.Price >= @MinPrice) AND
          (@MaxPrice IS NULL OR T.Price <= @MaxPrice)
END 

See how simple it is?  Simply give the optional parameters a default NULL value (@CategoryID int = NULL).  Then, and here's the cool part, make each part of the WHERE clause a conditional that will either evaluate to true if the parameter is null or evaluate according to the requirement.

@CategoryID IS NULL

will return true if the parameter CategoryID has not been given, thus eliminating it from the filtering process.

@CategoryID IS NULL OR T.CategoryID = @CategoryID

basically says, "if I have a value, use me."

That's it!  AND those bad boys together and you've got a respectable dynamic query built right into a nice stored procedure!  Isn't that cool?

More Info

Tuesday, June 29, 2010

One of the really useful Visual Studio add-ins is Smart Paster. It adds a "Paste As." context menu that allows you to paste in the clipboard text as a comment, a correctly quoted string or a string builder.

There are versions for VS 2003, 2005 and 2008. But not 2010.

Sometimes you can just copy in the dll and addin file into the VS 2010 Addins folder (.\Documents\Visual Studio 2010\Addins) and edit the addin file (it's just xml) to say "10.0" instead of "9.0". But that doesn't work for SmartPaster - VS 2010 shows an error and insists on disabling the addin.

The VS 2008 download includes the source, so I tried to upgrade it.

It turns out the problem is when it creates the context menus it sets the CommandBarButton.FaceId property (to show an image next to the text). But in VS2010 that throws a DeprecatedException.

Ok, simple fix, but the original source is old code with a fairly high WTF-per-line ratio (well, it was written 2004, .Net 1.1). Before long I had ported it from VB.Net to C# (thanks Telerik) and rewritten large parts (mostly refactoring with Coderush). I simplified by dropping the "regionize" stuff (never use it), the VB support and the configuration form. Here's my code- you can create a new Extensibility Addin project, replace the Connect class and add the SmartPaster class- see below.

It's still a port, so certainly not as clean as something just written from scratch. And perhaps VS2010 has nicer ways of doing all these things now the code window is a WPF control - the EnvDTE objects are ugly and hard to use. Anyway, thanks to Alex Papadimoulis for the original code.

More

 

Thursday, June 17, 2010

Recently, I had a customer want to launch a webpage when the final installation dialog was presented. 

Obviously the original request was for a traditional web page link shown by the traditional underscored phrase.  No version of InstallShield supports this feature – although in the Release Notes for InstallShield 2010, there is a mention of being able to embed a hyperlink within a dialog.  For for all of you who are not up-to-date with the latest IS version, here are the steps that I took to handle this request.

I modified the existing link that enabled you to "Launch a program" that is built into the final dialog "SetupCompleteSuccess".  The important parts of the dialog are:

  • CheckLaunchProgram – this is the CheckBox field
  • LaunchProgramText – this is the text that accompanies the CheckBox field, relatively small
  • UpdateTextLine2 – this is another text field that I commandeered to provide more accomplanying text than what was available.

Step 1: 

Ensure all fields have the same launch conditions.  The one I grabbed had a different launch conditions – so ensure the conditions are the same - alter the SHOW Condition

"SHOWLAUNCHPROGRAM="-1″ And PROGRAMFILETOLAUNCHATEND <> "" And NOT Installed And NOT ISENABLEDWUSFINISHDIALOG

This allows all of the fields to show on the dialog that you are working with. 

Step 2:

Alter the wording on the Text fields to your requirements.  Then reshift the TOP and LEFT values so that it lines up on the screen.  The original locations tend to bunch up near the top screen.  Suggestion – if you have a massive installation project, you won't be able to view the changes easily – I used a "testbed" project with no files to get the look and feel of the dialog to my likeing before I injected the changes into the final project.

Step 3:

Add the properties that control the visibility of the new fields into the Property Manager.  These are:

IE_EXPLORER_PATH   (no value needed for this one)

LAUNCHPROGRAM="1″
SHOWLAUNCHPROGRAM="-1″
PROGRAMFILETOLAUNCHATEND="-1″

Note that the last two properties have "-1″ – this matches up with the Launch Conditions.

Step 4:

Now create the new InstallShield Custom Action that will be triggered by the checkbox.  I named my Custom Action "ISI_LaunchURL" and when using the Custom Action Wizard, it was a "New EXE" that had a "Path in Property Value".  Since its execution is from a DOACTION, there is no sequencing required.  Since you want the installation to finish while the URL is being launched, specify for the Return Processing property – Asynchronous (Don't wait for completion).  The property that holds the path is "IE_EXPLORER_PATH" and will be created next via a System Search function.  The command line will be something like this:

[WEB_ADDRESS]/Docs/Help.aspx#ServiceInstallation

Now, the [WEB_ADDRESS] is a MSI Property that comes across within one of the Dialogs - the customer had entered that value, so I know that the value contains Http://www.XXXX.com – if the format was not correct, then I would need to append/prepend the required values.  Warning:  Be aware that if the customer enters a space at the end of the  WEB_ADDRESS field, then that space would be incorrectly carried forward to this URL – so you may need to edit the field after the entry was made in the dialog.

Step 5:

Now create the new System Search entry.  Use the Wizard and choose "Registry – File Path as specified by a reg entry"  On the next screen, enter "HKEY_LOCAL_MACHINE" for the Registry Root entry and enter "SOFTWARE¥Microsoft¥Windows¥CurrentVersion¥App Paths¥IEXPLORE.EXE" and leave the remaining fields blank.  On the MSI Property, enter "IE_EXPLORER_PATH".  This System Search will kick off at the very beginning of the install and will retrieve the path entry of the Internet explorer and put the value in the MSI Property "IE_EXPLORER_PATH", which should be like this "C:¥Program Files¥Internet Explorer¥IEXPLORE.EXE"

Step 6:

Now we need to modify the dialog "SetupCompleteSuccess" and have it launch the web page URL when the button is checked.  Navigate to the dialog, and select the dialog's "Behavior" section.  Find the OK button and enter the following condition:  DOACTION as the Event, then select from the Argument dropdown  "ISI_LAUNCHURL" and finally put in the condition  LAUNCHPROGRAM="1″

Now you must move that Event up to be in front of the EndDialog EXIT event! 

Step 7:

Now Test / evaluate your finished work.  Note that when the final dialog is displayed, the checkbox will automatically be checked.  This is because the MSI Property was present within the Property Manager.  You had to put it there to be able to reference it within the above steps.  If you now remove it from the Property Manager, the default action is that the box is unchecked.  When testing, you can check the box, hit FINISH button and the URL should be launched.

More

One of the options that was available to you with installations prior to Windows Installer emergence was the ability to rerun an installation multiple times.  By this I mean running the setup again without entering Maintenance Mode – as if it had never been run before.

You can still use InstallShield to create an InstallScript using the  option and then select the Multi-Instance option which lets your end users rerun an installation multiple times as a first-time installation rather than as a maintenance installation. 

But as you know, I studiously avoid InstallScript projects – I use Basic MSI Projects, so how do you make a Basic MSI Project eligible to be rerun?

Note that there are four Standard MSI actions that are the primary method by which the MSI Application is registered under the Windows Installer that we need to be concerned with: 

  • RegisterUser
  • RegisterProduct
  • PublishFeatures
  • PublishProduct

Alter the condition on each of these Standard Actions to use zero – which means the condition will never be set true and the Standard Action will never run.  I recommend you alter the comment to reflect the change – as I have shown above.

Impact

The usage of this technique means the product will not be registered under Windows Installer – nor will it appear in the Add/Remove Programs Control Panel.   Use only if you have a specific requirement!

Usage Scenarios

I have encountered two separate situation where this technique served admirably.  

Once a vendor was creating unique sales video/slideshow presentations to sell to his customers.  After a few customers used his services, he encountered a situation where the same end-client was attempting to install the presentations from two of his customers.  The dreaded Maintenance mode was disrupting the end-client from installing the second presentation.   I was able to make the install rerunable – and used a scripted Custom Action to read a special file delivered with each presentation.  This information allowed me to install the unique files, create shortcuts to access the presentation and create a uninstall technique to allow the end-user to remove the files when no longer required.

Another vendor needed to be able to allow an unlimited amount of customers to be established on the server.  Each customer would have a unique IIS Virtual Directory with the same files installed.  XML documents that were specially configured during the install would maintain the customer data within the Virtual Directory.  A very complex Visual Studio .NET solution was involved to support the customer processes.  Each customer install used a rerunable Basic MSI Webproject to deliver the files and create the Virtual Directory.

Warning!

I don't recommend you do this to simply to avoid the Maintenance Mode!  A great technique if you have a specific requirement – but you will lose so much functionality that Windows Installer/MSI packages offer.

 

Tuesday, May 11, 2010

SolutionConverter will convert a complete Visual Studio solution from one version to another; it allows you to convert your solutions to both older and newer versions. Currently, Visual Studio 2005, 2008, and 2010 are supported, including Visual C# Express and Visual Basic Express editions. Visual C++ project conversion is not yet supported.

 

More

Wednesday, April 7, 2010

Too many people don’t realize that there are other options than <!-- --> comments to annotate HTML. These comments are harmful because they are sent to the client and thus make your page heavier than it needs to be.

When doing ASP.NET, a simple drop-in replacement is server comments, which are delimited by <%-- --%> instead of <!-- -->. Those server comments are visible in your source code, but will never be rendered to the client.

Here’s a simple way to sanitize a web site. From Visual Studio, hit CTRL+H to bring the search and replace dialog.

Choose “Replace in Files” from the second menu on top of the dialog. Open the find options, check “use” and make sure “Regular expressions” are selected. Use “*.aspx;*.ascx;” as the file types to examine. Choose “Entire Solution” under “Look in”.

Here’s the expression to search for comments:

\<!--{[^-]*}--\>

And here’s the replacement string:

<%--\1--%>

I usually use the “Find Next” and “Replace” buttons rather than the more brutal “Replace All” in order to not apply the fix blindingly. Once this is done, I do a second manual pass of finds with the same expression to make sure I didn’t miss anything.

More

 

Tuesday, March 2, 2010

enter code hereIn the SQL Server Full-Text Indexing scheme i want to know if a table is in

  • start_chage_tracking mode
  • update_index mode
  • start_change_tracking and start_background_updateindex modes

The problem is that i set my tables to "background update index", and then tell it to "start change tracking", but then some months later it doesn't seem to be tracking changes.

How i can i see the status of "background updateindex" and "change tracking" flags?

example:

sp_fulltext_table @tabname='DiaryEntry', @action='start_background_updateindex'

Server: Msg 15633, Level 16, State 1, Procedure sp_fulltext_table, Line 364
Full-text auto propagation is currently enabled for table 'DiaryEntry'.

sp_fulltext_table @tabname='Ticket', @action='start_background_updateindex'
Server: Msg 15633, Level 16, State 1, Procedure sp_fulltext_table, Line 364
Full-text auto propagation is currently enabled for table 'Ticket'.

Obviously a table has an indexing status, i just want to know it show i can display it to the user (i.e. me).

The other available API:

EXECUTE sp_help_fulltext_tables

only returns the tables that are in the catalog, it doesn't return their status.

TABLE_OWNER  TABLE_NAME  FULLTEXT_KEY_INDEX_NAME  FULLTEXT_KEY_COLID  FULLTEXT_INDEX_ACTIVE  FULLTEXT_CATALOG_NAME
===========  ==========  =======================  ==================  =====================   =====================
dbo          DiaryEntry  PK_DiaryEntry_GUID       1                   1                      FrontlineFTCatalog
dbo          Ticket      PK__TICKET_TicketGUID    1                   1                      FrontlineFTCatalog

And i can get the PopulateStatus of an entire catalog:

SELECT FULLTEXTCATALOGPROPERTY('MyCatalog', 'PopulateStatus') AS PopulateStatus

which returns a status for the catalog:

0 = Idle
1 = Full population in progress
2 = Paused
3 = Throttled
4 = Recovering
5 = Shutdown
6 = Incremental population in progress
7 = Building index
8 = Disk is full. Paused.
9 = Change tracking

but not for a table.


SQL Server 2000 SP4

SELECT @@version
Microsoft SQL Server  2000 - 8.00.194 (Intel X86)
    Aug  6 2000 00:57:48
    Copyright (c) 1988-2000 Microsoft Corporation
    Standard Edition on Windows NT 5.0 (Build 2195: Service Pack 4)

Regardless of any bug, i want to create UI to easily be able to see its status.

 

 

T-SQL Code

This is how I wrote the query to capture this information.  I am sure there are other ways to pull this information, but this is one easy way to pull the data. This has been tested for SQL 2000 to SQL 2008. I have used cursors and several system stored procedures and also directly queried the system tables. In order to minimize the impact, I changed the transaction isolation level to read uncommitted to do dirty reads and avoid any potential blocking issues..

set transaction isolation level read uncommitted
set nocount on
declare @tbl sysname
declare @cat sysname
create table #temp_ca( 
TABLE_OWNER varchar(100),
TABLE_NAME varchar(256),
FULLTEXT_KEY_INDEX_NAME varchar(256),
FULLTEXT_KEY_COLID int,
FULLTEXT_INDEX_ACTIVE int,
FULLTEXT_CATALOG_NAME varchar(256)
)
create table #temp_status(
Catalog varchar(64),
TblName varchar(64), 
[IsEnabled] bit,
ChangeTracking varchar(24),
PopulateStatus varchar(64),
RowCnt int,
FTS_CT int,
Delta int,
PercentCompleted varchar(128), 
path nvarchar(260)
)
insert into #temp_ca
exec sp_help_fulltext_tables 
declare ca_cursor cursor for
select TABLE_NAME, FULLTEXT_CATALOG_NAME from #temp_ca
open ca_cursor
fetch next from ca_cursor into @tbl, @cat
while @@fetch_STATUS = 0
begin
insert into #temp_status
select 
cast (@cat as varchar(40)) Catalog
, cast(object_name(si.id) as varchar(25)) TblName
, cast(OBJECTPROPERTY(tbl.id,'TableHasActiveFulltextIndex') as bit) as [IsEnabled]
, case isnull(OBJECTPROPERTY(tbl.id,'TableFullTextBackgroundUpdateIndexon'),0) 
+ ISNULL(OBJECTPROPERTY(tbl.id,'TableFullTextChangeTrackingon'),0) 
when 0 then 'Do not track changes'
when 1 then 'Manual'
when 2 then 'Automatic'
end [ChangeTracking]
, case FULLTEXTCATALOGPROPERTY ( @cat , 'PopulateStatus' ) 
when 0 then 'Idle' 
when 1 then 'Full population in progress'
when 2 then 'Paused' 
when 3 then 'Throttled' 
when 4 then 'Recovering' 
when 5 then 'Shutdown' 
when 6 then 'Incremental population in progress' 
when 7 then 'Building index' 
when 8 then 'Disk is full. Paused.'
when 9 then 'Change tracking'
end PopulateStatus
, si.RowCnt, fulltextcatalogproperty(@cat, 'ItemCount') FTS_CT 
, si.RowCnt - fulltextcatalogproperty(@cat, 'ItemCount') Delta 
, cast ( 100.0 * fulltextcatalogproperty(@cat, 'ItemCount') 
/ cast(si.RowCnt as decimal (14,2))
as varchar) +'%' as PercentCompleted
, ISNULL(cat.path, 'Check Default Path')
from 
dbo.sysobjects as tbl
INNER JOIN sysusers as stbl on stbl.uid = tbl.uid
INNER JOIN sysfulltextcatalogs as cat 
on (cat.ftcatid=OBJECTPROPERTY(tbl.id, 'TableFullTextCatalogId')) 
AND (1=CasT(OBJECTPROPERTY(tbl.id, 'TableFullTextCatalogId') as bit))
INNER JOIN sysindexes as si on si.id = tbl.id 
where si.indid in (0,1) and si.id = object_id(@tbl)
fetch next from ca_cursor into @tbl, @cat
end
close ca_cursor
deallocate ca_cursor
select * from #temp_status
drop table #temp_ca
drop table #temp_status

Next Steps

 

Wednesday, February 24, 2010

Believe me it’s easy. If only you won’t get caught up with the 'Impersonation' word. A lot of sites took me for a jolly ride on the Impersonation road.

Here's a demo of the code:



using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Net;

public class ShareThis
{

 //used in calling WNetAddConnection2
 [StructLayout (LayoutKind.Sequential)]
  public struct NETRESOURCE
 {
  public int dwScope;
  public int dwType;
  public int dwDisplayType;
  public int dwUsage;
  [MarshalAs (UnmanagedType.LPStr)]
  public string lpLocalName;
  [MarshalAs (UnmanagedType.LPStr)]
  public string lpRemoteName;
  [MarshalAs (UnmanagedType.LPStr)]
  public string lpComment;
  [MarshalAs (UnmanagedType.LPStr)]
  public string lpProvider;
 }

 //WIN32API - WNetAddConnection2
 [DllImport("mpr.dll", CharSet=System.Runtime.InteropServices.CharSet.Auto)]
 private static extern int WNetAddConnection2A (
  [MarshalAs (UnmanagedType.LPArray)]
  NETRESOURCE [] lpNetResource,
  [MarshalAs (UnmanagedType.LPStr)]
  string lpPassword,
  [MarshalAs (UnmanagedType.LPStr)]
  string lpUserName,
  int dwFlags
  );

 //WIN32API - WNetCancelConnection2
 [DllImport("mpr.dll", CharSet=System.Runtime.InteropServices.CharSet.Auto)]
 private static extern int WNetCancelConnection2A (
  [MarshalAs (UnmanagedType.LPStr)]
  string lpName,
  int dwFlags,
  int fForce
  );

 public static void CopyFile (string share, string username, string password,
  string dirFrom, string dirTo, string filename)
 {
  NETRESOURCE [] nr = new NETRESOURCE [1];
  nr[0].lpRemoteName = share;
  nr[0].lpLocalName = ""; //mLocalName;
  nr[0].dwType = 1; //disk
  nr[0].dwDisplayType = 0;
  nr[0].dwScope = 0;
  nr[0].dwUsage = 0;
  nr[0].lpComment = "";
  nr[0].lpProvider = "";
  WNetAddConnection2A (nr, password, username, 0);

  // Do you stuff here, copy files, create dirs etc
  File.Copy (dirFrom + "\\" + filename, dirTo + "\\" + filename);

  WNetCancelConnection2A (share, 0, -1);
 }

 public static void Main(string[] args)
 {
  CopyFile(@"\\sa191", "username", "password", @"f:\shared",
   @"\\sa191\shared", "123.shp");
 }
}

 

Thursday, February 18, 2010

Every good developer knows never to re-invent the wheel, especially if there is software out there that has been tested by others, and has an established track record. As a developer using the .NET framework I’ve found some of these libraries invaluable, so I’m sharing them for some of the other dev’s out there with a brief outline of how to use.

Yedda Twitter Library.

URL: http://devblog.yedda.com/index.php/twitter-c-library/

I’ve used this on a number of very simple twitter projects, where I’ve just needed to send an update. As the site says its more of a wrapper for the Twitter API than an actual library, but none the less its an easy way to integrate with the service via a drop in dll. Here’s how to use it.

  1. Dim objYedda As New Yedda.Twitter
  2. Dim status as String
  3. Dim strTwitterUser as String = “username”
  4. Dim strTwitterPassword as String = “password”
  5. status = “Hello World”
  6. objYedda.Update(strTwitterUser, strTwitterPassword, strStatus, Yedda.Twitter.OutputFormatType.RSS)

This small section of code will update your Twitter stream (provided you have a reference to the DLL), and your username and password correct.

FileHelpers Library

URL: http://filehelpers.sourceforge.net/

The FileHelpers library was created to stop developers from continuing to parse CSV. If you are doing any kind of importing and exporting within your application using the CSV format to get data in or out, look no further.

You can strong type your flat file simply by coding up a class that maps a data type to each record. This way data consistency, and import / export reliability can be tightened up on quite a bit.  Writing out to a new file is also pretty easy once you’ve created your base classes defining the structure. Great little library for your toolkit.

First define the structure class…

  1. <DelimitedRecord(“,”)>_
  2. Public Class Product
  3. Public ProductName As String
  4. Public ProductCode As Integer
  5. etc..
  6. End Class

Add a reference to the FileHelper.dll, and read from the file , casting to a array of product objects.

  1. Dim engine As New FileHelperEngine(GetType(Product))
  2. Dim myProduct As Product() = DirectCast(engine.ReadFile(“product.txt”), Product())

Perform actions on the array of Products.

  1. For Each pro As Product In myProduct)
  2. Response.Write(pro.ProductName)
  3. Next

Elmah

URL: http://code.google.com/p/elmah/

Elmah stands for error logging module and handlers. It is a completely pluggable in system for error handling within your .NET app. It catches bot thrown and unhandled exceptions across the scope of your app, logs them, and allows you to browse the full stack trace, all without exposing the error to the users of your application. That’s useful for a number of reasons. Firstly you aren’t getting the performance hit of using debug=true within your application (which by the way you should never be using in a production environment anyway) – and it means you can still get to the bottom of little blips as and if they happen. Coolio.

Elmah works as an HTTP module, so it takes little or no effort to deploy on any project. Just configure a few bits and bobs, and away it goes. All of the configuration is performed in the configuration file – just telling it whether you want your errors logged in a database, in memory or in a txt file is as simple as changing some web config parameters. You can even grab recent errors via RSS and get notified like that.

Log4Net

URL: http://logging.apache.org/log4net/

Following in the same vein as Elmah – Log4Net is a port of the well known logging framework for Java log4J. Whilst Elmah concentrates on exceptions that are thrown, Log4Net allows a much more granualar approach to program debugging.

With log4net it is possible to enable logging at runtime without modifying the original application binary and without incurring a high performance cost. Multiple “levels” of logging can be set within your program as well, and so you can determine quickly where “fatal” errors occur, and where “warnings” occur that can be ignored in the safe running of your application.

Log4Net enables all of these things whilst providing the same level of control over the logging format and location as Elmah. You can decide whether you’d like your debug message sent  to a database, a text file, or indeed a TCP port. Different “Appenders” define where and how to send the messages, so if there’s somewhere else you’d like to see errors, you can easily write your own appender to perform this. Again, the appenders are defined in the web.config file.

  1. Imports log4net
  2. Imports log4net.Config
  3. Private Shared log As log4net.ILog
  4. Public Sub Page_Load(Byval s as Object, Byval e As EventArgs) Handles MyBase.Load
  5. log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType)
  6. BasicConfigurator.Configure()
  7. log.Debug(“Debug Message”)
  8. log.Warn(“Warning Message”)
  9. log.Fatal(“Fatal Message”)
  10. End Sub

Enterprise DT FTP Library

URL: http://www.enterprisedt.com/products/edtftpnet/overview.html

Enterprise DT is a great little FTP library, that performs all the needful without you getting your hands too dirty. It works with both web and offline applications, and again is a port of a Java library. I’ve used it for automating FTP tasks at the command line, sending photos between two sites automatically via FTP, and for sending feeds to google. It’s really easy to use, and saves you from having to write your own FTP operations. Fire it up, pass some usernames and passwords to it, and away we go. As below:

  1. Dim ftp as FTPConnection = new FTPConnection()
  2. ftpConnection.ServerAddress = “myserver”
  3. ftpConnection.UserName = userName
  4. ftpConnection.Password = password
  5. ftpConnection.Connect()
  6. ftpConnection.UploadFile(localFilePath, remoteFileName)
  7. ftp.Close()

HtmlAgilityPack

URL: http://www.codeplex.com/htmlagilitypack

The Html Agility Pack is a library for parsing HTML. It is particularly useful if you are doing any kind of scraping work, with the main object of the software to transform real world HTML into structured and parseable DOM structure. It supports plain XPATH or XSLT syntax for traversing through HTML, making loops and extraction of text a breeze.  Knowing these two technologies isn’t a pre-requisite to using it, but it sure as heck helps.  You don’t have to setup the WebRequest or anything to grab remote files, which is handy – as you’ll see from the example.

  1. Dim hw As New HtmlAgilityPack.HtmlWeb
  2. Dim doc As HtmlAgilityPack.HtmlDocument
  3. doc = hw.Load(“http://blog.webdistortion.com”)
  4. For Each s As HtmlAgilityPack.HtmlNode In doc.DocumentNode.SelectNodes(“//a[@href]“)
  5. Dim att As HtmlAgilityPack.HtmlAttribute = s.Attributes(“href”)
  6. Response.Write(att.Value & vbCrLf)
  7. Next

OpenAuth Library

URL: http://code.google.com/p/oauth-dot-net/

Open Auth is slowly becoming the norm, with web apps many preferring its usage over other less secure forms of authentication. This library is a .NET implementation of OpenAuth, and is mighty useful if you need to get up and running quickly. You are sure to run into a web service that needs you to auth via it. Google, Yahoo, Netflix and Twitter all support OpenAuth to interact with their service. The code needed for open auth is more extensive than some of the other bits and bobs, and has been better explained by others. Some of these links are worth a look.

  • Shannon Whitley offers this example: Code | Live demo
  • Daniel Crenna’s examples:

OAuth Specification

The OAuth Workflow

OAuth Walkthrough

Microsoft AntiXSS library

URL: http://bit.ly/toCrt

This is one of the security packs that MS have released to help .NET developers write better, more secure code. Essentially it is an encoding library designed to help protect ASP.NET web-based applications from XSS attacks, and works on the principals of inclusion (white-listing) to accept valid characters. I’ve used it successfully on a couple of projects, and some of the pre-written methods have been rigourously tested by leading security experts.

  1. Microsoft.Security.Application.AntiXss.HtmlEncode(strNotrust)
  2. Microsoft.Security.Application.AntiXss.JavaScriptEncode(strNotrust)

C5 Collections – Collections for .NET

URL: http://www.itu.dk/research/c5/

C5 provides functionality and data structures not provided by the standard .Net System.Collections.Generic namespace, such as persistent tree data structures, heap based priority queues, hash indexed array lists and linked lists, and events on collection changes. Also, it is more comprehensive than collection class libraries on other similar platforms, such as Java. Unlike many other collection class libraries, C5 is designed with a strict policy of supporting “code to interface not implementation”. Definitely worth a look.

Honourable Mentions

Dependency Injection/Inversion of Control

Logging

Compression

Ajax

Data Mapper

ORM

Charting/Graphics

PDF Creators/Generators

Unit Testing/Mocking

Automated Web Testing

URL Rewriting

Controls

Unclassified

More