Reflection, Type.GetProperties and performance

In C#, you can use reflection to get a list of properties and fields for a type – which can be very useful when comparing objects for instance, or creating automated tests. However, if you’re repeatedly using GetProperties or GetFields, you should probably cache the results, because the call if fairly slow.

Caching can be done using a dictionary, for instance.

Performance Comparison

Running GetProperties 233.900 took 1351 ms (5.7 µs/call), which in turn means that we can call GetProperties 173.131 times per second. That might sound really fast, but using a cached version where we use a Dictionary<,> to perform the caching, 233.900 calls took 17 ms (0,07 µs /call), meaning that we can call our cached method 13.758.823 times per second!

The cached method is 80 times faster. If you use complex binding flags, the differences in the results are bound to be even greater! But hold on, the case isn’t that clear cut. Read on!

When to Optimize

Should you optimize? Well, that depends, because in this case, optimizing through caching;

  • reduces the flexibility of the code
  • increase the complexity of the code
  • increases the memory footprint (marginally)

80 times faster seems like a big difference, but if you’re going to use PropertyInfo.GetValue to access the properties that were returned, this optimization will be dwarfed by the performance implications of GetValue – see my post about get value.

Each GetProperties takes 5.7 µs – that’s the non-optimized version. Plenty fast. Say that that call returns 10 properties and you then proceed to call GetValue for each of those, that’ll be 10 calls at 7.4 µs a pop (computed from my link above), or 74 µs. So even if you optimize GetProperties to 70 ns, the optimized version runs at 74 µs instead of about 80 µs. Not a big save for the added complexity.

If you’re calling GetProperties a small, intermediate or even large number of times, don’t use this optimization!

If you call it a huge number of times, millions or billions of times throughout the execution of your application, you could consider using caching. Or, perhaps, you should rethink your design choices completely…

The Source Code

You’ll find my benchmark code below.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            while (true)
            {
                const int iterations = 100;

                Console.WriteLine("Comparing Property Get Methods");
                long direct = TestGetPropertiesDirect(iterations);
                long cached = TestGetPropertiesCached(iterations);
                Console.WriteLine(
                    "Cached access is {0} times faster than reflection access",
                    1.0 * direct / cached);

                Console.WriteLine();
                Console.WriteLine();

                Console.WriteLine("Done, press r to go again!");
                if (Console.ReadKey().KeyChar != 'r')
                {
                    return;
                }
            }
        }

        private static long TestGetPropertiesDirect(int iterations)
        {
            Console.WriteLine("TestGetPropertiesDirect...");

            List<Type> types = GetTestTypes();
            Stopwatch sw = Stopwatch.StartNew();
            int gets = 0;
            for (int i = 0; i < iterations; i++)
            {
                foreach (Type type in types)
                {
                    List<PropertyInfo> properties = type.GetProperties().ToList();
                    gets++;
                }
            }

            Console.WriteLine(
                "  {0} iterations took {1} ms, {2} iterations/s",
                gets,
                sw.ElapsedMilliseconds,
                gets / (sw.ElapsedMilliseconds / 1000.0));

            return sw.ElapsedMilliseconds;
        }

        private static Dictionary<Type, List<PropertyInfo>> _propertyDictionary = 
            Dictionary<Type, List<PropertyInfo>>();

        private static long TestGetPropertiesCached(int iterations)
        {
            Console.WriteLine("TestGetPropertiesCached...");

            Stopwatch sw = Stopwatch.StartNew();
            List<Type> types = GetTestTypes();
            int gets=0;
            for (int i = 0; i < iterations; i++)
            {
                foreach (Type type in types)
                {
                    List<PropertyInfo> properties = GetCachedProperties(type);
                    gets++;
                }
            }

            Console.WriteLine(
                "  {0} gets took {1} ms, {2} iterations/s",
                gets,
                sw.ElapsedMilliseconds,
                gets / (sw.ElapsedMilliseconds / 1000.0));

            return sw.ElapsedMilliseconds;
        }

        private static List<PropertyInfo> GetCachedProperties(Type type)
        {
            List<PropertyInfo> properties;
            if (_propertyDictionary.TryGetValue(type, out properties) == false)
            {
                properties = type.GetProperties().ToList();
                _propertyDictionary.Add(type, properties);
            }

            return properties;
        }

        // Returns a list of types to use for the subsequent tests
        private static List<Type> GetTestTypes()
        {
            return
                typeof(string)
                    .Assembly
                    .GetTypes()
                    .ToList();
        }
    }
}

Setting data through reflection; SetValue

In C#/.NET, you can set data from an object using reflection which can be useful for things like mapping and testing. Typically, you’d use direct access, but reflection allows you to set data to fields and properties that you were un-aware of at compile time. This can save you a lot of code. But what are the performance implications?

Turns out that setting data directly from a property or field is 60-150 times faster than using the “SetValue” method.

Also note that the “Reflection Field Accessor” is way faster (6.3 times) than the “Reflection Property Accessor”! So accessing fields through reflection is much faster than accessing properties through reflection, but both are much slower than direct access.

Test type Time Iterations/s Difference
Direct Property Accessor 34 ms 147.058.823 154 times faster than reflection
Reflection Property Accessor 5250 ms 952.380  
Direct Field Accessor 19 ms 263.157.894 59 times faster than reflection
Reflection Field Accessor 1243 ms 4.022.526  

Comparing Property Setter Methods
Direct Property Accessor…
  5000000 iterations took 34 ms, 147058823,529412 iterations/s
Reflection Property Accessor…
  5000000 iterations took 5250 ms, 952380,952380952 iterations/s
Direct access is 154 times faster than reflection access

Comparing Field Setter Methods
Direct Field Accessor…
  5000000 iterations took 19 ms, 263157894,736842 iterations/s
Reflection Field Accessor…
  5000000 iterations took 1243 ms, 4022526,14641995 iterations/s
Direct access is 65 times faster than reflection access
Done, press r to go again!

Here’s the actual code for performing these tests;

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            while (true)
            {
                const int iterations = 5000000;

                Console.WriteLine("Comparing Property Setter Methods");
                long direct = TestDirectPropertyAccess(iterations);
                long reflection = TestReflectionPropertyAccess(iterations);
                Console.WriteLine(
                    "Direct access is {0} times faster than reflection access",
                    reflection / direct);

                Console.WriteLine();
                Console.WriteLine();

                Console.WriteLine("Comparing Field Setter Methods");
                direct = TestDirectFieldAccess(iterations);
                reflection = TestReflectionFieldAccess(iterations);
                Console.WriteLine(
                    "Direct access is {0} times faster than reflection access",
                    reflection / direct);

                Console.WriteLine("Done, press r to go again!");
                if (Console.ReadKey().KeyChar != 'r')
                {
                    return;
                }
            }
        }

        private static long TestDirectPropertyAccess(int iterations)
        {
            ClassWithProperty cwp = new ClassWithProperty();
            Console.WriteLine("Direct Property Accessor...");

            Stopwatch sw = Stopwatch.StartNew();
            List<string> list = new List<string>();
            for (int i = 0; i < iterations; i++)
            {
                cwp.MyProperty = list;
            }

            Console.WriteLine(
                "  {0} iterations took {1} ms, {2} iterations/s",
                iterations,
                sw.ElapsedMilliseconds,
                iterations / (sw.ElapsedMilliseconds / 1000.0));

            return sw.ElapsedMilliseconds;
        }

        private static long TestReflectionPropertyAccess(int iterations)
        {
            ClassWithProperty cwp = new ClassWithProperty();
            PropertyInfo propertyInfo =
                typeof(ClassWithProperty)
                    .GetProperties()
                    .Single(prop => prop.Name == "MyProperty");

            object[] emptyArray = new object[0];

            Console.WriteLine("Reflection Property Accessor...");
            Stopwatch sw = Stopwatch.StartNew();
            List<string> list = new List<string>();
            for (int i = 0; i < iterations; i++)
            {
                propertyInfo.SetValue(cwp, list, emptyArray);
            }

            Console.WriteLine(
                "  {0} iterations took {1} ms, {2} iterations/s",
                iterations,
                sw.ElapsedMilliseconds,
                iterations / (sw.ElapsedMilliseconds / 1000.0));

            return sw.ElapsedMilliseconds;
        }

        private static long TestDirectFieldAccess(int iterations)
        {
            ClassWithProperty cwp = new ClassWithProperty();
            Console.WriteLine("Direct Field Accessor...");

            Stopwatch sw = Stopwatch.StartNew();
            List<string> list = new List<string>();
            for (int i = 0; i < iterations; i++)
            {
                cwp.MyField = list;
            }

            Console.WriteLine(
                "  {0} iterations took {1} ms, {2} iterations/s",
                iterations,
                sw.ElapsedMilliseconds,
                iterations / (sw.ElapsedMilliseconds / 1000.0));

            return sw.ElapsedMilliseconds;
        }

        private static long TestReflectionFieldAccess(int iterations)
        {
            ClassWithProperty cwp = new ClassWithProperty();
            FieldInfo fieldInfo =
                typeof(ClassWithProperty)
                    .GetFields()
                    .Single(prop => prop.Name == "MyField");

            object[] emptyArray = new object[0];

            Console.WriteLine("Reflection Field Accessor...");

            Stopwatch sw = Stopwatch.StartNew();
            List<string> list = new List<string>();
            for (int i = 0; i < iterations; i++)
            {
                fieldInfo.SetValue(cwp, list);
            }

            Console.WriteLine(
                "  {0} iterations took {1} ms, {2} iterations/s",
                iterations,
                sw.ElapsedMilliseconds,
                iterations / (sw.ElapsedMilliseconds / 1000.0));

            return sw.ElapsedMilliseconds;
        }
    }

    public class ClassWithProperty
    {
        public ClassWithProperty()
        {
            MyProperty = new List<string>();
            MyField = new List<string>();
        }

        // I know, totally wrong naming and everything...
        public List<string> MyField;
        public List<string> MyProperty { get; set; }
    }
}

Getting data through reflection; GetValue

In C#/.NET, you can get data from an object using reflection which can be useful for things like mapping and testing. Typically, you’d use direct access, but reflection allows you to get data from fields and properties that you were un-aware of at compile time. This can save you a lot of code. But what are the performance implications?

Turns out that getting data directly from a property or field is 50-80 times faster than using the “GetValue” method. The code to create and access the delegate looks like this;

            ClassWithProperty cwp = new ClassWithProperty();

            PropertyInfo propertyInfo =
                typeof(ClassWithProperty)
                    .GetProperties()
                    .Single(prop => prop.Name == "MyProperty");

            MethodInfo method = propertyInfo.GetAccessors().First();

            Func<ClassWithProperty, List<string>> dlg =
                (Func<ClassWithProperty, List<string>>)
                Delegate.CreateDelegate(typeof(Func<ClassWithProperty, List<string>>), method);

            List<string> list = dlg(cwp);

For properties, you can also create a delegate that calls the setter method directly, which is plenty fast. This method can’t be used for fields though.

Note that the “Reflection Field Accessor” is way faster (6.3 times) than the “Reflection Property Accessor”! So accessing fields through reflection is much faster than accessing properties through reflection, but both are much slower than direct access.

Test type Time Iterations/s Time/iter Difference
Direct Property Accessor 36 ms 138.888.888 7,2 ns Baseline
Delegate Property Accessor 42 ms 119.047.619 8,4 ns 1.16 times slower
Reflection Property Accessor 6098 ms 819.940 1219 ns 107 times slower
Direct Field Accessor 15 ms 333.333.333 3 ns Baseline
Reflection Field Accessor 953 ms 5.246.589 190 ns 59 times slower

 

Here’s the actual code for performing these tests;

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            while (true)
            {
                const int iterations = 5000000;

                Console.WriteLine("Comparing Property Get Methods");
                long direct = TestDirectPropertyAccess(iterations);
                long delegatea = TestDelegatePropertyAccess(iterations);
                long reflection = TestReflectionPropertyAccess(iterations);
                Console.WriteLine(
                    "Direct access is {0} times faster than reflection access",
                    1.0 * reflection / direct);
                Console.WriteLine(
                    "Direct access is {0} times faster than delegate access",
                    1.0 * delegatea / direct);

                Console.WriteLine();
                Console.WriteLine();

                Console.WriteLine("Comparing Field Access Methods");
                direct = TestDirectFieldAccess(iterations);
                reflection = TestReflectionFieldAccess(iterations);
                Console.WriteLine(
                    "Direct access is {0} times faster than reflection access",
                    reflection / direct);

                Console.WriteLine("Done, press r to go again!");
                if (Console.ReadKey().KeyChar != 'r')
                {
                    return;
                }
            }
        }

        private static long TestDirectPropertyAccess(int iterations)
        {
            ClassWithProperty cwp = new ClassWithProperty();
            Console.WriteLine("Direct Property Accessor...");

            Stopwatch sw = Stopwatch.StartNew();
            for (int i = 0; i < iterations; i++)
            {
                List<string> list = cwp.MyProperty;
            }

            Console.WriteLine(
                "  {0} iterations took {1} ms, {2} iterations/s",
                iterations,
                sw.ElapsedMilliseconds,
                iterations / (sw.ElapsedMilliseconds / 1000.0));

            return sw.ElapsedMilliseconds;
        }

        private static long TestDelegatePropertyAccess(int iterations)
        {
            ClassWithProperty cwp = new ClassWithProperty();

            PropertyInfo propertyInfo =
                typeof(ClassWithProperty)
                    .GetProperties()
                    .Single(prop => prop.Name == "MyProperty");

            MethodInfo method = propertyInfo.GetAccessors().First();

            Func<ClassWithProperty, List<string>> dlg =
                (Func<ClassWithProperty, List<string>>)
                Delegate.CreateDelegate(typeof(Func<ClassWithProperty, List<string>>), method);


            Console.WriteLine("Reflection Property Accessor...");
            Stopwatch sw = Stopwatch.StartNew();
            for (int i = 0; i < iterations; i++)
            {
                List<string> list = dlg(cwp);
            }

            Console.WriteLine(
                "  {0} iterations took {1} ms, {2} iterations/s",
                iterations,
                sw.ElapsedMilliseconds,
                iterations / (sw.ElapsedMilliseconds / 1000.0));

            return sw.ElapsedMilliseconds;
        }

        private static long TestReflectionPropertyAccess(int iterations)
        {
            ClassWithProperty cwp = new ClassWithProperty();
            PropertyInfo propertyInfo =
                typeof(ClassWithProperty)
                    .GetProperties()
                    .Single(prop => prop.Name == "MyProperty");

            object[] emptyArray = new object[0];

            Console.WriteLine("Reflection Property Accessor...");

            Stopwatch sw = Stopwatch.StartNew();
            for (int i = 0; i < iterations; i++)
            {
                List<string> list = (List<string>)propertyInfo.GetValue(cwp, emptyArray);
            }

            Console.WriteLine(
                "  {0} iterations took {1} ms, {2} iterations/s",
                iterations,
                sw.ElapsedMilliseconds,
                iterations / (sw.ElapsedMilliseconds / 1000.0));

            return sw.ElapsedMilliseconds;
        }

        private static long TestDirectFieldAccess(int iterations)
        {
            ClassWithProperty cwp = new ClassWithProperty();
            Console.WriteLine("Direct Field Accessor...");

            Stopwatch sw = Stopwatch.StartNew();
            for (int i = 0; i < iterations; i++)
            {
                List<string> list = cwp.MyField;
            }

            Console.WriteLine(
                "  {0} iterations took {1} ms, {2} iterations/s",
                iterations,
                sw.ElapsedMilliseconds,
                iterations / (sw.ElapsedMilliseconds / 1000.0));

            return sw.ElapsedMilliseconds;
        }

        private static long TestReflectionFieldAccess(int iterations)
        {
            ClassWithProperty cwp = new ClassWithProperty();
            FieldInfo fieldInfo =
                typeof(ClassWithProperty)
                    .GetFields()
                    .Single(prop => prop.Name == "MyField");

            Console.WriteLine("Reflection Field Accessor...");

            Stopwatch sw = Stopwatch.StartNew();
            for (int i = 0; i < iterations; i++)
            {
                List<string> list = (List<string>)fieldInfo.GetValue(cwp);
            }

            Console.WriteLine(
                "  {0} iterations took {1} ms, {2} iterations/s",
                iterations,
                sw.ElapsedMilliseconds,
                iterations / (sw.ElapsedMilliseconds / 1000.0));

            return sw.ElapsedMilliseconds;
        }
    }

    public class ClassWithProperty
    {
        public ClassWithProperty()
        {
            MyProperty = new List<string>();
            MyField = new List<string>();
        }

        // I know, totally wrong naming and everything...
        public List<string> MyField;
        public List<string> MyProperty { get; set; }
    }
}

Sleep in T-SQL / SQL Server script

Sometimes you need to make a script sleep for a short period of time, either for debugging purposes, or to make sure that something else has time to finish. To do this in T-SQL, you can use this command, which took me a lot of googling to find;

WAITFOR DELAY '00:00:00:50' -- 50ms delay

Benchmarking SQL Server operations

Sometimes you’re stuck trying to optimize a large stored procedure and the first step in optimizations is to figure out where the time is being spent. It’s not always where you expect, and going at it haphazardly doesn’t cut it.

The answer is of course benchmarking (timing), to figure out where the time is spent, and to figure out if your “fix” improved performance or not.

First of all, declare the variables you’ll need. Do this at the top of the the procedure/script that you’re benchmarking;

declare
  @totalTime datetime=GetDate(),
  @partTime datetime=GetDate()

Then drop in scripts like these throughout the code you’re benchmarking;

print ' Timing: [insert_descroption_here]'
print ' * Total time='+convert(varchar(10), datediff(ms, @totalTime,GetDate()))
print ' * Last Part='+convert(varchar(10), datediff(ms, @partTime,GetDate()))
set @partTime=GetDate()

Once you’ve found a block that takes an inordinate amount of time, either try to figure out how to optimize it, or insert more benchmarking points to pinpoint the actual culprit.

Here’s an example;

declare
  @totalTime datetime=GetDate(),
  @partTime datetime=GetDate()

-- Simulates actual script
WAITFOR DELAY '00:00:00:50' -- 50ms delay

print ' Timing: [Initiated]'
print ' * Total time='+convert(varchar(10), datediff(ms, @totalTime,GetDate()))
print ' * Last Part='+convert(varchar(10), datediff(ms, @partTime,GetDate()))
set @partTime=GetDate()

-- Simulates actual script
WAITFOR DELAY '00:00:10:00' -- 10s delay

print ' Timing: [Collected base data]'
print ' * Total time='+convert(varchar(10), datediff(ms, @totalTime,GetDate()))
print ' * Last Part='+convert(varchar(10), datediff(ms, @partTime,GetDate()))
set @partTime=GetDate()

-- Simulates actual script
WAITFOR DELAY '00:00:00:50' -- 50ms delay

print ' Timing: [Compute sums]'
print ' * Total time='+convert(varchar(10), datediff(ms, @totalTime,GetDate()))
print ' * Last Part='+convert(varchar(10), datediff(ms, @partTime,GetDate()))
set @partTime=GetDate()

print 'Done!'

This script produces this output;

Timing: [Initiated]

* Total time=50

* Last Part=50

Timing: [Collected base data]

* Total time=10043

* Last Part=9993

Timing: [Compute sums]

* Total time=10096

* Last Part=53

Done!

As you’ll notice, it’s the part up to “Collected base data” that’s slow, so that’s where you should start either benchmarking further or actually optimizing!

IsAuthenticated in Silverlight, Revisited

In an earlier series of earlier posts, I talked about binding the IsEnabled property of links / buttons / controls to IsAuthenticated. I recently needed to revisit that code and found that I wanted to create the binding in code and I wanted to refactor the binding / authentication code so that I exists in its own class. Below you’ll find what I came up with.

Creating WPF bindings in code

Turns out it’s very simple to create a WPF binding in code, you need the property name (name path actually), the source object and Dependency property you wish to bind to;

        private bool _myBool;
        public bool MyBool
        {
            get
            {
                return MyBool;
            }
            set
            {
                _myBool = value;
                SendPropertyChanged("MyBool");
            }
        }

        public void SetupBinding()
        {
            Binding binding = new Binding("MyBool");
            binding.Source = this;
            storeImageButton.SetBinding(
                Control.IsEnabledProperty, 
                binding);
        }

 

AuthenticatedBinder

The class below will allow you to create bindings in code that bind to the IsAuthenticated property of the WebContext.Current.Authentication object. To create a binding, you can use code similar to this example;

        public EvolvedImage()
        {
            InitializeComponent();
            AuthenticatedBinder.BindIsEnabledToIsAuthenticated(
                storeImageButton);
        }

 

Here’s the source code;

using System.Windows.Controls;
using System.Windows.Data;
using ImageEvolvatron.Gui.ViewModels;

namespace ImageEvolvatron.Gui
{
    public class AuthenticatedBinder : ViewModelBase
    {
        private static readonly AuthenticatedBinder _instance = new AuthenticatedBinder();
        private static Binding _binding = CreateBinding();

        private AuthenticatedBinder()
        {
            WebContext.Current.Authentication.LoggedIn += 
                (sender, e) => SendPropertyChanged(() => IsAuthenticated);
            WebContext.Current.Authentication.LoggedOut += 
                (sender, e) => SendPropertyChanged(() => IsAuthenticated);
        }

        public static AuthenticatedBinder Instance { get { return _instance; } }

        public static Binding IsAuthenticatedBinding
        {
            get
            {
                return _binding;
            }
        }

        public bool IsAuthenticated
        {
            get
            {
                return WebContext.Current.User.IsAuthenticated;
            }
        }

        public static void BindIsEnabledToIsAuthenticated(Control control)
        {
            control.SetBinding(Control.IsEnabledProperty, IsAuthenticatedBinding);
        }

        private static Binding CreateBinding()
        {
            Binding binding = new Binding("IsAuthenticated");
            binding.Source = _instance;
            return binding;
        }
    }
}

 

Oh, and ViewModelBase looks like this;

using System;
using System.ComponentModel;
using System.Linq.Expressions;

namespace ImageEvolvatron.Gui.ViewModels
{
    public class ViewModelBase :
        INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        
        protected static string GetPropertyName<T>(Expression<Func<T>> action)
        {
            var expression = (MemberExpression)action.Body;
            return expression.Member.Name;
        }
        
        protected virtual void SendPropertyChanged<T>(Expression<Func<T>> action)
        {
            string propertyName = GetPropertyName(action);
            SendPropertyChanged(propertyName);
        }

        private void SendPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

Silverlight Applications Stop Building Properly

It’s happened to me too many times; all of a sudden, my Silverlight code stops working and no matter what I do, nothing changes. After changing tons of stuff, I realize that my breakpoints aren’t being hit…

Breakpoints not being hit The message reads;

The breakpoint will not currently be hit. No symbols have been loaded for this document.

That’s a major clue, a lot of things can cause this, but in my case, it used to work and now it doesn’t. There are absolutely no warnings, no hints or indications of what’s going on.

It turns out that my current code isn’t being used, Visual Studio is using some old version of my code! I can verify this by changing something that’s visual (a label for instance) and sure enough, the label isn’t changed the way it should! If I change the code so that there’s a bug in it, it won’t compile. I fix the bug, and it “compiles” but it doesn’t use my actual code. What to do?

The Solution

These steps solve the problem for me, some of the times…

Empty the debug/bin directory from your Silverlight application. In my case it’s called ImageEvolvatron\ImageEvolvatron.Gui\Bin\Debug and it contains dlls, pdbs, xaps etc. It would appear that these are cached for some reason, but deleting them solves that issue.

Create a bat file to solve this problem if it’s a frequent occurrence.

The Other Solution

These steps solve the problem for me, some of the times…

  1. Stop the Silverlight application
  2. Restart Visual Studio
  3. Stop all ASP.NET Development Servers if they’re running (there’s an icon in the task bar), right click and select “Stop”
     Stop web server
  4. Close all open tabs in firefox/whatever browser you’re using that might be connected to the old code. Stop the entire browser if you have to
  5. Right click on the solution in the solution explorer and selecting “Clean Solution”
  6. Right click again and selecting “Rebuild Solution”

Now it works – for me. Your mileages may vary. I think it’s safe to assume that some of those steps are superfluous, but which ones to skip – that I don’t know.