Saturday, May 7, 2005

Unit Testing Void Methods

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

I was recently asked the good question "Should you create tests for void methods?". My answer: It depends. Let's look at the following example:

public class Foo1
{
    public Foo1()
    {
        _intCounter = 0;
    }

    private int _intCounter;
    public int Counter
    {
        get
        {
            return _intCounter;
        }
    }

    public void Increment()
    {
        _intCounter++;
    }

}

In this case, the void method Increment() changes the state of the Foo1 object, which can be measured by the Counter property. So, the following would be a decent test:

[Test] public void TestVoidMethod()
{
    Foo1 f = new Foo1();
    Assert.AreEqual(0,f.Counter);
    f.Increment();
    Assert.AreEqual(1,f.Counter);
}

The intent is that we want to always test deterministic logic, such as code that contains conditions, loops, or operators. While this is straight-forward with static functions (plug in x, get y), it can still apply to void methods. The difference is that a void method may be updating another property of an instantiated object instead of a return value.

However note that void methods may lack any logic to test. For example, the method below simply wraps some architecture block that writes out files. There's no logic here to test.

public void WriteFile(string strContent)
{
    ArchitectureBlock.WriteToFile(strContent);
}

Thursday, May 5, 2005

Securing Images From Unauthorized Users

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

Sometimes we want to secure images so that only authorized users can view them. Private images might include proprietary diagrams or images with personal information (perhaps paychecks). The problem is that the nature of images in Html is simply a URL to an already-accessible file. For example, the source code for the Google logo at www.google.com is essentially . The src attribute is a relative path from the google main page, so you can directly view this image at http://www.google.com/intl/en/images/logo.gif.

For a secure image, we want:

  1. The application can access it
  2. The user cannot.

This can't merely be solved by trying to "hide" the images in some directory, because the page's source is public. They can't hide. However, we can use .Net to solve this by:

  1. Store our images in a file only accessible to the application
  2. Use System.Drawing to dynamically create an image from a file
  3. Response out that image to an ImgPage.aspx page
  4. Make an image in our main page, and set its source property to the ImgPage.aspx page.

This technique of dynamically displaying images in a WebForm is based off of MVP Scott Mitchell's article Create Snazzy Web Charts and Graphics On the Fly with the .NET Framework. The conceptual difference is that instead of dynamically creating an image (such as a chart) and outputting it, we're dynamically creating the image simply by loading in an image file.

The first step is easy. The second step requires some coding. The function below resides in ImgPage.aspx.cs. It assumes that you pass in the image file path in any way you see fit. One way is to get the filename from the querystring. For simplicity this method assumes just "gif" images, but you could get the image extension and set the type as appropriate.

private void Page_Load(object sender, System.EventArgs e)
 {
     string strFile = Request.QueryString["img"].ToString();
     SetImage(strFile);
 }
 
 private void SetImage(string strFile)
 {
     //Set Response type for an image
     Response.ContentType = @"image/gif";
 
     System.Drawing.Bitmap bm = null;
     using (bm)
     {
         bm = (Bitmap)Bitmap.FromFile(strFile);
         bm.Save(Response.OutputStream,ImageFormat.Gif);
     }
 }

First we set the page Response's content type. We then create a BitMap from a file, and save it to the Response OutputStream. Note that ImgPage.aspx is an empty page designed solely to receive this output.

Our main page has an image with the source property set to aspx page and its querystring, such as ImgPage.aspx?img=FilePath.gif. The main page code behind can then do whatever it needs to determine the correct querystring.

<asp:Image id="Image1" runat="server" ImageUrl="ImgPage.aspx">asp:Image>

While I've built a proto-type for this, I admit that I haven't tested it in an enterprise application, but it seems pretty promising.

Wednesday, May 4, 2005

Hosting a WinControl in an ASP.Net Page

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

About a year ago I had the opportunity to write an article for 4GuysFromRolla: Hosting a Windows Control in a Web Form. The article is very straight-forward. It also gets a lot of questions.

The main point is that you can host a Windows UserControl within a web page. There are some highlights from the article, and from what I've learned since then:

  • This requires that the client has the .Net Redistributable installed.
  • Passing data to and from the WinConrol is hard. You can pass data to the control by dynamically recreating the HTML for it (discussed in the article). The best way I know to get data from the control is via an Xml Web Service (not by writing to an Xml File, as I indicated in the article). Writing to a file system could be a security leak, and requires tweaking the security configuration.
  • While the WinControl appears different when embedded in the web page, it can open up a fully-functional win pop-up form. For example, you could add a Timer to the popup form, but it may not work in the WebForm.

The most common problems I get from readers trying to get their WinControl to work:

  • They haven't run the demo, and there's some issue that would be flushed out if they had.
  • It works on their Dev machine, but not someone else's - usually because the other person's machine didn't have the .Net Redistributable installed.
  • They're trying to access some secure resource - if possible I try to avoid this and use web services instead.

I think embedded WinControls are great for toys - for example you want to make a simple online educational game for a school. However, I find that their security and deployment restrictions are cumbersome for an enterprise app.

Monday, May 2, 2005

Using Trace Switches in .Net

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

.Net has some great tracing features already built in. One of them is trace switches. You can create your own switches for each business process that needs tracing, such as trying to debug a tedious problem in the development environment. You can set switches in your config to various levels, and then call them from code. The trace levels (from MSDN) are:

Trace LevelConfiguration File Value
Off0
Error1
Warning2
Info3
Verbose4

You could set the following switches in your config. If the switch is omitted from the config file (and not instantiated elsewhere), then it is simply ignored.

<configuration>   
    <system.diagnostics
>
        <switches
>
            <add name="Security" value="4"
/>
            <add name="SubmitRequest" value="4"
/>
            <add name="ExternalMessages" value="0"
/>
        switches
>
    system.diagnostics
>

    <system.web
>
        ...
    system.web
>
configuration>

You could then instantiate the switch in code like so:

private System.Diagnostics.TraceSwitch mySwitch = new System.Diagnostics.TraceSwitch("Security","Login and Security");

Finally, you could access the various level by calling the Trace (i.e. "TraceVerbose") from the switch instance. This returns a bool if the level (from the app.config) is at or higher than . For example, TraceError is true if the Level is set to Error, Warning, Info, or Verbose; otherwise, it's false.

TraceSwitches provide an easy way for simple instrumentation of our business processes.

Saturday, April 30, 2005

Source Code for MessageBox Button

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

A couple people have requested the source code for the ServerConfirm button I wrote. This adds a yes-no MessageBox to a regular html button. Clicking either yes or no triggers the appropriate server event.

Note that I wrote this version over a year ago, and there are some changes I would make:

  1. The JavaScript used to get the selected MessageBox value doesn't work in NN.
  2. This is a custom-rendered control, which is created entirely from scratch - i.e. just html controls. It could be made as an extended control instead. This would still be compiled to a DLL and be reusable among ASP.Net projects (unlike UserControls), but would give the additional advantage of keeping all the WebControl.Button's extra properties that merely the Html button lacks.

I'll cover these in a future blog post. FYI, other blog posts related to this are:

With that all said, here's the source code below. It has three main regions:

  1. Public properties (Text for the button's text value, and Message for the MessageBox).
  2. Postback Data - handles posting data back to the server
  3. Postback Events - has a yes and no event

The Render method renders all the necessary Html and JavaScript to create the button.

Imports System.ComponentModel
Imports System.Web.UI

Namespace Stall.WebControls

    "Text"
), ToolboxData("<{0}:ServerConfirmButton runat=server>")> Public Class ServerConfirmButton
        Inherits System.Web.UI.WebControls.WebControl
        Implements IPostBackDataHandler
        Implements IPostBackEventHandler

#Region
"Properties"

        Protected _strText As
String
        True), Category("Appearance"), DefaultValue("")> Property [Text]() As
String
           
Get
                Return _strText
            End
Get

            Set(ByVal Value As String)
                _strText = Value
            End
Set
        End
Property

        Protected _strMessage As
String
        True), Category("Appearance"), DefaultValue("")> Property Message() As
String
           
Get
                Return _strMessage
            End
Get

            Set(ByVal Value As String)
                _strMessage = Value
            End
Set
        End
Property

#End Region

        Protected Overrides Sub Render(ByVal output As System.Web.UI.HtmlTextWriter)
           
'output.Write([Text])

            Dim strHdnName As String = Me.UniqueID

           
'generate hidden field to store answer:
           
'
            output.WriteBeginTag("Input")
            output.WriteAttribute("type", "hidden")
            output.WriteAttribute("name", strHdnName)
            output.WriteAttribute("value", "none")
            output.Write(HtmlTextWriter.TagRightChar)

           
'generate button:
            output.WriteBeginTag("Input")
            output.WriteAttribute("value", Me.Text)
            output.WriteAttribute("type", "button")
            output.WriteAttribute("onclick", "javascript:if (confirm('" & _strMessage & "')) {document.all.item('" & strHdnName & "').value = 'Yes';} else{document.all.item('" & strHdnName & "').value = 'No';}" & Page.GetPostBackClientEvent(Me, "EventClicked"))
            output.Write(HtmlTextWriter.TagRightChar)

           
'write out source of control:
            output.Write("")

        End
Sub

#Region
"PostBack Data"

        Protected _blnConfirm As Boolean =
False

        Public Function LoadPostData(ByVal postDataKey As String, ByVal postCollection As System.Collections.Specialized.NameValueCollection) As Boolean Implements System.Web.UI.IPostBackDataHandler.LoadPostData

           
'Note: a field with Me.UniqueID must be declared for this to fire.
           
'Get the hidden field value so we know which event to trigger
            If (postCollection(postDataKey).Equals("Yes"))
Then
                _blnConfirm =
True
           
Else
                _blnConfirm =
False
            End
If

        End
Function

        Public Sub RaisePostDataChangedEvent() Implements System.Web.UI.IPostBackDataHandler.RaisePostDataChangedEvent
           
'Get the hidden field value so we know which event to trigger
        End
Sub

#End Region

#Region
"PostBack Events"

        Public Sub RaisePostBackEvent(ByVal eventArgument As String) Implements System.Web.UI.IPostBackEventHandler.RaisePostBackEvent
           
'check the hidden field to determine which event to raise.
            If (_blnConfirm)
Then
                OnConfirmYes(EventArgs.Empty)
           
Else
                OnConfirmNo(EventArgs.Empty)
            End
If
        End
Sub

        Event ConfirmYes(ByVal sender As Object, ByVal e As EventArgs)

        Protected Overridable Sub OnConfirmYes(ByVal e As EventArgs)
           
'raise the event
            RaiseEvent ConfirmYes(Me, EventArgs.Empty)
        End
Sub

        Event ConfirmNo(ByVal sender As Object, ByVal e As EventArgs)

        Protected Overridable Sub OnConfirmNo(ByVal e As EventArgs)
            RaiseEvent ConfirmNo(Me, EventArgs.Empty)
        End
Sub

#End Region

    End
Class

End
Namespace

Wednesday, April 27, 2005

Windows User Controls: Mult-View and File TextBoxes

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

A while ago I wrote some advanced TextBox windows controls:

  1. MutliTextbox - Designed to split a single big window into multiple sections, just like splitting an Excel worksheet into multiple views.
  2. FileTextBox - Designed to easily load and save files to the text area.

These take advantage of WinForms ability to write your own custom controls. These are standard Windows User Controls (System.Windows.Forms.UserControl). Such controls can be added to the toolbox under the "UserControls" tab, and have full design support.

Both of these are free controls at: http://www.timstall.com/Code/WinControls.aspx. I find these useful when I making my own tools.

Monday, April 25, 2005

Hosting Multiple Instances of an ASP.Net App

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

Hosting multiple instances of an ASP.Net web app on a single server can be useful. While running multiple instances of an *.exe is trivial, multiple instances of a web app are not because they're both served through IIS. For example, the client may want a static copy of the app for internal demos, but for various reasons unique to that client, they can't use the staging instance for their demo. Or the client may want separate apps for two totally separate vendors. They can then configure (not re-compile) the apps appropriately.

.Net can do this. Assuming that you're deploying the app via an MSI, you  primarily need to just change the MSI's version. You'll then also need to modify the apps config, such as making it point to it's own database.

However, be aware of two limitations:

  1. You'll need to change the version of the MSI file, which requires checking it out from source control and recompiling.
  2. You can't install an earlier version if a later already exists.

So, you could compile two instances, label them version 1.0 and 1.1, and then run each MSI (1.0 and then 1.1) and configure them as needed.