Thursday, July 30, 2009

Using a builder like pattern with MVC Helper Methods

The Microsoft MVC framework is a powerful new technology but after some work I found the helper extension methods to be a little restrictive. While creating copious methods with overloads it dawned on me a string builder like pattern could be used
Here is a pseudo code example what was implemented.


//Example of builder class
using System.Web.Mvc;
public class InlineTagBuilder : TagBuilder, IHelperBuilder
where T : InlineTagBuilder
{
public InlineTagBuilder(HtmlHelper helper, string tagName, TagRenderMode mode)
: base(tagName)
{

Helper = helper;
Mode = mode;
}

protected TagRenderMode Mode { get; private set; }

public HtmlHelper Helper { get; private set; }

new public T AddCssClass(string value)
{
base.AddCssClass(value);
return (T)this;
}

new public T MergeAttribute(string key, string value, bool replaceExisting)
{
base.MergeAttribute(key, value, replaceExisting);
return (T)this;
}

new public T MergeAttribute(string key, string value)
{
base.MergeAttribute(key, value, false);
return (T)this;
}

new public T MergeAttributes(IDictionary attributes, bool replaceExisting)
{
base.MergeAttributes(attributes, replaceExisting);
return (T)this;
}

new public T MergeAttributes(IDictionary attributes)
{
base.MergeAttributes(attributes, false);
return (T)this;
}

new public T SetInnerText(string innerText)
{
base.SetInnerText(innerText);
return (T)this;
}

public T AppendAttributeValue(string key, string right)
{
string left;
if (Attributes.TryGetValue(key, out left))
{
Attributes[key] = String.Concat(left, right);
}
else
{
Attributes.Add(key, right);
}
return (T)this;
}

public T InsertAttributeValue(string key, string left)
{
string right;
if (Attributes.TryGetValue(key, out right))
{
Attributes[key] = String.Concat(left, right);
}
else
{
Attributes.Add(key, left);
}
return (T)this;
}

public T AppendEvent(string name, string body)
{
return AppendAttributeValue(name, body);
}

public T InsertEvent(string name, string body)
{
return InsertAttributeValue(name, body);
}

public T AppendStyle(string value)
{
if (value != null && !value.EndsWith(";"))
{
value = String.Concat(value, ';');
}
return AppendAttributeValue("style", value);
}

public T SetId(string id)
{
return MergeAttribute("id", id, true);
}

public T SetTitle(string title)
{
return MergeAttribute("title", title, true);
}

public T SetInnerHtml(string innerHtml)
{
base.InnerHtml = innerHtml;
return (T)this;
}

public static implicit operator string(InlineTagBuilder builder)
{
return builder.ToString(builder.Mode);
}
}
public class AnchorTagBuilder : InlineTagBuilder
{
//Anchor Set Methods Here Each Method Returns This For Inline Stacking Of Method Calls...
}
//Add extension class for easy access from the helper in the view
public static class AnchorTagBuilderExtensions
{
public static AnchorTagBuilder Anchor(this HtmlHelper helper)
{
return new AnchorTagBuilder(helper);
}
}
//Example of what the view would look like
<html>
<body>

<%=Html.Anchor().MergeAttribute("href","http://www.microsoft.com").AddCssClass("footNote").AppendEvent("onclick","alert('Really Annoying Alert Box!')") %>

</body>
</html>

Tuesday, July 28, 2009

IIS7 Failed Request Tracing Not Writing Logs

Ran in to this today failed request tracing was enabled and showing up in the IIS Admin Console but no matter what I did the logs would not show up. It boils down to two different issues:

  1. The folder you are sending the logs to must have permissions set the app pool writing to the folder must have full control or at least write permission to the folder.
  2. And this is the gotcha, make sure you have the module in the applicationHost.config

    <globalmodules>
    ...
    <add name="FailedRequestsTracingModule" image="%windir%\System32\inetsrv\iisfreb.dll" precondition="bitness64"></add>
    ...
    </globalmodules>



Without that line in the config it will not log, there is nothing in the event log app log or www logs that will tell you its not there either.

Wednesday, July 22, 2009

Locking Multithreaded Unit Tests for Sequential Access In Visual Studio

As I mentioned in a prior post getting Visual Studio Unit tests to execute single threaded is impossible unless you want to write a replacement to the unit test runner. I have several tests that use remoting and rely on a pre-defined channel. When executing things in parallel this causes socket already in use errors. Because each test is executed in an isolated app domain using a lock on a static object wont work either. I have heard you can lock during class setup and teardown this may work due to some locking semantics in the Runner library but for those of you wanting the benefits of setting up and tearing down using class inheritance from a different assembly good luck no dice. I have chosen a very old school solution simply write a file to the unit test directory and use a mutex as a lock this works across multiple processes. Don't forget to test for the unexpected by protecting against an infinite loop Use a timeout to ensure the mutex is not infinitely held waiting for a stalled process.

public int TimeoutInSeconds { get; set; }

public string Name { get; private set; }

private Mutex Mutex
{
get
{
if (_mutex == null)
{
_mutex = new Mutex(false, Name);
}
return _mutex;
}
}

private string LockFileName
{
get
{
return String.Concat(Name, ".lock");
}
}

public override void Initialize(UnitTestBase test)
{
Mutex.WaitOne(new TimeSpan(0, 0, 0, TimeoutInSeconds, 0));
}

public override void Cleanup(UnitTestBase test)
{
Mutex.ReleaseMutex();
}


//Test Setup
long ticks = DateTime.Now.Ticks;
while (File.Exists(LockFileName))
{
Thread.Sleep(100);
if (TimeoutInSeconds > 0 && new TimeSpan(ticks - DateTime.Now.Ticks).Seconds >= TimeoutInSeconds)
{
throw new Exception(String.Format("Unable to obtain lock {0}", Name));
}
}
using (StreamWriter writer = File.CreateText(LockFileName))
{
writer.Write("Lock held by ");
writer.Write(test.GetType().FullName);
}
//Teardown
File.Delete(LockFileName);

Tuesday, July 21, 2009

Using Setup Scripts in For Visual Studio Unit Tests

I could not find much documentation on this so I thought I should post a quick overview of how to create setup scripts for Visual Studio. You specify the setup script in the testrunconfig file under "Setup and Cleanup Scripts" the script is relative to the solution file it applies to. When you run a unit test you will see a qtsetup.bat in the
TestResults\<TestName>
folder. Open the file and it will show you your script merged with the variables generated by the testing process very useful if you are looking for the Deployment Directory Path. The Setup Script is executed relative to the deployment dir folder or
TestResults\<TestName>\Out
in most cases.




One drawback to using deployment items is you cannot use wild cards in the includes. In my case this was a pretty big problem because my binaries are all in one folder, upwards of 500 files. As a work around I created a cmd file and executed a file copy instead this saved a huge amount of time when executing a unit test.

Debugging an external process through Microsoft Visual Studio Unit Test


An update to this article while the code is useful I did not end up using this because I was unable to control the process from within the unit test. Visual studio operates each unit test in an isolated app domain which makes it difficult to instantiate processes across the test run cycle. See my next post on how I have solved this particularity annoying problem. I have been met with an obstacle nearly every step of the way with the Visual Studio Unit tests. Had I not been switching to Teams I would abandon the whole thing and go back to NUnit which was a much more flexible framework.

Debugging a client server type model within Microsoft Visual Studio can be challenging when also trying to leverage the Microsoft Visual Studio Unit Test framework. The problem lies in the fact you cannot easily spin of a second process to host the server side communication and debug both processes at the same time. You will find that the debugger will only debug through the unit test or client side of the communication stack. After some research and testing I have found a way to launch and attach to a process within the unit test execution path without any extra foot work.
The following code will launch a process check to see if the code is within a debugging session then attempt to attach to that process. If you have been googleing you may have found that you can access the visual studio environment using:
(EnvDTE._DTE)System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE")

This is not the droid you are looking for, While this returns an instance of visual studio it may not be the executing instance you need to attach the debugging session to.
Thanks to Rick Strahl for publishing an article with the right COM+ incantations to retrieve all active instances the DTE I made some modifications for my purposes and viola! Something that seems like it should be so simple turns out to be quite tricky. The message filter was taken directly from the MSDN site to resolve issues with RPC_E_CALL_REJECTED message. I also as a side note I was never able to debug throgh the property that returns the executing environment. This was due to re-entry, I was in a debug session at a break point while the other process was trying to attach to the current environment context which was returning as busy which is why you will get the RPC_E_CALL_REJECTED during debugging.


public sealed class ExecuteAndAttachToBehaviorAttribute : TestFixtureBehaviorAttribute
{
private static EnvDTE.DTE __executingDevelopmentToolsEnvironment;
private const uint S_OK = 0;

public ExecuteAndAttachToBehaviorAttribute()
{
WaitForProcessInMilliseconds = 0;
}

#region Properties

///
/// Path to the executable relative to the currently executing ApplicationBase path
///

public string Path { get; set; }

public int WaitForProcessInMilliseconds { get; set; }

private Process Process { get; set; }

private EnvDTE.DTE ExecutingDevelopmentToolsEnvironment
{
get
{
UCOMIRunningObjectTable rot;
if (__executingDevelopmentToolsEnvironment == null && GetRunningObjectTable(0, out rot) == S_OK)
{
UCOMIEnumMoniker enumMon;
rot.EnumRunning(out enumMon);
if (enumMon != null)
{
const int numMons = 100;
int Fetched = 0;
UCOMIMoniker[] aMons = new UCOMIMoniker[numMons];
enumMon.Next(numMons, aMons, out Fetched);
UCOMIBindCtx ctx;

if (CreateBindCtx(0, out ctx) == S_OK)
{
MessageFilter.Register();
System.Diagnostics.Process currentProcess = System.Diagnostics.Process.GetCurrentProcess();
for (int i = 0; i < Fetched; i++)
{
object instance;
EnvDTE.DTE dte;
rot.GetObject(aMons[i], out instance);
dte = instance as EnvDTE.DTE;
if (dte != null)
{
foreach (EnvDTE.Process p in dte.Debugger.DebuggedProcesses)
{
if (p.ProcessID == currentProcess.Id)
{
__executingDevelopmentToolsEnvironment = dte;
break;
}
}
if (__executingDevelopmentToolsEnvironment != null)
{
break;
}
}
}
MessageFilter.Unregister();
}
}
}
return __executingDevelopmentToolsEnvironment;
}
}

#endregion

#region Methods

[DllImport("ole32.dll", EntryPoint = "GetRunningObjectTable")]
private static extern uint GetRunningObjectTable(uint res, out UCOMIRunningObjectTable ROT);

[DllImport("ole32.dll", EntryPoint = "CreateBindCtx")]
private static extern uint CreateBindCtx(uint res, out UCOMIBindCtx ctx);

private void Process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
System.Diagnostics.Debug.WriteLine(String.Format("Unable to launch {0} the following error information was returned from the process:\r\n{1}", Path, e.Data));
}

public override void Setup()
{
Process = new Process();
Process.StartInfo = new ProcessStartInfo(Path);
Process.Start();
Process.ErrorDataReceived += new DataReceivedEventHandler(Process_ErrorDataReceived);
#if DEBUG
if (System.Diagnostics.Debugger.IsAttached)
{
try
{
if (WaitForProcessInMilliseconds > 0)
{
Thread.Sleep(WaitForProcessInMilliseconds);
}
if (ExecutingDevelopmentToolsEnvironment == null)
throw new Exception("Unable to locate a valid instance of Visual Studio 2008, unable to attach using the current debugger instance.");
bool attached = false;
foreach (EnvDTE.Process process in ExecutingDevelopmentToolsEnvironment.Debugger.LocalProcesses)
{
if (process.ProcessID == Process.Id)
{
process.Attach();
attached = true;
}
}
if (!attached)
throw new Exception(String.Format("Unable to locate process id {0}, attach failed to complete successfully", Process.Id));
}
catch (Exception exception)
{
System.Diagnostics.Debug.WriteLine(String.Format("Unable to attach debugger to '{0}' the following exception was generated:\r\n{1}", Path, exception.ToString()));
System.Diagnostics.Debugger.Launch();

}
}
#endif
}

public override void TearDown()
{
try
{
Process.Kill();
Process.Dispose();
}
catch (Exception exception)
{
System.Diagnostics.Debug.WriteLine(String.Format("Unable to kill {0} the following error information was returned:\r\n{1}", Path, exception.ToString()));
}
}

#endregion

private class MessageFilter : IOleMessageFilter
{
public static void Register()
{
IOleMessageFilter newFilter = new MessageFilter();
IOleMessageFilter oldFilter = null;
CoRegisterMessageFilter(newFilter, out oldFilter);
}

public static void Unregister()
{
IOleMessageFilter oldFilter = null;
CoRegisterMessageFilter(null, out oldFilter);
}

int IOleMessageFilter.HandleInComingCall(int dwCallType, System.IntPtr hTaskCaller, int dwTickCount, System.IntPtr lpInterfaceInfo)
{
return 0;
}

int IOleMessageFilter.RetryRejectedCall(System.IntPtr hTaskCallee, int dwTickCount, int dwRejectType)
{
if (dwRejectType == 2)
{
return 99;
}
return -1;
}

int IOleMessageFilter.MessagePending(System.IntPtr hTaskCallee,int dwTickCount, int dwPendingType)
{
return 2;
}

[DllImport("Ole32.dll")]
private static extern int
CoRegisterMessageFilter(IOleMessageFilter newFilter, out IOleMessageFilter oldFilter);
}

[ComImport(), Guid("00000016-0000-0000-C000-000000000046"),
InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
interface IOleMessageFilter
{
[PreserveSig]
int HandleInComingCall(
int dwCallType,
IntPtr hTaskCaller,
int dwTickCount,
IntPtr lpInterfaceInfo);

[PreserveSig]
int RetryRejectedCall(
IntPtr hTaskCallee,
int dwTickCount,
int dwRejectType);

[PreserveSig]
int MessagePending(
IntPtr hTaskCallee,
int dwTickCount,
int dwPendingType);
}
}