Linq to Serilog

Serilog is a logging framework that enables structured logging. More information on how it can be used and configured can be found here. Note that for the following snippets you need Serilog 1.3 or greater.

Take for example the following code snippet (taken from the sample) where we changed the configuration to write to a json as well.

using Serilog;
using Serilog.Enrichers;
using Serilog.Formatting.Json;
using Serilog.Sinks.IOFile;
using System;

namespace Sample
{
    class Program
    {
        static void Main()
        {
            Log.Logger = new LoggerConfiguration()
              .MinimumLevel.Debug()
              .WriteTo.File(@"log.txt")
              .WriteTo.Sink(new FileSink(@"log.json", new JsonFormatter(false, null, true), null))
              .WriteTo.Console()
              .Enrich.WithProperty("App", "Test Harness")
              .Enrich.With(new ThreadIdEnricher(),
                          new MachineNameEnricher())
              .CreateLogger();

            Log.Information("Just biting {Fruit} number {Count}", "Apple", 12);
            Log.ForContext<Program>().Information("Just biting {Fruit} number {Count:0000}", "Apple", 12);

            Log.Information("I've eaten {Dinner}", new[] { "potatoes", "peas" });

            Log.Information("I sat at {@Chair}", new { Back = "straight", Legs = new[] { 1, 2, 3, 4 } });
            Log.Information("I sat at {Chair}", new { Back = "straight", Legs = new[] { 1, 2, 3, 4 } });

            var context = Log.Logger.ForContext("MessageId", 567);
            try
            {
              context.Information("Processing a message");
              throw new NotImplementedException("Nothing doing.");
            }
            catch (Exception ex)
            {
              context.Error(ex, "Rolling back transaction!");
            }

            Console.ReadKey(true);
        }
    }
}

Below is the result of the generated text file (log.txt),

log.txt
2014-05-09 20:01:43.920 +02:00 [Information] Just biting "Apple" number 12
2014-05-09 20:01:43.937 +02:00 [Information] Just biting "Apple" number 0012
2014-05-09 20:01:43.938 +02:00 [Information] I've eaten ["potatoes", "peas"]
2014-05-09 20:01:43.951 +02:00 [Information] I sat at { Back: "straight", Legs: [1, 2, 3, 4] }
2014-05-09 20:01:43.954 +02:00 [Information] I sat at "{ Back = straight, Legs = System.Int32[] }"
2014-05-09 20:01:43.954 +02:00 [Information] Processing a message
2014-05-09 20:01:43.954 +02:00 [Error] Rolling back transaction!
System.NotImplementedException: Nothing doing.
   at SerilogSample.Program.Main() in c:SerilogSampleSerilogSampleProgram.cs:line 35

but it becomes more interesting if we look at the json file (log.json) that has been generated.

{"Timestamp":"2014-05-09T20:01:43.9200064+02:00","Level":"Information","MessageTemplate":"Just biting {Fruit} number {Count}","RenderedMessage":"Just biting "Apple" number 12","Properties":{"Fruit":"Apple","Count":12,"App":"Test Harness","ThreadId":1,"MachineName":"MyMachine"}}
{"Timestamp":"2014-05-09T20:01:43.9370156+02:00","Level":"Information","MessageTemplate":"Just biting {Fruit} number {Count:0000}","RenderedMessage":"Just biting "Apple" number 0012","Properties":{"Fruit":"Apple","Count":12,"SourceContext":"SerilogSample.Program","App":"Test Harness","ThreadId":1,"MachineName":"MyMachine"}}
{"Timestamp":"2014-05-09T20:01:43.9380179+02:00","Level":"Information","MessageTemplate":"I've eaten {Dinner}","RenderedMessage":"I've eaten ["potatoes", "peas"]","Properties":{"Dinner":["potatoes","peas"],"App":"Test Harness","ThreadId":1,"MachineName":"MyMachine"}}
{"Timestamp":"2014-05-09T20:01:43.9510191+02:00","Level":"Information","MessageTemplate":"I sat at {@Chair}","RenderedMessage":"I sat at { Back: "straight", Legs: [1, 2, 3, 4] }","Properties":{"Chair":{"Back":"straight","Legs":[1,2,3,4]},"App":"Test Harness","ThreadId":1,"MachineName":"MyMachine"}}
{"Timestamp":"2014-05-09T20:01:43.9540137+02:00","Level":"Information","MessageTemplate":"I sat at {Chair}","RenderedMessage":"I sat at "{ Back = straight, Legs = System.Int32[] }"","Properties":{"Chair":"{ Back = straight, Legs = System.Int32[] }","App":"Test Harness","ThreadId":1,"MachineName":"MyMachine"}}
{"Timestamp":"2014-05-09T20:01:43.9549787+02:00","Level":"Information","MessageTemplate":"Processing a message","RenderedMessage":"Processing a message","Properties":{"MessageId":567,"App":"Test Harness","ThreadId":1,"MachineName":"MyMachine"}}
{"Timestamp":"2014-05-09T20:01:43.9549787+02:00","Level":"Error","MessageTemplate":"Rolling back transaction!","RenderedMessage":"Rolling back transaction!","Exception":"System.NotImplementedException: Nothing doing.rn   at SerilogSample.Program.Main() in c:\SerilogSample\SerilogSample\Program.cs:line 35","Properties":{"MessageId":567,"App":"Test Harness","ThreadId":1,"MachineName":"MyMachine"}}

Each log event is persisted as a json object and separated by a newline. If we look more closely to one of the events you see that we have a Properties collection that contains the Enrich values and the values passed through the property values collection.

{
  "Timestamp": "2014-05-09T20:01:43.9510191+02:00",
  "Level": "Information",
  "MessageTemplate": "I sat at {@Chair}",
  "RenderedMessage": "I sat at { Back: "straight", Legs: [1, 2, 3, 4] }",
  "Properties": {
    "Chair": {
      "Back": "straight",
      "Legs": [
        1,
        2,
        3,
        4
      ]
    },
    "ThreadId": 1,
    "MachineName": "MyMachine"
  }
}

With Json.NET we can deserialize the log file so that we can use LINQ to query our log events. We have LogEventLevel and LogEvent that represent our log item.

public enum LogEventLevel
{
    Verbose,
    Debug,
    Information,
    Warning,
    Error,
    Fatal
}

public class LogEvent
{
    public DateTimeOffset Timestamp { get; set; }
    public LogEventLevel Level { get; set; }
    public string MessageTemplate { get; set; }
    public string RenderedMessage { get; set; }
    public string Exception { get; set; }
    public Dictionary<string, JToken> Properties { get; set; }
}

Note that the generated json file is not valid and that we need to separate each log event with a comma and make an array of it. Below you have a util class that will do all the plumbing and note that we are using FileStream so that it can also be read even when the file is in use.

public class SerilogUtils
{
    public static IEnumerable Load(string jsonPath)
    {
        var json = string.Empty;

        using (var fs = new FileStream(jsonPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
        using (var sr = new StreamReader(fs, Encoding.Default))
        {
            json = sr.ReadToEnd();
        }

        if (string.IsNullOrEmpty(json))
            return Enumerable.Empty();

        json = json.Replace(Environment.NewLine, ",");
        json = "[" + json + "]";

        var logEvents = JsonConvert.DeserializeObject<List>(json);
        return logEvents;
    }
}

LINQPad is a great tool to play with LINQ queries. Just make a reference to Json.NET and use the following using statements inside LINQPAD and after that you can do all sort of reports about you're log events.

  • Newtonsoft.Json
  • Newtonsoft.Json.Linq
  • System.Linq

SerilogLINQPad

Below are some query examples

logEvents = logEvents.Where(x => x.Level == LogEventLevel.Information);
logEvents = logEvents.Where(x => (int)x.Properties["ThreadId"] == 1);
logEvents = logEvents.Where(x => !string.IsNullOrEmpty(x.Exception));

var result = logEvents
    .Where(x => x.Properties.ContainsKey("MessageId"))
    .GroupBy(x => (int)x.Properties["MessageId"]);

In the next blog post we will go a little further and see how we can customize the layout and use json path expressions.

Building the source code of ASP.NET (Web API)

There is a lot of activity in the source code of ASP.NET on CodePlex. Because of some bug that has been fixed, I wanted to try out the latest build.

Simply download the latest source code from CodePlex (I used changed set 88372a0b4ab9). Like it is mentioned on CodePlex you have to obtain the NuGet Packages first and after that you can build.

build RestorePackages
build

To test some things out I created a simple console application (inside the same solution) and added the following references with the following code

  • System.Net.Http
  • System.Web.Http
  • System.Web.Http.SelfHost
static void Main(string[] args)
{
   var baseAddress = "http://localhost:7777/";
   var config = new HttpSelfHostConfiguration(baseAddress);
   config.Routes.MapHttpRoute(
      name: "DefaultApi",
      routeTemplate: "api/{controller}/{id}",
      defaults: new
      {
         id = RouteParameter.Optional
      });

   var server = new HttpSelfHostServer(config);
   server.OpenAsync().Wait();
   Console.WriteLine("The server is running...");
}

When you have installed ASP.NET MVC4 Beta you will get the following error

Could not load type 'System.Web.Http.RouteParameter' from assembly 'System.Web.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'.

Because the signed assembly has the same version it will always load the one that reside in the GAC. There are breaking changes in the new version and it clearly doesn't load the latest 'System.Web.Http' assembly within the solution! This can be seen and monitored by using Fuslogvw.

To resolve this problem we can use DEVPATH. It's an environment variable that you can set to a folder (typically you're output folder). The runtime will use the DEVPATH folder for probing before looking into the GAC! After setting the DEVPATH environment variable (I had to restart my Visual Studio to take effect) you have to add the following in the config file and after that everything worked fine.

<?xml version="1.0"?>
<configuration>
  <runtime>
    <developmentMode developerInstallation="true"/>
  </runtime>
</configuration>

Roslyn - Formatting Code

Roslyn CTP is the implementation of a 'compiler-as-a-service' for C# and VB.Net. Generally compilers are black boxes, the Roslyn project changes that by opening up APIs that you can use for code related tasks in your tools and applications.

In this post we are investigating source code formatting. Inside my projects I use as much as possible code generation and whether it is coming from T4 templates or some other mechanism, there is usually the need to format your code in a consistent way.

Inside Roslyn we can use the SyntaxTree class that resides in the Roslyn.Compilers.CSharp namespace to parse a text file (that contains C# of VB code). And after we can use the Format extension method that resides in the CompilationUnitSyntax class to reformat your code.

var code = File.ReadAllText("Sample.cs");
var tree = SyntaxTree.ParseCompilationUnit(code);
var root = (CompilationUnitSyntax)tree.Root;
var formattedCode = root.Format().GetFullText();

Take for example the code fragment below which is totally unformatted.

sample.cs
namespace Domain
{
using System;
using System
.Collections
.Generic;
using System.ComponentModel.DataAnnotations;

public class BankAccount
:Entity,
IValidatableObject
{
public BankAccountNumber BankAccountNumber {
get;
set; }

public string Iban
{
get
{
return string
.Format("ES{0} {1} {2} {0}{3}",
this.BankAccountNumber.CheckDigits,
this.BankAccountNumber
.NationalBankCode,
this.BankAccountNumber
.OfficeNumber,
this.BankAccountNumber
.AccountNumber);
}
set {

}
}

public decimal Balance { get;
private set; }

public virtual ICollection
BankAccountActivity
{
get
{
if (_bankAccountActivity
== null)
_bankAccountActivity =
new HashSet();

return _bankAccountActivity;
}
set
{
_bankAccountActivity =
new HashSet(value);
}
}
}
}

When passing the code fragment through the SyntaxTree and calling the Format extension method you get the following result which is formatted correctly.

namespace Domain
{
   using System;
   using System.Collections.Generic;
   using System.ComponentModel.DataAnnotations;

   public class BankAccount : Entity, IValidatableObject
   {
      public BankAccountNumber BankAccountNumber
      {   
         get;
         set;
      }

      public string Iban
      {
         get
         {
            return string.Format("ES{0} {1} {2} {0}{3}", this.BankAccountNumber.CheckDigits, this.BankAccountNumber.NationalBankCode, this.BankAccountNumber.OfficeNumber, this.BankAccountNumber.AccountNumber);
         }
         set
         {
         }
      }

      public decimal Balance
      {
         get;
         private set;
      }

      public virtual ICollection BankAccountActivity
      {
         get
         {
            if (_bankAccountActivity == null)
               _bankAccountActivity = new HashSet();
            return _bankAccountActivity;
         }
         set
         {
            _bankAccountActivity = new HashSet(value);
         }
     }   
   }
}

Note that when you are using for example lambda expressions or delegates that the formatter will add unnecessary newlines and whitespaces. Take for example the following code fragment after calling the Format method.

public void SomeMethod()
{
   this.Click += (s, e) =>
   {
      MessageBox.Show(((MouseEventArgs)e).Location.ToString());
   }
   ;
   treeView.AfterExpand += new TreeViewEventHandler(delegate (object o, TreeViewEventArgs t)
   {
      t.Node.ImageIndex = (int)FolderIconEnum.open;
      t.Node.SelectedImageIndex = (int)FolderIconEnum.open;
   }
);
}

Thankfully we have everything in control through the APIs and we can rewrite the expression (like the Format extension method is doing).

For that we need to create a class inheriting from SyntaxRewriter that resides in the Roslyn.Compilers.CSharp namespace and implements the Visitor pattern.

public class CodeBeautifier: SyntaxRewriter
{
   protected override SyntaxToken VisitToken(SyntaxToken token)
   {
      switch (token.Kind)
      {
         case SyntaxKind.SemicolonToken:

         if (token.GetPreviousToken().Kind == SyntaxKind.CloseBraceToken ||
            token.GetPreviousToken().Kind == SyntaxKind.CloseParenToken)
         {
            return token
            .WithLeadingTrivia()
            .WithTrailingTrivia(Syntax.ElasticCarriageReturnLineFeed);
         }

         break;

      case SyntaxKind.CloseBraceToken:

         if (token.GetNextToken().Kind == SyntaxKind.CloseParenToken ||
            token.GetNextToken().Kind == SyntaxKind.SemicolonToken)
         {
             return token
             .WithTrailingTrivia();
         }

      break;

      case SyntaxKind.CloseParenToken:

         if (token.GetPreviousToken().Kind == SyntaxKind.CloseBraceToken)
         {
            return token
            .WithLeadingTrivia();
         }

      break;
   }

    return token;
}

Note that I am visiting the syntax tokens, these are the terminals of the language grammar (representing the smallest syntactic fragments) and investigating the current kind of token with the next or previous kind of token. Syntax Trivia represents the parts such as whitespace, comments and preprocessor directives. Inside the VisitToken method I am replacing the syntax trivia parts.

To use the CodeBeautifier class you need to simply create an instance of it and using the Visit method to pass your node.

var code = File.ReadAllText("Sample.cs");
var tree = SyntaxTree.ParseCompilationUnit(code);
var root = (CompilationUnitSyntax)tree.Root;
var formattedCode = new CodeBeautifier().Visit(root.Format()).GetFullText();

After the syntax rewriting you will see that the code now looks like below

public void SomeMethod()
{
this.Click += (s, e) =>
{
MessageBox.Show(((MouseEventArgs)e).Location.ToString());
};
treeView.AfterExpand += new TreeViewEventHandler(delegate (object o, TreeViewEventArgs t)
{
t.Node.ImageIndex = (int)FolderIconEnum.open;
t.Node.SelectedImageIndex = (int)FolderIconEnum.open;
});
}

Windows Live Writer 2011 Tips

Windows Live Writer (WLW) is a great tool that enables you to edit and publish your blog posts. Below you find some tips about WLW.

Make WLW portable

I love portable applications, it enables you to store them on a USB drive or in the cloud without losing your settings and data. It turns out that it’s pretty easy to make WLW portable, and thankfully the investigation I did 3 years ago is still valid for version 2011!

The steps are very simple

  1. Copy all the contents of your installation folder (C:\Program Files (x86)\Windows LiveWriter) to your destination folder (e.g. USB stick)
  2. Create a folder called UserData in your destination folder (same level with the folders Dictionaries, Plugins, …)

You will see after launching WLW and configuring your blog site it will create all necessary files and folders for you settings and posts inside the UserData folder!

wlwuserdatafolder

Edit existing posts

With WLW it’s possible to edit existing posts (that originally were not posted with WLW) on your blog. It’s not directly clear from the user interface how to do that!

Click on the app menu in the ribbon bar en click on Open recent post, not the items that appear on the right!

wlwopenrecentpostmenu

After that you get a dialog where you can choose your blog site on the right. It retrieves all post items of your blog and you simply pick the one that you want to edit!

wlwrecentpostdialog

Use DropBox to sync your local drafts across computers

I use DropBox a lot, it’s a free hosting service that enables you to store and share files and folders across the internet. I use it to share my portable utilities and tools, and also WLW (after making it portable). This way I can always access my drafts and edit them when and where I want!

Introducing Neo4jConnect

I started with an open-source project called Neo4jConnect that enables you to connect to Neo4j, which is a graph-database written in Java and accessible through the REST api. On this page you can find the several operations exposed by Neo4j through REST. Neo4jConnect exposes an object api to manipulate the graph database.

More information and a quickstart can be found on the project site.

In the next couple of weeks I will add more documentation and features!

ArchiMate - Architecture modeling language and tool

ArchiMate is an open and independent Enterprise Architecture modeling language that supports the description, analysis and visualization of architecture within and across business domains. ArchiMate is one of the open standards hosted by The Open Group and is based on the IEEE 1471 standard.

The goals of ArchiMate are

  • To describe architectures and their relations
  • Communicate enterprise architectures with all stakeholders
  • Judge the impact of changes
  • Realise architecture by relating to existing standards, techniques and tools

Archi is a free open source java application to create ArchiMate models.

archi3_800

You can also find a quick reference card of Archimate here and you can also download Archimate stencils for Visio.

Create a .NET Windows Service in 5 steps with Topshelf

Topshelf is an open-source hosting framework for building Windows Services using .NET. With Topshelf you can create in a few lines of code your own windows service. It's a kind of internal DSL for building windows services. I used version 2.2 of Topshelf and the binaries and sources can be found here (GitHub).

First download Topshelf from GitHub, I used version 2.2 (direct link).

  1. Create a console application named SampleWindowsService inside Visual Studio - Be sure to change the target framework to .NET Framework 4
  2. Reference the binaries TopShelf.dll and log4net.dll (included in Topshelf).
  3. Create a simple service called 'SampleService' that simply write every 5 seconds to the log. Note that we create explicit a Start and Stop method which is conceptually the minimum that a windows service need.

    public class SampleService
    {
        private Timer _timer = null;
        readonly ILog _log = LogManager.GetLogger(
                                        typeof(SampleService));
    
        public SampleService()
        {
            double interval = 5000;
            _timer = new Timer(interval);
            _timer.Elapsed += new ElapsedEventHandler(OnTick);
        }
    
        protected virtual void OnTick(object sender, ElapsedEventArgs e)
        {
            _log.Debug("Tick:" + DateTime.Now.ToLongTimeString());
        }
    
        public void Start()
        {
            _log.Info("SampleService is Started");
    
            _timer.AutoReset = true;
            _timer.Enabled = true;
            _timer.Start();
        }
    
        public void Stop()
        {
            _log.Info("SampleService is Stopped");
    
            _timer.AutoReset = false;
            _timer.Enabled = false;
        }
    }
    
  4. In the main method of our console application we will use Topshelf to host our SampleService. We we are telling Topshelf how to start and stop the service, what the service name is, etc. Note that we need to configure log4net for Topshelf and our service!

    static void Main(string[] args)
    {
        XmlConfigurator.ConfigureAndWatch(
            new FileInfo(".\log4net.config"));
    
        var host = HostFactory.New(x =>
        {
            x.EnableDashboard();
            x.Service(s =>
            {
                s.SetServiceName("SampleService");
                s.ConstructUsing(name => new SampleService());
                s.WhenStarted(tc =>
                {
                    XmlConfigurator.ConfigureAndWatch(
                        new FileInfo(".\log4net.config"));
                    tc.Start();
                });
                s.WhenStopped(tc => tc.Stop());
            });
    
            x.RunAsLocalSystem();
            x.SetDescription("SampleService Description");
            x.SetDisplayName("SampleService");
            x.SetServiceName("SampleService");
        });
    
        host.Run();
    }
    
  5. The only thing we have to do now is to configure log4net. Create a file called log4net.config with the following configuration.

    log4net.config
    <?xml version="1.0" encoding="utf-8" ?>
    <log4net>
      <appender name="main" type="log4net.Appender.ConsoleAppender">
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%-5level - %message%newline" />
        </layout>
      </appender>
      <appender name="udp" type="log4net.Appender.UdpAppender">
        <RemoteAddress value="log4view-local"/>
        <RemotePort value="7071"/>
        <layout type="log4net.Layout.XmlLayoutSchemaLog4j"/>
      </appender>
      <root>
        <level value="DEBUG" />
        <appender-ref ref="main" />
        <appender-ref ref="udp" />
      </root>
    </log4net>
    

    This configuration enables to output to the console and through a UDP network protocol so that we can easily monitor the log statements when installed as a windows service. I used Log2Console (Codeplex) to monitor my log statements through UDP.

    Note

    Make sure the output directory of log4net.config is set to Copy always

    Note

    Note that there is an issue with log4net related to IPv6 and Windows Vista/7. You can fix it by adding the following 127.0.0.1 log4view-local to your hosts file which can be found in folder C:\Windows\System32\drivers\etc\hosts.

When you fit F5 you will see that Topshelf outputs some log statements and you will see that the log statements of our SampleService is included and everything is working properly.

TopshelfConsole

In order to install SampleService as a Windows Service you simply need to do the following through the command prompt.

Warning

Be sure to launch the command prompt as an administrator!

SampleWindowsService.exe install

After that when the windows service has been installed successfully we can start the service through services.msc or simply by typing

SampleWindowsService.exe start

Now we can open Log2Console to monitor our log files that is send through the UDP appender.

Log2Console To uninstall the service we simply write

SampleWindowsService.exe uninstall

The sources can be found here (BitBucket)

Code coverage with Visual Studio

Code coverage is used to determine how effectively your tests exercise the code in your application. This way you can identify sections of code that are covered, not covered or partially covered by your tests.

Visual Studio uses 2 different types of analysis, block-based statement coverage (C1 coverage) and line-based coverage.

  • Block-based statement coverage

    A block is defined as a sequence of instructions that have a single entry point and a single exit point. Exit points include branch instructions, a function call, a return instruction, or, for managed code, a throw instruction.

  • Line-based coverage

    For line-based coverage, the tools identify all of the blocks that make up a line and then use this information to determine the level of coverage for the line. If all of the blocks that make up the line are covered, then the tools report that the line is covered. If no blocks in the line are covered, then the tools report that the line is not covered. If some, but not all, of the blocks in the line are covered, then the tools report that the line is partially covered.

Take for example the following class that reside in MyProject.BusinessLogic assembly

public class Foo
{
   public int Calculate(int x, int y)
   {
      if (x > 0 && y < 0)
      {
         return -1;
      }
      else
      {
         return 1;
      }
   }
}

And a unit test that reside in MyProject.BusinessLogic.Test assembly

[TestClass]
public class FooTest
{
   [TestMethod]
   public void Calculate()
   {
      Foo foo = new Foo();
      Assert.AreEqual(1, foo.Calculate(3, 4));
   }
}

To enable code coverage you need to double-lick on the LocalTestRun.testrunconfig file that is located in the 'Solution Items' folder.

testrunconfig_2

Inside the 'Code Coverage' tab you select the assembly that you want to instrument. In this case we select MyProject.BusinessLogic.dll assembly.

codecoverage_1

Now you will need to run your unit tests again. Note that code coverage doesn't work when you debug your unit tests, so you will need to run your unit tests through the menu 'Test –> Run –> All Tests in Solution (CTRL+R, A)'. After that you can view a report about the code coverage results through the menu 'Test –> Windows –> Code Coverage Results'.

codecoverageresults_4

From the results we notice that we don't have 100% code coverage because our unit test only reached one part of the condition inside the Calculate method. If you open the Foo class and enable the code coloring you see the parts that are covered, not covered or partially covered.

CodeCoverageColoring_2

  • Light Blue: Indicates that the entire line of code was exercised in the test run.
  • Beige: Indicates that only a portion of the code blocks within the line of code were exercised in the test run.
  • Reddish Brown: Indicates that the line was not exercised in the test run.

Code coverage inside Visual Studio uses statement coverage and in this case the number of IL instructions reached is taken into account. If we add some statements in the Foo class and run again our code coverage we notice that the coverage has been raised form 71,43% to 92,59%. It's important to notice, that when you refactor your class it influences the code coverage even when the contract of the class is the same! This is very different from Branch coverage where each control structure is evaluated to true and false. In this case we would have 50% code coverage.

public class Foo
{
   public int Calculate(int x, int y)
   {
      if (x > 0 && y < 0)
      {
         return -1;
      }
      else
      {
         Console.WriteLine(x.ToString());
         Console.WriteLine(x.ToString());
         Console.WriteLine(x.ToString());
         Console.WriteLine(x.ToString());
         Console.WriteLine(x.ToString());
         Console.WriteLine(x.ToString());
         Console.WriteLine(x.ToString());
         Console.WriteLine(x.ToString());
         Console.WriteLine(x.ToString());
         Console.WriteLine(x.ToString());

         return 1;
      }
   }
}

Enumerating project items in a Visual Studio solution

Often you have the need to iterate through a collection and most of the time the iteration logic is weaved with the action that need to be done. This is because we are used to program in an imperative approach. In some scenarios it's better to use a functional approach and let other functions decide which action need to be applied. This way we can for example reuse our iteration logic.

Below is an iterator that starts from a solution or project and iterates through all project items inside the solution.

public class ProjectItemIterator : IEnumerable<EnvDTE.ProjectItem>
{
    IEnumerable<EnvDTE.Project> projects;

    public ProjectItemIterator(EnvDTE.Solution solution)
    {
        if (solution == null)
            throw new ArgumentNullException("solution");

        projects = solution.Projects.Cast<EnvDTE.Project>();
    }

    public ProjectItemIterator(IEnumerable<EnvDTE.Project> projects)
    {
        if (projects == null)
            throw new ArgumentNullException("projects");

        this.projects = projects;
    }

    public IEnumerator<EnvDTE.ProjectItem> GetEnumerator()
    {
        foreach (EnvDTE.Project currentProject in projects)
            foreach (var currentProjectItem in Enumerate(currentProject.ProjectItems))
                yield return currentProjectItem;
    }

    IEnumerable<EnvDTE.ProjectItem> Enumerate(EnvDTE.ProjectItems projectItems)
    {
        foreach (EnvDTE.ProjectItem item in projectItems)
        {
            yield return item;

            if (item.SubProject != null)
            {
                foreach (EnvDTE.ProjectItem childItem in Enumerate(item.SubProject.ProjectItems))
                    yield return childItem;
            }
            else
            {
                foreach (EnvDTE.ProjectItem childItem in Enumerate(item.ProjectItems))
                    yield return childItem;
            }
        }
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}