Thursday, June 16, 2005

Strong Names and PublicKeyTokens

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/strong_names_and_publickeytokens.htm]



Every now and then I see config files containing lines like:



<add assembly="System.Data,  Version=1.0.2411.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
</assemblies>



When I first came to .Net I tried to avoid this, but it actually serves a useful purpose: specifying your assemblies in the config ensures that your app is using the correct version of an assembly. Many developers I've known don't want to deal with details like that, so I'm going to blog about some things that I've found to make it easier.
Where does this info come from?
This is the assembly's FullName. You could view it with the following code:

public string GetFullName(string strAssemblyPath) {
    Assembly a = Assembly.LoadFrom(strAssemblyPath));
    return a.FullName;
}
It has the following parts:
  • Assembly Name
  • Version
  • Culture
  • PublicKeyToken (generated from a strong-name)
This link on MSDN provides more info about Assembly names.
How can I tell the version and PublicKeyToken of an assembly?
You can tell both via the Assembly.FullName property as mentioned above. But you can also use other tools.
  • Assembly - right click on the assembly in Windows Explorer, and look at the "Version" tab.

  • PublicKeyToken  - use the Strong Name tool (sn.exe), run "sn -T C:\Temp\Common.dll" with the BIG 'T' (not little 't', which gives the token for the inline file instead).
My app doesn't need to worry about this. How can I simplify my life?
If you remove a section from the config file, then it won't filter on that. For example, if you don't care about the strong name, then you can simply remove it and use this instead:
<add assembly="System.Data,  Version=1.0.2411.0, Culture=neutral/></assemblies>
However, this could get you in trouble if multiple assemblies with different strong names are used later on.
Next...
I'll discuss some practical application of these concepts in a future post.

Monday, June 13, 2005

.Net Tools besides NUnit, NAnt, and NDoc

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/net_tools_besides_nunit_nant_and_ndoc.htm]

There are a lot of other great and free tools out there besides just NUnit, NAnt, and NDoc. I'm always keeping my eyes out for freeware tools because they can really simplify one's life. Two things I do to help organize this: I have a special folder in IE dedicated to just tools, and each new tool gets its own subfolder. This provides a place if I ever come upon other articles or links for that tool. If I dump every tool-related download, FAQ, and article, then the folder gets unmanageable. Once you get 20+ tools, this is a big deal. I also have a corresponding folder structure on my hard drive where I can save the installers. I don't download them directly to the ProgramFiles folder because it gets flooded.

Here are some tools that aren't as popular as NUnit, but I've still found useful.

Free online tools (provided as a web-page):

Free Downloads:

I'm sure this list will continue to grow.

Sunday, June 12, 2005

Using WebUIValidation.js in your own controls

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/using_webuivalidationjs_in_your_own_controls.htm]

ASP.Net includes several validator controls. These controls first validate data on the client using JavaScript. Much of the scripting for this is contained in the WebUIValidation.js file, which is in an IIS directory like \aspnet_client\system_web\1_1_4322. Each page containing any validator already includes this script, therefore you can reuse any of its functions in your own controls. For example, view the source of an aspx.page that has a validator on it, and you'll see a reference to the WebUIValidation.js file. Because it's a bunch of free methods, lets look at this file to see how we can benefit from it.

The file has many functions. I've included descriptions for some of the more relevant ones:

  • ValidatorUpdateDisplay(val)
  • ValidatorUpdateIsValid()
  • ValidatorHookupControlID(controlID, val)
  • ValidatorHookupControl(control, val)
  • ValidatorGetValue(id) - gets the value of a control given its Id
  • ValidatorGetValueRecursive(control)
  • Page_ClientValidate()
  • ValidatorCommonOnSubmit()
  • ValidatorEnable(val, enable) - can enable/disable a specific validator
  • ValidatorOnChange()
  • ValidatorValidate(val)
  • ValidatorOnLoad()
  • ValidatorConvert(op, dataType, val)
  • ValidatorCompare(operand1, operand2, operator, val)
  • CompareValidatorEvaluateIsValid(val) - used by Compare Validator
  • CustomValidatorEvaluateIsValid(val)  - used by Custom Validator
  • RegularExpressionValidatorEvaluateIsValid(val) - used by Regular Expression Validator
  • ValidatorTrim(s) - trims excess white space from input
  • RequiredFieldValidatorEvaluateIsValid(val) - used by Required Field Validator
  • RangeValidatorEvaluateIsValid(val) - used by Range Validator
  • ValidationSummaryOnSubmit()

The ID (such as in the ValidatorGetValue) indicates a string. The 'val' parameter indicates the Validator control itself, not just the ID. I previously wrote about how to use the ValidatorEnable to Disable Validators on the Client. However you can use other methods to get the value of a control or extend existing validator functionality. This is almost essential if you're going to build your own validators by deriving from BaseValidator.

JavaScript can be messy to many developers, and it's helpful to know that such a toolbox of production-ready scripts is already available to your pages.

Wednesday, June 8, 2005

Non-VSS Source Control for Easier Automation of ASP.Net Projects

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/nonvss_source_control_for_easier_automation_of_aspnet_projec.htm]

Like many Microsoft developers, I've used VSS as my primary source control system - and suffered for it. Perhaps the biggest problem I've had with VSS is the inability to batch the creation of ASP.Net web projects. Such applications require a virtual directory, and it seems like VSS hard-codes this info into a binary (not even an XML file) somewhere.

Automating Web project creation is useful for improving process. For example, you could create a batch script that get a version (whether the latest of a branch) from source control, and does all the preparation work to make it ready for development (set config values, create its own databases, make the virtual directories, etc...)

Normally to automate ASP.Net app creation (which I'll discuss more in another blog), you need to:

  1. Get the source code
  2. Create the virtual directory in IIS (with VBS, Directory Services, or even NAnt's tag).
  3. Set the virtual directory in the *.webinfo file
  4. Set the virtual directory in the solution file

The problem becomes when VSS is tightly integrated into Visual Studio, then it creates a fifth step:

  1. Find that binary file, "decode" it, set the virtual directory, and recode.

After *hours* of looking at this, I finally had to move onto other things.

However, my new firm (Ameripay) uses non-VSS source control that isn't tightly integrated into VS.Net, so it doesn't have that limiting 5th step. Consequently, I can now automate the creation of Web projects.

Sunday, June 5, 2005

Creating Excel Workbooks in .Net

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/creating_excel_workbooks_in_net.htm]

Every now and then I find myself wanting to dynamically create an Excel Workbook in .Net. Maybe it's for a data export, or to set up a structured template. Either way, there's only a small set of common utilities needed to get started. However, because Excel's automation model is based in COM (not .Net), it can be difficult to create those utilities. So below are the ones I've started used. Two disclaimers:

  1. I have only used these for simple, internal tools, not enterprise-critical production environments. Therefore they are not streamlined for performance.
  2. I've collected snippets here and there from other articles (which I no longer know where), so the idea isn't original.

My goal is to be able to programmatically create an Excel sheet, like so:

ExcelHelper.Utilities u = new ExcelHelper.Utilities();
u.AddSheet("Test1");
u.SetCellContent("A",new Cell("B",3), "Hello World!");
u.SetCellContent("A",new Cell("B",4), "=B3");
u.Show = true;

In this code, I first create an ExcelHelper.Utilities object which provides the methods to create and manipulate the workbook. I then add a sheet, set specific cell contents, and show the end result. This is sufficient for assembling most simple workbooks.

The code for ExcelHelper is below. Note that we needed to import the Microsoft Excel Core Library (in the COM tab):

using System;
using Microsoft.Office.Core;
using System.Diagnostics;

namespace ExcelHelper
{
  public class Utilities
  {
    public Utilities()
    {
      this.StartExcel();
    }

    private Excel.Application excelApp = null;

    private void StartExcel()
    {
      if( this.excelApp == null )
      {
        this.excelApp = new Excel.ApplicationClass();
//starts an Empty instance of Excel
      
        CreateNewWorkBook();
      }
    }

    private void CreateNewWorkBook()  
//string strFilePath
    {
      Excel.Workbook wb = this.excelApp.Workbooks.Add(Type.Missing);

     
//delete all worksheets but 1:
      DeleteSheet(3);
      DeleteSheet(2);
    }


    public void DeleteSheet(int intIndex)
    {
      ((Excel.Worksheet)this.excelApp.ActiveWorkbook.Sheets[intIndex]).Delete();
    }

    public void AddSheet(string strName)
    {
      Excel.Worksheet newWorkSheet;
      newWorkSheet =
        (Excel.Worksheet)this.excelApp.Worksheets.Add(
        Type.Missing, Type.Missing, Type.Missing, Type.Missing);
      newWorkSheet.Name = strName;
    }

    public void SetCellContent(string strWorkSheet, Cell c, string strContent)
    {
      Excel.Worksheet sheet1 =
        (Excel.Worksheet)this.excelApp.Sheets.get_Item(1);
      ((Excel.Range)sheet1.Cells[c.Row,c.Column]).Value2 = strContent;
    }


    public bool Show
    {
      get 
      {
        return this.excelApp.Visible;
      }
      set
      {
        this.excelApp.Visible = value;
      }
    }

  }
}

Notice that I also created a "Cell" object. This simply takes a value in the familiar Letter(Column)-Number(Row) format and translates it to a Number-Number used by Excel's automation object.

using System;namespace ExcelHelper{  public struct Cell  {    public Cell(int intRow, int intCol)    {      _intRow = intRow;      _intCol = intCol;    }    public Cell(string strCol, int intRow)    {      _intRow = intRow;      _intCol = ConvertStringToIntColumn(strCol);      if (_intCol > 256)        throw new ArgumentException("Column cannot be greater than IV (256). Value was '" + strCol + "'.");    }    private static int ConvertStringToIntColumn(string strCol)     {      strCol = strCol.ToUpper();      //A --> 1, BD --> (26+4), IV      //Can only have max of two characters.      char[] ach = strCol.ToCharArray();            if (ach.Length == 0)        throw new ArgumentException("Column cannot have 0 length. Must be A - IV. Was: '" + strCol + "'.");            else if (ach.Length == 1)        return GetNumberValue(ach[0]);      else        return 26*GetNumberValue(ach[0]) + GetNumberValue(ach[1]);    }    private static int GetNumberValue(char c)     {      return (int)c - 64;    }    private int _intRow, _intCol;    public int Row     {      get       {        return this._intRow;      }    }    public int Column     {      get       {        return this._intCol;      }    }  }}

Thursday, June 2, 2005

Multiple Onclick JavaScript Events on a Single Control

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/multiple_onclick_javascript_events_on_a_single_control.htm]

Change History: This post was updated on July 5, 2005. See Update at bottom.

You can't add two identical events to the same Html control. For example, if you have a button with an onclick event, and you add another onclick at the server, the second event will overwrite the first. This becomes a problem if you need to add an event to some variable control, but you don't know what events that control already has. However, .Net provides a way to handle this.

We can:

  1. See if the variable html control already has an onclick event.
  2. If it does, then dynamically register a new script that calls both the original onclick script as well as the new script.
  3. Replace the variable control's onclick value with that new script.

The following code snippets this. This first block shows a simple JavaScript (with a parameter) being called by a button's onclick event.

function DoStuff1(var1) {
    alert('1: ' + var1);
}

...

...

This snippet shows the server code to check if an onclick event already exists, and add a new wrapper if needed. Note that it persists the onclick values in viewstate to ensure that the wrapper function doesn't wrap itself upon postback. For example, if the first onclick event called DoStuff1(), and we wanted to dynamically add a new onclick function DoStuff2(), we could create a wrapper function Wrapper1() that called both functions, and was called from the onclick. Wrapper1() becomes the new value of the button's onclick.

private void Page_Load(object sender, System.EventArgs e)
{
    string s = this.Button1.Attributes["onclick"];
    if (!Page.IsPostBack)
        OriginalJSFunction = s;
    if (s == null)
        this.Button1.Attributes.Add("onclick","DoStuff2()");
    else
    {
        if (!Page.IsClientScriptBlockRegistered("JS1"))
            Page.RegisterClientScriptBlock("JS1",GetJS(OriginalJSFunction));
        this.Button1.Attributes.Add("onclick","Wrapper1()");
    }
}

private string OriginalJSFunction
{
    get
    {
        object o = ViewState["OriginalJSFunction"];
        if (o == null)
            return "";
        else
            return o.ToString();
    }
    set
    {
        ViewState["OriginalJSFunction"] = value;
    }
}

private string GetJS(string strOriginalFn) //will handle initial JS with params
{
    System.Text.StringBuilder sb = new System.Text.StringBuilder();
    sb.Append(@"
       
    ");

    return sb.ToString();
}

 While this approach is tedious, it lets you dynamically add an event to a control without overwriting that control's existing event. This is useful when making your own custom controls that integrate with existing Html controls already on the page.

UPDATE July 5, 2005

Since writing this post, I've come across a cleaner technique. You can run multiple JavaScripts simply by listing them in the onclick event:

 id="Button1" onclick="DoStuff1('Hello1');Method2();Method3()"

Therefore we can simplify our codebehind. This has two main changes: (1) It modifies OriginalJSFunction to append the ";" to the end of a non-function, (2) It then always adds an onclick method, as opposed to checking if there already exists an onclick method.

private void Page_Load(object sender, System.EventArgs e)
{
  string s = this.Button1.Attributes["onclick"];
  if (!Page.IsPostBack)
    OriginalJSFunction = s;
   
  this.Button1.Attributes.Add("onclick",OriginalJSFunction + ";DoStuff2()");
}

private string OriginalJSFunction
{
  get
  {
    object o = ViewState["OriginalJSFunction"];
    if (o == null)
      return "";
    else
      return o.ToString() + ";";
  }
  set
  {
    ViewState["OriginalJSFunction"] = value;
  }
}

Monday, May 30, 2005

Automating process with Dynamically Compiled C#

[This was originally posted at http://timstall.dotnetdevelopersjournal.com/automating_process_with_dynamically_compiled_c.htm]

I had the opportunity to have another article published in .Net Developer's Journal this month: Automating Your Processes - an NAnt Case Study. The article compares various techniques to automate processes, and then makes a plug for using dynamically compiled C#. This gives the best of both worlds - the flexibility of scripting but the structure and support of a compiled OOP language. The article concludes by applying this to an NAnt build process.

At this time, the article requires a subscription to .Net Developer's Journal to view.