Wednesday, July 9, 2008

Designing a batch system with.net 2.0

A common requirement for larger scale systems is to have some kind of batch processing system. Batch processing is beneficial when you have certain tasks that consume alot of resources and the tasks are not urgent. An example might be sending out an email notification to subscribers or printing invoices.
There are a few things to keep in mind when designing a batch system:
  • You should always use a sufficient amount of logging to help you resolve issues.
  • Designing a good BatchJob base class will make things alot easier for future development.
  • Batch system events should be visible.
  • The Batch System should be easy to re-configure (dont hard code stuff you shouldnt).
  • Build provisions for batch job failiures and time outs.
  • Dispose of resources properly.

I'll write a bit about how I implemented a batch system I just finished using c#.net and an asp.net 2.0 application to provide visibilty of batch system events.

Sunday, July 6, 2008

File upload manager Web User Control in ASP.net


Here is some code for a File upload manager control.
This control can:


  • Upload multiple files

  • Remove any uploaded files

  • Check for duplicate uploads

  • Reduces risk of concurrent file access issues by assigning a sessionId sub-folder under the upload directory.

  • Remembers uploaded files over post backs.

All you have to do is assign the FileUploadDirectory property. This will be a full local path on the local machine.


Note. I have written this control with the intention of creating a re-usable dll, to see how to do this check my blog here: http://maoriitguy.blogspot.com/2008/07/creating-ascx-library.html


Create a new file in VS named FileUploadManager.ascx and paste this code:



<%@ Control Language="C#" ClassName="DKCS.FileUploadManager" %>
<script runat="server"> private string _fileUploadDirectory;
public ListItemCollection UploadFiles { get { return CheckBoxList1.Items; } }
public string Title { get { return Label1.Text; } set { Label1.Text = value; } }
public string FileUploadDirectory { get { return HiddenFieldUploadDir.Value; } set { if (!System.IO.Directory.Exists(value)) ThrowNotImplementedException();
_fileUploadDirectory = value + "/" + Session.SessionID;
try { System.IO.Directory.CreateDirectory(_fileUploadDirectory); } catch { ThrowNotImplementedException(); }
HiddenFieldUploadDir.Value = _fileUploadDirectory; } }
protected void Button2_Click(object sender, EventArgs e) { if (FileUpload1.FileName == null FileUpload1.FileName.Equals("")) return;
if (FileUploadDirectory == null FileUploadDirectory.Equals("")) { ThrowNotImplementedException(); }
//check File exists foreach (ListItem item in CheckBoxList1.Items) { if (item.Text.Equals(FileUpload1.FileName)) return; } string localFilePath = FileUploadDirectory + "/" + FileUpload1.FileName; FileUpload1.SaveAs(localFilePath);
CheckBoxList1.Items.Add(new ListItem(FileUpload1.FileName, localFilePath)); Button1.Enabled = true; }
private void ThrowNotImplementedException() { throw new NotImplementedException("The FileUploadManager.FileUploadDirectory property must be set to a absolute directory path on the local server. Ensure the asp.net application has permissions to read and write to the upload directory."); }
protected void Button1_Click(object sender, EventArgs e) { ListItemCollection temp = new ListItemCollection();
foreach (ListItem item in CheckBoxList1.Items) { if (item.Selected) { System.IO.File.Delete(item.Value); temp.Add(item); } }
foreach (ListItem item in temp) { CheckBoxList1.Items.Remove(item); }
if (CheckBoxList1.Items.Count == 0) { Button1.Enabled = false; } }
</script>
<div> <table> <tr> <td align="center" colspan="2"> <asp:Label ID="Label1" runat="server" Text="Upload Files"></asp:Label></td> </tr> <tr> <td> <asp:FileUpload ID="FileUpload1" runat="server" Width="300px" Height="24px" /></td> <td> <asp:Button ID="Button2" runat="server" Text="Upload" OnClick="Button2_Click" Width="80px" /></td> </tr> <tr> <td colspan="2"> <asp:CheckBoxList ID="CheckBoxList1" runat="server"> </asp:CheckBoxList> <asp:HiddenField ID="HiddenFieldUploadDir" runat="server" /> </td> </tr> <tr> <td align="center" colspan="2"> <asp:Button ID="Button1" runat="server" Text="Remove Selected" Enabled="False" OnClick="Button1_Click" /></td> </tr> </table></div>




Here is a screen of it inside a WebPartZone:


Creating an .ascx library in ASP.net

Here is a solution that will enable you to create .dlls for those useful re-usable web user controls you've created and use them in future asp.net web applications.

The msdn has a guide over here: http://msdn.microsoft.com/en-us/library/aa479318.aspx

lets assume you already have some web user controls in a asp.net website, if you dont, create some...
Heres how I got it to work:

  1. For your .ascx control, embed your code behind into the page in a script block.
    <script runat="server">

    You will need to modify the Control tag, you should only use the Language and ClassName attributes. eg.
    <%@ Control Language="C#" ClassName="DKCS.FileUploadManager"%>
    Notice how I have prefixed the ClassName attribute with my namespace DKCS.
  2. make sure the code compiles and test your control. Its a good idea to delete the code behind file if there was one being used previously.
  3. Publish your website. The purpose of doing this is to generate the dlls for your controls.
    You will need to use these settings:
    -Deselect Allow this precompiled site to be updateable
    -Select Use fixed naming and single page assemblies
    -I also deselected enable strong naming on precompiled assemblies. This is useful if you want to put your control in the GAC.
  4. Now browse to the location where you published your site to, look in the bin directory and you'll see some dll's in there. Your control will be named something like this:
    App_Web_fileuploadmanager.ascx.cdcab7d2.dll

    And there you have it, a dll with your control in it. It is actually a custom control but it has not lost any of its functionality as a web user control.

To use the dll:

  1. Add a reference to the dll in your website.
  2. On the page you want to use the control you will need to use the Register tag. Mine was configured like this:

    <%@ Register TagPrefix="DKCS" Namespace="DKCS" Assembly="App_Web_fileuploadmanager.ascx.cdcab7d2" %>
  3. You should now be able to use your control on the page like this:



    The designer might not render it but it should be ok once you run it in a browser.