May 2006 Entries

Dependency Injection with StructureMap

 

I have posted previously about using Spring.NET as an IoC container and how we can implement some of this out of the box with the System.ComponentModel namespace. StructureMap is another dependency injection framework that in my opinon is much more approachable than Spring out of the box. Why? - Let's take a look at what it takes to start requesting services from the StructureMap container.

 

IRule rule = ObjectFactory.GetInstance<IRule>();
or for .NET 1.1

 

IRule rule = ObjectFactory.GetInstance(typeof(IRule)) as IRule;
To begin with, StructureMap now supports generics (granted the 1.0.2 release of Spring does as well), however what is important is I didn't have to create an IResource to initialize an XmlObjectFactory to begin requesting services (again there are some shortcuts in Spring as well but lets continue on). Custom backing stores can be defined for the configuration by deriving from MementoSource in StructureMap. Something that I think is important to note here is that if you aren't using generics, you will request a particular service via a Type, which is different from Spring in which you provide a string as the look up key within the configuration. In my opinion this is nice as it gives me compile time confirmation that I am requesting a type that exists, whereas I could possibly misspell the string if I were using Spring and the compiler wouldn't skip a beat.

Another nice aspect about StructureMap is the ability use attributes to decrease the amount of configuration data that needs to be stored within an XML file. One of the major complaints with Spring is the excess configuration information. This also allows for code to be refactored without making changes to the configuration file. Also, configuration data can be broken up into several config files or even stored as embedded resources. StructureMap comes with StructureMapExplorer as well as StructureMapDoctor to help diagnose problems. I did have to apply the [STAThread()] attribute to the Main method for the StructureMapExplorer code, without that, an instance of the AxWebBrowser could not be created and the application would crash. Since they are targetting .NET 2.0, maybe they will consider using the new WebBrowser class? Overall Jeremy Miller and Jeffrey Palermo have created a very nice product, well worth checking into for your next design.

Asynchronous File I/O with NAnt

 

I recently needed to recursively iterate over a directory and change the content of files based on a search term. While there are certain NAnt tasks that have been defined to perform similar operations such as the foreach task used in conjunction with a loadfile and its associated filterchain and replacestring, I didn't see an easy way to recursively navigate the directory and perform my changes. A potential solution might have been nesting several foreach tasks and assigned the child directories to a property and subsequently call a different task to pull that directory out of the property, it didn't seem very natural or safe (could the property change before the second NAnt task requests it's value?). To my knowledge there is no locking mechanism in NAnt for properties. I'm interested if anyone has tackled this issue before directly within NAnt. Because custom NAnt tasks are easy to write, here is what I came up with. Initially I was performing the reading and writing of the file synchronously but the performance was horrible, a simple rewrite of that portion allowed me to perform asynchronous file I/O and my performance bottleneck disappeared. The follow code below allows me to define my task in NAnt as follows:

 

<recurse directory="${ProjectPath\src\${ProjectName}.Web" lookfor="SomeWord" replacewith="${ProjectName}" />

 

 

namespace developernotes.tasks
{
using System;
using
System.IO;
using
System.Text;
using
NAnt.Core.Util;
using
NAnt.Core.Attributes;
using
System.Text.RegularExpressions;

[TaskName("recurse")]
public class RecurseTask : NAnt.Core.Task
{
public RecurseTask(){}

[TaskAttribute(
"directory", Required=true)]
public string Directory
{
get{return directory;}
set{directory = value;}
}
private string directory = string.Empty;

[TaskAttribute("lookfor", Required=true)]
public string LookFor
{
get{return lookfor;}
set{lookfor = value;}
}
private string lookfor = string.Empty;

[TaskAttribute("replacewith", Required=true)]
public string ReplaceWith
{
get{return replacewith;}
set{replacewith = value;}
}
private string replacewith = string.Empty;

[TaskAttribute("pattern")]
public string Pattern
{
get{return pattern;}
set{pattern = value;}
}
private string pattern = "*.*";

private void
WalkDirectory(DirectoryInfo di)
{
if(di != null)
{
FileInfo[] fia
= di.GetFiles(this.Pattern);
if
(fia != null)
{
foreach(FileInfo fi in fia)
{
ReplaceFileContent(fi.FullName)
;
}
}

foreach(DirectoryInfo d in di.GetDirectories("*.*"))
{
WalkDirectory(d)
;
}
}
}

protected override void ExecuteTask()
{
DirectoryInfo di
= new DirectoryInfo(this.Directory);
WalkDirectory(di);
}

public class FileState
{
public byte[] Data;
public string
FileName;
public
FileStream FileStream;
}

private void ReplaceFileContent(string file)
{
FileStream fileStream
= new FileStream(file, FileMode.Open);
FileState state = new FileState();
state.FileStream = fileStream;
state.FileName = file;
state.Data = new byte[fileStream.Length];
fileStream.BeginRead(state.Data, 0,
(
int)fileStream.Length,
new AsyncCallback(ReadDone), state);
}

private void ReadDone(IAsyncResult result)
{
FileState state
= result.AsyncState as FileState;
Stream stream = state.FileStream;
int
bytesRead = stream.EndRead(result);
stream.Close();
if
(bytesRead != state.Data.Length)
{
throw new ApplicationException("Invalid read:"
+ state.FileName);
}
string content = ASCIIEncoding.ASCII.GetString(state.Data);
string
update = Regex.Replace(content, this.LookFor,
this.ReplaceWith, RegexOptions.IgnoreCase);
WriteContent(state.FileName, update);
}

private void WriteContent(string file, string content)
{
FileStream fileStream
= new FileStream(file,
FileMode.Truncate)
;
FileState state = new FileState();
state.FileStream = fileStream;
byte
[] data = ASCIIEncoding.ASCII.GetBytes(content);
fileStream.BeginWrite(data, 0, data.Length,
new AsyncCallback(WriteDone), state);
}

private void WriteDone(IAsyncResult result)
{
FileState state
= (FileState)result.AsyncState;
Stream stream = state.FileStream;
stream.EndWrite(result);
stream.Close();
}
}
}

Intellisense for SQL Server

 

I have mentioned previous how I enjoy working with Red-Gate software, they have done it again and this time it's free. Now you can get Intellisense for SQL inside Query Analyzer, Enterprise Manager, SQL Server Management Studio and Visual Studio 2003/2005. Here's a screenshot:

Download it here.

Tech Ed 2006 BoF Session

 

Eric Jacobs and I will be holding a BoF session at Tech Ed called ".NET User Group Open Forum" Tuesday night at 7:45. The purpose is to discuss ways to facilitate a .NET user group in your area, driving membership and promoting good content. We will discuss both things to do and not do when running your user group. If you are a member of an existing user group or would like to start one in your area and will be at Tech Ed, stop by our BoF session and share your thoughts. Unfortunately I won't be able to make it to Jeffrey Palermo BoF as we are scheduled at the same time, hopefully someone will post highlights from his "Agile Development with .NET" BoF session.