random .NET and web development musings

If you make a table 100% width:

table {
    width: 100%
}

then you’ll find that the cells within it will arrange themselves so they have a width proportional their respective content lengths. However, sometimes you might want to have cell be as narrow as possible, and fit itself down to its contents, and let the others auto width themselves.

One options is to give the cell in question a fixed width, but this isnt a flexible solution if the width needs to vary.

Step up the following:

table {
    width 100%;
}
table td.narrow {
    width: 1px;
    white-space: nowrap;
}

the width on the cell will make it behave like the min-width property, it will be at least 1px wide, larger content will cause it to expand. Brilliant! Then add a cheeky nowrap to prevent it from wrapping at whitespace and you’re away!

Aceness

If you want to cast an Enumerable’s inner type, you can use LINQ:

var casted = myEnumerable.Cast<int>();

But what when you only know the inner type (int) at runtime?

The easiest way is the following reflection magic:

public static IEnumerable Cast(this IEnumerable self, Type innerType)
{
	var methodInfo = typeof (Enumerable).GetMethod("Cast");
	var genericMethod = methodInfo.MakeGenericMethod(innerType);
	return genericMethod.Invoke(null, new [] {self}) as IEnumerable;
}

The objectives for this post are to outline how to:

  • Create a user account in AD
  • Create a folder to hold the website code and assign the user read/execute rights
  • Grant the user access to use ASP.NET
  • Create an app pool running as our new identity
  • Create an iis site assigned to the app pool and pointing at our folder

First off you will need to add a reference to Microsoft.web.Administration.dll, which is in

c:\windows\system32\InetSrv

These are the namespaces you’ll need:

using System;
using System.Diagnostics;
using System.DirectoryServices.AccountManagement;
using System.IO;
using System.Security.AccessControl;
using Microsoft.Web.Administration;

To begin we need to get a handle on AD, specifically the container where we want our user to be created:

var usersContext = new PrincipalContext(ContextType.Domain, "MyDomain", "ou=Users,ou=MyDomain,dc=MyDomain");

Next we create the user:

var webUser = new UserPrincipal(usersContext, "username", "password", true);
webUser.Save();

We then need to grant access to ASP.NET for the user (perhaps with v2 instead of v4)

Process.Start(@"c:\windows\microsoft.net\framework64\v4.0.30319\aspnet_regiis.exe", "-ga username");

Next we create the directory where we want the code to be housed, and grant the user read and execute permission on the folder:

var deploymentDir = Directory.CreateDirectory(fullPath);
var deploymentDirSecurity = deploymentDir.GetAccessControl();

deploymentDirSecurity.AddAccessRule(new FileSystemAccessRule("MyDomain\" + webUser.Name, FileSystemRights.ReadAndExecute, InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit, PropagationFlags.None, AccessControlType.Allow));

deploymentDir.SetAccessControl(deploymentDirSecurity);

Now we can create the app pool:

var manager = new ServerManager();

var applicationPool = manager.ApplicationPools.Add("My new app pool");

applicationPool.ManagedPipelineMode = ManagedPipelineMode.Integrated;
applicationPool.ProcessModel.IdentityType = ProcessModelIdentityType.SpecificUser;
applicationPool.ProcessModel.UserName = parameters.Username;
applicationPool.ProcessModel.Password = parameters.Password;

And then the site:

var site = manager.Sites.Add("My new IIS site", "http", ":80:mydomain.com", fullPath);
site.Applications[0].ApplicationPoolName = "My new app pool";

manager.CommitChanges();

And there you have it :)

You use a convention!

public class StringPropertyConvention : IPropertyConvention, IPropertyConventionAcceptance
{
	public void Apply(IPropertyInstance instance)
	{
		instance.Length(4001);
	}

	public void Accept(IAcceptanceCriteria<IPropertyInspector> criteria)
	{
		criteria.Expect(x => x.Property.PropertyType == typeof (string));
	}
}

:D

Make sure you register the conventions with the automapper.

Peace

Time and time again I encounter repositories which are just a free-for-all mess, so here is a definitive guide to SVN and project code organisation for the tidy-inept.

Most people will use their repository for multiple projects. For this reason it is a sensible idea to have top level folders which semantically group your code. How you do this depends largely on what code/projects you have in SVN. Here’s an example:

root
- Customer A
-- Project X
-- Project Y
- Customer B
-- Project Q
-- Project X

Next, under each Project you should have:

Project X
- trunk
- branches
- releases
- tags

Sometimes its more appropriate to have the releases folder under branches (as they technically are branches), it doesn’t really matter. What is important is that you are branching your releases!

.NET Project Organisation

Here is how I organise my projects:

/
/lib
/src
/src/<projectname>.sln
/src/<projectname><assembly-specific-names>
/tools
/rakefile.rb

Nice and clean :D

The lib folder holds all your 3rd party assemblies which your project utilises.
The src folder contains…you guessed it, your source code!
The tools folder contains mainly build tools and test runners. Mine also has the NHibernate Profiler in there, too.
The rakefile is my build script, if you enjoy angled-bracketed-unneccessaryness this could be your NAnt or MSBuild file.

Next, externals. If your project shares code with another, this shared code should be under a separate project, and included via an svn:external under the lib folder. For example:

/lib/<folder-for-external>

Useful Resources

Here are some great examples of best practices for code and svn organisation.

And here is an excellent book on the subject!

One tidy life….Done!

Following on from my recent post how to test asp.net mvc views existence, I found myself with in a very similar situation! This time however, it was FileResult instead of ViewResult.

I’m not making any excuses, this is far from the prettiest or most flexible solution, but for my setup it works perfectly :)

Have some code:

public abstract class ControllerTest<TController> where TController : IController
{
	protected void AssertFileIsCorrectAndExists(FileResult result, string filename)
	{
		// hack of death!!!!
		var webProjectPath = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, Path.Combine("../../../", typeof(TController).Assembly.GetName().Name)));

			// crudely trim leading ~/
		if (filename.StartsWith("~/"))
			filename = filename.Substring(2);

		var fullPath = Path.Combine(webProjectPath, filename);

		Assert.IsTrue(File.Exists(fullPath), "File `" + fullPath + "` does not exist");
	}

Following a recent embarrassing episode where I had been banging on about how unit testing saves the world, then released a broken site, I decided a solution to prevent this happening again was necessary!

I currently test my MVC ViewResults like this:

public class when_requesting_a_login_form : ControllerTest<LoginController>
{
	private ViewResult result;

	protected override void Act()
	{
		this.result = this.SubjectUnderTest.LogIn() as ViewResult;
	}

	[Test]
	public void the_login_view_should_be_returned()
	{
		this.result.ViewName.ShouldEqual("Login");
	}
}

However this doesn’t check that the Login.aspx view actually exists (which it didn’t in the case I’m talking about, as I only found out at runtime). Below is my new test:

[Test]
public void the_login_view_should_be_returned()
{
	AssertViewIsCorrectAndExists(result, "Login");
}

This assertion not only checks that the view names match, but also that the file exists on disc.

The solution is far from elegant, but it works, which is better than nothing!

Before I show you the codes, some prerequisite knowledge about my solution setup is required.

My folders are ALWAYS organised like this:

/src/Project.Web/
/src/Project.Domain/
/src/Project.Tests.Unit/

with these folder names matching the assembly names. This allows the following very fragile code to work:

protected void AssertViewIsCorrectAndExists(ViewResult result, string viewName)
{
	ViewEngines.Engines.Clear();
	ViewEngines.Engines.Add(new TestingViewEngine());
	Assert.AreEqual(viewName, result.ViewName, "View names do not match");

	var controllerContext = Stub<ControllerContext>();
	controllerContext.RouteData = new RouteData();
	controllerContext.Controller = SubjectUnderTest;

	var controllerName = typeof(TController).Name;
	controllerContext.RouteData.Values.Add("controller", controllerName.Substring(0, controllerName.Length - "controller".Length));

	var viewEngineResult = result.ViewEngineCollection.FindView(controllerContext, result.ViewName, result.MasterName);
	Assert.IsNotNull(viewEngineResult.View, "View '" + result.ViewName + "' could not be found");
}

public class TestingViewEngine : WebFormViewEngine
{
	public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
	{
		// hack of death!!!!
		var webProjectPath = Path.GetFullPath(Path.Combine(Environment.CurrentDirectory, Path.Combine("../../../", controllerContext.Controller.GetType().Assembly.GetName().Name)));

		foreach(var location in this.ViewLocationFormats)
		{
			var virtualViewPath = string.Format(location, viewName, controllerContext.RouteData.Values["controller"]);

			// crudely trim leading ~/
			virtualViewPath = virtualViewPath.Substring(2);

			var fullPath = Path.Combine(webProjectPath, virtualViewPath);

			if (File.Exists(fullPath))
				return new ViewEngineResult(MockRepository.GenerateStub<IView>(), this);
		}

		return new ViewEngineResult(this.ViewLocationFormats);
	}
}

Crude I know, but I hope this helps someone!

Ok this was an annoying one, If your TDD.Net runner keeps telling you “No tests run (No tests where found)” and you’ve run the “InstallTDNetRunner.bat” you can try the following:

Edit the InstallTDNetRunner.bat

and comment out the last 2 lines:

regedit MSpecTDNet.reg
del MSpecTDNet.reg

Run the bat file and a .reg should appear in the same folder. Edit this.

in there you should see: AssemblyPath”=”<some path>\\Machine.Specifications.TDNetRunner.dll”

Make sure this path is actually correct (it wasnt for me). Then save the .reg and run it.

Job done :)

Just a quick one, but it took me a while to figure this out. Hopefully this can help someone else out.

To get MSpec working with ReSharper you need to copy all the MSpec dlls into the resharper’s plugins folder, so you end up with:

ReSharper/bin/plugins/machine.specifications/<dlls here>

Make sure you delete whichever one of the ReSharperRunner dlls is not your ReSharper version!

Enjoy :)