Popular Posts

Wednesday, June 29, 2011

Dynamically compile code at runtime

Compile Lambda or Code String to MethodInfo or Action or Func:
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;

namespace CodeCompiler
{
  public class CompilationException : Exception
  {
    public List<CompilerError> Errors { get; private set; }
    public CompilationException(CompilerErrorCollection errors) : base("Error compiling expression")
    {
      Errors = new List<CompilerError>();
      foreach (CompilerError error in errors)
        Errors.Add(error);
    }
  }

  public class Compiler
  {
    public List<string> ReferenceDLLs { get; private set; }
    public List<Assembly> ReferenceAssemblies { get; private set; }

    public Compiler()
    {
      ReferenceDLLs = new List<string> { "System.dll", "System.Core.dll", "System.Data.dll", "System.Data.DataSetExtensions.dll", "System.Xml.dll", "System.Xml.Linq.dll" };
      //ReferenceDLLs = AppDomain.CurrentDomain.GetAssemblies().Where(x => !x.IsDynamic).Select(x => x.Location).ToList();
      ReferenceAssemblies = new List<Assembly>();
    }

    public Assembly CompileCSFile(string csFilePath)
    {
      using (StreamReader reader = new StreamReader(csFilePath))
        return compileString(reader.ReadToEnd());
    }

    private MethodInfo compileStringToMethodInfo(string code)
    {
      Assembly assembly = compileString(code);
      return assembly.GetType("FakeNamespace.FakeClass").GetMethod("MethodResult", BindingFlags.Static | BindingFlags.Public);
    }

    private Assembly compileString(string code)
    {
      CodeDomProvider compiler = CodeDomProvider.CreateProvider("CSharp");
      CompilerResults result = compiler.CompileAssemblyFromSource(new CompilerParameters(ReferenceDLLs.Union(ReferenceAssemblies.ConvertAll(x => x.Location)).ToArray()), code);
      if (result.Errors.Count > 0)
        throw new CompilationException(result.Errors);
      return result.CompiledAssembly;
    }

    private List<string> defaultUsingStatements()
    {
      return new List<string>()
                       {
                            "using System;",
                            "using System.Collections.Generic;",
                            "using System.Linq;",
                            "using System.IO;"
                       };
    }

    private string surroundLambaWithFakeClass<X, Y>(string text, List<string> usingStatements = null)
    {
      usingStatements = usingStatements ?? defaultUsingStatements();
      string code = usingStatements.Aggregate((x, y) => x + "\r\n" + y) + @"

                namespace FakeNamespace
                {
                  public class FakeClass
                  {
                    public static CLASS2 MethodResult(CLASS1 param1)
                    {
                       Func<CLASS1, CLASS2> lambda = " + text + @";
                       return lambda(param1);
                    }
                  }
          }";

      code = code.Replace("CLASS1", typeof(X).ToString()).Replace("CLASS2", typeof(Y).ToString()).Replace("`1[", "<").Replace("]", ">");
      return code;
    }

    private string surroundLambaWithFakeClass<X>(string text, List<string> usingStatements = null)
    {
      usingStatements = usingStatements ?? defaultUsingStatements();
      string code = usingStatements.Aggregate((x, y) => x + "\r\n" + y) + @"

                namespace FakeNamespace
                {
                  public class FakeClass
                  {
                    public static void MethodResult(CLASS1 param1)
                    {
                       Action<CLASS1> lambda = " + text + @";
                       return lambda(param1);
                    }
                  }
          }";

      code = code.Replace("CLASS1", typeof(X).ToString()).Replace("`1[", "<").Replace("]", ">");
      return code;
    }

    private string surroundMethodWithFakeClass<X>(string text, List<string> usingStatements = null)
    {
      usingStatements = usingStatements ?? defaultUsingStatements();
      string code = usingStatements.Aggregate((x, y) => x + "\r\n" + y) + @"

                namespace FakeNamespace
                {
                  public class FakeClass
                  {
                    public static void MethodResult(CLASS1 param1)
                    {
                       " + text + @"
                    }
                  }
          }";

      code = code.Replace("CLASS1", typeof(X).ToString()).Replace("`1[", "<").Replace("]", ">");
      return code;
    }

    private string surroundMethodWithFakeClass<X, Y>(string text, List<string> usingStatements = null)
    {
      usingStatements = usingStatements ?? defaultUsingStatements();
      string code = usingStatements.Aggregate((x, y) => x + "\r\n" + y) + @"

                namespace FakeNamespace
                {
                  public class FakeClass
                  {
                    public static CLASS2 MethodResult(CLASS1 param1)
                    {
                       " + text + @"
                    }
                  }
          }";

      code = code.Replace("CLASS1", typeof(X).ToString()).Replace("CLASS2", typeof(Y).ToString()).Replace("`1[", "<").Replace("]", ">");
      return code;
    }

    public Func<X, Y> ConvertMethodStringToMethodInfo<X, Y>(string text, List<string> usingStatements = null)
    {
      string code = surroundMethodWithFakeClass<X, Y>(text, usingStatements);
      MethodInfo method = compileStringToMethodInfo(code);
      return x => (Y)method.Invoke(null, new object[] { x });
    }

    public Action<X> ConvertMethodStringToMethodInfo<X>(string text, List<string> usingStatements = null)
    {
      string code = surroundMethodWithFakeClass<X>(text, usingStatements);
      MethodInfo method = compileStringToMethodInfo(code);
      return x => method.Invoke(null, new object[] { x });
    }

    public Func<X, Y> ConvertLambaStringToMethodInfo<X, Y>(string text, List<string> usingStatements = null)
    {
      string code = surroundLambaWithFakeClass<X, Y>(text, usingStatements);
      MethodInfo methodInfo = compileStringToMethodInfo(code);
      Func<X, Y> result = x => (Y)methodInfo.Invoke(null, new object[] { x });
      return result;
    }

    public Action<X> ConvertLambaStringToMethodInfo<X>(string text, List<string> usingStatements = null)
    {
      string code = surroundLambaWithFakeClass<X>(text, usingStatements);
      MethodInfo methodInfo = compileStringToMethodInfo(code);
      return x => methodInfo.Invoke(null, new object[] { x });
    }

    public static string EscapeToProtectFromCodeInjection(string searchText)
    {
      return "\"" + searchText.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"";
    }
  }
}

Here's how to use it:
          Func<string, IEnumerable<string>> stringSplitFunction = Compiler.ConvertLambaStringToMethodInfo<String, IEnumerable<String>>("x => x.Split(new string[] { \",\" }, StringSplitOptions.RemoveEmptyEntries).AsEnumerable()");
          IEnumerable<string> result = stringSplitFunction("val1,val2");

Wednesday, June 15, 2011

Mutation Testing in C#

I found a cool tool today for doing mutation testing in C#. Basically, it runs your unit tests to analyze your code coverage, then it purposefully modifies the sections of your code that are covered, to see if the tests will still pass. The theory is, if the tests pass, then even though you have coverage, you're not asserting the right things. So, it helps you analyze the quality of your unit tests.
Check it out here: http://galera.ii.pw.edu.pl/~adr/CREAM/index.php

Saturday, June 11, 2011

Linq Recursive Extensions

Here are some extension methods to allow you to recurse through a class hierarchy:
    public static class LinqExtensions
    {
        public static IEnumerable<T> Recurse<T>(this T source, Func<T, IEnumerable<T>> getChildren) where T : class
        {
            if (source == null)
                return new List<T>();
 
            List<T> result = getChildren(source).Recurse<T>(getChildren).ToList();
            result.Insert(0, source);
             
            return result;
        }
 
        public static IEnumerable<T> Recurse<T>(this T source, Func<T, T> getChild) where T : class
        {
            if (source != null)
                yield return source;
 
            T child = getChild(source);
            while (child != null)
            {
                yield return child;
                child = getChild(child);
            }
        }
 
        public static IEnumerable<T> Recurse<T>(this IEnumerable<T> source, Func<T, T> getChild) where T : class
        {
            List<T> result = new List<T>();
            if (source != null)
            {
                foreach (T child in source)
                    result.AddRange(child.Recurse<T>(getChild));
            }
             
            return result;
        }
 
        public static IEnumerable<T> Recurse<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> getChildren) where T : class
        {
            if (source != null)
            {
                foreach (T child in source)
                {
                    if (child != null)
                        yield return child;
 
                    IEnumerable<T> subChildren = getChildren(child);
                    if (subChildren != null)
                    {
                        foreach (T subChild in subChildren.Recurse<T>(getChildren))
                        {
                            if (subChild != null)
                                yield return subChild;
                        }
                    }
                }
            }
        }
    }

Here's an example of how to use them:
    public class Node
    {
      public List<Node> Children = new List<Node>();
    }

    Node parent = new Node();
    IEnumerable<Node> children = parent.Recurse(x => x.Children);

Lambda Equality Comparer Extension Method

Here's an extension method that will convert a lambda to an equality comparer:
public static class FuncExtension
{
    private class GenericComparer<T> : EqualityComparer<T>
    {
        private readonly Func<T, T, bool> _lambda;
        private readonly bool _passThroughNulls;
        
        public GenericComparer(Func<T, T, bool> lambda, bool passThroughNulls)
        {
            _lambda = lambda; _passThroughNulls = passThroughNulls;
        }

        public override bool Equals(T x, T y)
        {
            if (!_passThroughNulls)
            {
                if (System.Object.Equals(x, default(T)) || System.Object.Equals(y, default(T)))
                {
                    return System.Object.Equals(x, default(T)) && System.Object.Equals(y, default(T));
                }
            }
            
            return _lambda(x, y);
        }
    
        public override int GetHashCode(T obj)
        {
            return 0;
        }
    }
    
    public static IEqualityComparer<T> ToEqualityComparer<T>(this Func<T, T, bool> lambda, bool passThroughNulls = false)
    {
        return new GenericComparer<T>(lambda, passThroughNulls);
    }
}

Here's an example of how to use it:
Func<SomeClass, SomeClass, bool> compareFunction = (x, y) => x.SomeProperty == y.SomeProperty && x.SomeProperty2 == y.SomeProperty2;
EqualityComparer<SomeClass> comparer = compareFunction.ToEqualityComparer();

TF14098: Access Denied creating branch

When creating a branch from main to release you may get an error like this:
Branch from Main
TF14098: Access Denied: User CTAC\dgarner needs Read permissions(s)
for $/RxInfoInquiry/Main/*.
 
Even though you're able to get latest on main & build/run the app, there is some random file/folder underneath the main branch that you don't have permissions to. This is causing you to not be able to make a new release branch.
 
You'll need to find the file & add read permissions to [DefaultCollection]\Contributors (assuming you're a developer & in that group).
 
There is a command-line utility called "TF" that you can use to interact with TFS. You can access it through the
"Visual Studio Command Prompt" found on your start menu.
To find the problem file, you'll need to recurse through all the items in the project & check their permissions.
 
 
Here's the basic steps I used. The program I had an issue with was RxInfoInquiry.
This will tell you the permissions of each folder in your project:
tf permission c:\source\RxInfoInquiry /recursive > allPermissions.txt
I then opened the file in an editor supporting regex's & I searched for any folder where Contributors didn't have read permissions.
Once the file is found & the read permissions have been granted, you should be able to create the new release branch.
If you're unlucky & it's a file, rather than a whole folder with the missing permissions, you'll need to check individual files instead of folders.
To get a list of all items in the project:
tf dir c:\source\RxInfoInquiry /recursive > c:\temp\allFiles.txt
You could take this list & convert each filename to a "tf permission" command & run it as a batch file. Then, search for the file with the missing permissions.

VS Setup Project - 401: Unable to update the dependencies of the project

There is a bug in VS2010 that causes setup projects to not compile sometimes.
 
An example error message you might get is: " 401: Unable to update the dependencies of the project. "
The following patch should correct the problem.

Find Memory Leaks or Infinite Loops From a Memory Dump with WinDBG

If you have a memory leak or infinite loop on production that you don't know how to reproduce easily on your development box, it's easy to investigate by analyzing a memory dump of the application running in production. If you can reproduce it on your development box, it's easier to use a profiling tool like JetBrains dotTrace or RedGate Ants Profiler.
Capture a memory dump

#1 -> Reproduce the problem on any machine
#2 -> Get a memory dump of the program while experiencing the problem
2a) Open Task Manager
2b) Find program in processes tab. Pay special attention to if the process name has a "*32", meaning 32-bit process
2c) Right click on process & choose create dump file
2d) A pop up window will show you where the memory dump was saved (usually C:\Users\#User#\AppData\Local\Temp\)
#3 -> Copy the memory dump to your development machine.
Analyze a memory dump
1a) When installing, check all the boxes for a full install, otherwise you won't get both the 32 & 64 bit versions.
1b) After installing, open this folder (C:\Program Files\Microsoft SDKs\Windows\v7.1\Redist\Debugging Tools for Windows\)
1c) Install both the dbg_x86.msi & dbg_amd64.msi files. (one of them will probably already be installed, based on your type of cpu, but you need both)
#2 -> Open the version (32 or 64 bit) of WinDbg that matches the type of memory dump you have. If the task manager process name had "*32", you'll need 32-bit WinDbg.
2a) C:\Program Files\Debugging Tools for Windows (x64)\windbg.exe (64-bit)
2b) C:\Program Files (x86)\Debugging Tools for Windows (x86)\windbg.exe (32-bit)
#3 -> In WinDbg select File/Open Crash Dump from the menu.
#4 -> Open the memory dump .DMP file you created from the task manager
#5 -> Find out which version of the .NET Framework the program for your crash dump runs on. [3/3.5 are extensions of 2]
#6 -> Find the directory for the correct version of the .NET Framework on your computer (including 32/64 bit).
6a) C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319
6b) C:\WINDOWS\Microsoft.NET\Framework64\v4.0.30319

6c) C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727
6d) C:\WINDOWS\Microsoft.NET\Framework64\v2.0.50727
#7 -> Type the following commands to load the .NET Debugging extension SOS into WinDbg
7a) .exepath+ C:\WINDOWS\Microsoft.NET\Framework64\v4.0.30319\ (use the path for the .NET Framework directory corresponding to your app)
7b) lmv m clr (this loads the clr dll, which is the core of the .net framework)
Note: If you get an error, you can try this alternative .load c:\Windows\Microsoft.NET\Framework64\v4.0.30319\clr.dll
7c) .loadby sos clr
Note: If you get an error, you can try this alternative .load c:\Windows\Microsoft.NET\Framework64\v4.0.30319\sos.dll

7d) .loadby mscordacwks clr
Note: If you get an error, you can try this alternative .load c:\Windows\Microsoft.NET\Framework64\v4.0.30319\mscordacwks.dll
#8 -> You should now be setup to analyze the dump file. Here are a few helpful commands:

Useful Commands
Investigate Memory Leak
!dumpheap -stat
!dumpheap -mt <string MT> (shows addresses of all strings, you can use whatever datatype is using all the memory)
!do 02ebf628 (the # should be the memory address of the object you want to view)
!gcroot 02ebf628 (find the containing class for the object at # memory address)

Investigate High CPU
!threads
~5s (change context to thread id 5)
kb 2000
!clrstack
!threadpool
!runaway
~* kb 2000
~* e !clrstack
!pe (show current exception on thread)
!dso (print objects on stack)
You can try these random commands, but the best thing is to follow the walkthrough on the following blog that matches the scenario you're experiencing: