You have an app developed a long time ago and it’s showing its age. It’s time to modernize it, but rewrite isn’t an option: it’s too complicated to rewrite it and it’s still working fine, there is no budget for rewriting the app, there are other priorities, and so on.

If the main reason for not rewriting the app is that it’s working fine, and the only thing that is showing its age is the UI, you have no reason to not modernize it. Microsoft sent the developers a clear message that WPF, Winforms and Win32 are alive and well, open sourcing them and porting to .NET Core. And, the best thing is that you can use the newest features in the Operating System and integrate them to your app without the need to rewrite it. You can even use the new UI of UWP apps in your own app, by using the technology named XamlIslands, where you can embed your own controls in your old app.

To show how this is done, we’ll create a WPF app that shows images and modernize it with Xaml Islands. For this app, I’ve dowloaded 30 images from http://unsample.net/ . This service sends me a zip file with a maximum of 30 photos, downloaded from https://unsplash.com, with a Json file with the credits. Our apps will show the photos and the credits.

Initially, go to http://unsample.net/ and download a set of 30 photos. In Visual Studio, create a new WPF app and name it WPFXamlIslands. In the Solution Explorer, create a new folder named Photos in the project and add the photos and the Json file from the zip to it. Select all files in the folder and change the Build Action to None and the Copy to Output Directory to Copy if Newer.

As we will be manipulating Json files, right-click on the References node and select Manage NuGet Packages, then install the Newtonsoft.Json package. After that, install the MVVM Light package, as we will be using MVVM for this project. You will have to remove the Microsoft.Practices.ServiceLocation using clause in the ViewModelLocator class and add the CommonServiceLocator using clause in the same file to make it compile.

Then, in MainWindow.xaml file, add this code:

<Window x:Class="WPFXamlIslands.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:c="clr-namespace:WPFXamlIslands.Converters"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800"
        DataContext="{Binding Source={StaticResource Locator}, Path=Main}">
    <Window.Resources>
        <c:StringToImageConverter x:Key="StringToImageConverter" />
    </Window.Resources>
    <Grid>
        <ScrollViewer HorizontalScrollBarVisibility="Disabled">
            <ItemsControl ItemsSource="{Binding Photos}" >
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <WrapPanel />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Border BorderBrush="Black" Background="Beige"  BorderThickness="1" Margin="5">
                            <StackPanel Margin="5">
                                <Image Source="{Binding Key, Converter={StaticResource StringToImageConverter}}" 
                                       Width="150" Height="150" Stretch="Uniform" />
                                <TextBlock Text="{Binding Value.UserName}" MaxWidth="150" Margin="0,5" />
                            </StackPanel>
                        </Border>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </ScrollViewer>
    </Grid>
</Window>

We’ve added a ItemsControl with a datatemplate to show the images and the name of the author. The items are presented in a WrapGrid, so the items are wrapped and the number of items change depending on the window width. To present the images, I’ve created a converter to convert the name of the image to a bitmap that can be assigned to the image:

public class StringToImageConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        string imagePath = $"{AppDomain.CurrentDomain.BaseDirectory}Photos\\{value}.jpg";
        BitmapImage bitmapImage = !string.IsNullOrWhiteSpace(value?.ToString()) &&
            File.Exists(imagePath) ?
            new BitmapImage(new Uri(imagePath)) :
            null;
        return bitmapImage;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

It will take the name of the image and create a BitmapImage with it. That way, we can use the converter in the data binding for the list items. The MainViewModel will be like this:

public class MainViewModel : ViewModelBase
{
    /// <summary>
    /// Initializes a new instance of the MainViewModel class.
    /// </summary>
    public MainViewModel()
    {
        Photos = JsonConvert.DeserializeObject<Dictionary<string, PhotoData>>(
            File.ReadAllText("Photos\\__credits.json"),
            new JsonSerializerSettings
            {
                ContractResolver = new DefaultContractResolver
                {
                    NamingStrategy = new SnakeCaseNamingStrategy()
                }
            });
    }

    public Dictionary<string, PhotoData> Photos { get; private set; }

It will read the files, deserialize the Json file and assign the resulting Dictionary to the property Photos. This dictionary has the name of the file as the key and a class named PhotoData as the value. PhotoData is declared as:

public class PhotoData
{
    public string UserName { get; set; }
    public string UserUrl { get; set; }
    public string PhotoUrl { get; set; }
}

Now, when you run the application, it will show something like this:

The app runs fine, but it can be improved to add the new features and animations given by UWP, using the Xaml Islands.

The easiest way to use a UWP control in a WPF or Winforms app is to use the Windows Community Toolkit. This is a toolkit of components created by the community and Microsoft and can be found on https://github.com/windows-toolkit/WindowsCommunityToolkit.

To use a UWP control, you must use the WindowsXamlHost  control in the window. It can be found in the Microsoft.Toolkit.WPF.UI.XamlHost  NuGet package. Install it and add a WindowsXamlHost control in the main window:

<Grid>
    <xaml:WindowsXamlHost x:Name="UwpButton" 
                          InitialTypeName="Windows.UI.Xaml.Controls.Button"
                          ChildChanged="UwpButton_ChildChanged" />
</Grid>

In the code behind, you must add the code to initialize the button in the event handler for ChildChanged:

private void UwpButton_ChildChanged(object sender, EventArgs e)
{
    WindowsXamlHost windowsXamlHost = (WindowsXamlHost)sender;

    Windows.UI.Xaml.Controls.Button button =
        (Windows.UI.Xaml.Controls.Button)windowsXamlHost.Child;
    if (button == null)
        return;
    button.Width = 100;
    button.Height = 40;
    button.Content = "UWP button";
    button.Click += Button_Click;
}

private void Button_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
    MessageBox.Show("UWP button works");
}

The ChildChanged is called when the child in the XamlHost changes. There you must configure the control added as a child (with the use of the InitialTypeName property).

That should be everything, but when you see the code, you see that the Button is not defined. In the error window, there is a warning saying that that Windows.Foundation.UniversalApiContract is missing. My first try was to find a dll with this name, which couldn’t be found. Then I noticed that what was needed was not the dll, but a winmd file with the Windows Metadata for the controls. In fact, there is a Windows.Foundation.UniversalApiContract.winmd file located in C:\Program Files (x86)\Windows Kits\10\References\10.0.18362.0\Windows.Foundation.UniversalApiContract\8.0.0.0\ (the version in your system might change), and I added this file as a reference and the errors regarding the button disappeared.

Then I ran the project and got a Catatstrophic Failure (this one is nice – I was expecting my computer to melt down, but fortunately, that didn’t occur :-)). After some more research, I came to this article (yes, Microsoft also suffers with Catastrophic Failures :-)), and the answer was pretty simple: add an Application Manifest. In the Solution Explorer, add an Application Manifest and change it like this:

<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
  <application>
    <!-- A list of the Windows versions that this application has been tested on
         and is designed to work with. Uncomment the appropriate elements
         and Windows will automatically select the most compatible environment. -->

    <!-- Windows Vista -->
    <!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->

    <!-- Windows 7 -->
    <!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->

    <!-- Windows 8 -->
    <!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->

    <!-- Windows 8.1 -->
    <!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->

    <!-- Windows 10 -->
    <maxversiontested Id="10.0.18358.0"/>0
    <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />

  </application>
</compatibility>

That will set the MaxVersionTested and the error disappears. When you run the application, you will have something like this:

Now we can see that our program works with the UWP control, then let’s continue to modernize it. We will add a FlipView to show the images. For that, we must change the InitialTypeName of the WindowsXamlHost to show the FlipView:

<xaml:WindowsXamlHost x:Name="XamlHost" 
                      InitialTypeName="Windows.UI.Xaml.Controls.FlipView" 
                      ChildChanged="XamlHost_ChildChanged" />

The code for the ChildChanged event will configure the FlipView and its DataTemplate:

        private void XamlHost_ChildChanged(object sender, EventArgs e)
        {
            WindowsXamlHost windowsXamlHost = (WindowsXamlHost)sender;

            Windows.UI.Xaml.Controls.FlipView flipView =
                (Windows.UI.Xaml.Controls.FlipView)windowsXamlHost.Child;
            if (flipView == null)
                return;
            var dataTemplate = (Windows.UI.Xaml.DataTemplate)XamlReader.Load(@"
<DataTemplate xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"">
  <Grid Margin=""5"">
      <Grid.RowDefinitions>
         <RowDefinition Height=""*"" />
         <RowDefinition Height=""40"" />
      </Grid.RowDefinitions>
      <Image Source=""{Binding PhotoUrl}"" Grid.Row=""0"" Margin=""5""
            Stretch=""Uniform"" />
      <TextBlock Text=""{Binding UserName}"" HorizontalAlignment=""Center""
            VerticalAlignment=""Center"" Grid.Row=""1""/>
  </Grid>
</DataTemplate>");
            
            flipView.ItemTemplate = dataTemplate;
            flipView.ItemsSource = ((MainViewModel)DataContext).Photos;
        }

We create the DataTemplate as a string and load it with XamlReader.Read, then set the ItemsSource to the Photos property of the ViewModel. In order to use it in a UWP control, we modified the obtention of the Photos property:

public class MainViewModel : ViewModelBase
{
    /// <summary>
    /// Initializes a new instance of the MainViewModel class.
    /// </summary>
    public MainViewModel()
    {
        Photos = JsonConvert.DeserializeObject<Dictionary<string, PhotoData>>(
            File.ReadAllText("Photos\\__credits.json"),
            new JsonSerializerSettings
            {
                ContractResolver = new DefaultContractResolver
                {
                    NamingStrategy = new SnakeCaseNamingStrategy()
                }
            }).Select(p => new PhotoData() {PhotoUrl = $".\\Photos\\{p.Key}.jpg", UserName = p.Value.UserName});
    }

    public IEnumerable<PhotoData> Photos { get; private set; }

}

With these changes, you can now run the program and see the photos in a FlipView:

Conclusion

As you can see, you can modernize your desktop application with UWP controls, using Xaml Islands. The WindowsXamlHost eases this work a lot, but the work is still clumsy: you must add the winmd file, add the manifest to the project and manipulate the UWP control in code, using the Windows.UI.Xaml namespace. Adding a DataTemplate to the FlipView requires parsing Xaml code that comes from a string. Not a simple task, but still feasible. Hopefully, things will be easier with Project Reunion and WinUI 3.

All the source code for this article is available at https://github.com/bsonnino/WPFXamlIslands

After I finished last article, I started to think that there should be another way to test the non-virtual methods of a class. And, as a matter of fact, there is another way that has been around for a long time: Microsoft Fakes. If your don’t know it, you can read this article.

While I wouldn’t recommend it for using in your daily testing, it’s invaluable when you are testing legacy code and don’t have any testing and lots of coupling in your code. The usage is very simple:

In the Solution Explorer of the test project, right-click in the dll you want to create a fake (in our case, is the dll of the main project and select Add Fakes Assembly:

That will add the fake assembly and all the infrastructure needed to use fakes in your tests. Then, in your test class, you can use the newly created fake. Just create a ShimContext and use it while testing the class:

[TestMethod]
public void TestVirtualMethod()
{
    using (ShimsContext.Create())
    {
        var fakeClass = new Fakes.ShimClassNonVirtualMethods();
        var sut = new ClassToTest();
        sut.CallNonVirtualMethod(fakeClass);
    }
}

[TestMethod]
public void TestNonVirtualMethod()
{
    using (ShimsContext.Create())
    {
        var fakeClass = new Fakes.ShimClassNonVirtualMethods();
        var sut = new ClassToTest();
        sut.CallNonVirtualMethod(fakeClass);
    }
}

As you can see, we initialize a ShimsContext and enclose it in an Using clause. Then we initialize the fake class. This class will be in the same namespace as the original one, with .Fakes at the end, and its name will be the same, starting with Shim. That way, we can use it as a fake for the original class and it will have all non-virtual methods overridden.

That feature, although not recommended in a daily basis, can be a lifesaver when you have to test legacy code with deep coupling with databases, UI, or even other libraries. Microsoft Fakes can fake almost everything, including System calls, so it can be a nice starting point to refactor your legacy code – with it, you can put some tests in place and star refactoring your code more confidently.

 

Introduction

Usually I use interfaces for my unit tests. This is a good practice, and should be followed when possible. But, last week I was testing some legacy code, which had no interfaces and stumbled with something: I was trying to mock a class with a non-virtual method and saw that the original method was being executed, instead of doing nothing (the expected for the mock). I was using NSubstitute, but the same principles apply to most of the more popular mocking frameworks, like Moq, FakeItEasy or Rhino mocks. All of them use for their backend Castle DynamicProxy to create a proxy around the object and do its magic. All of them state that they can only mock virtual methods, so I should be aware that I could not mock non-virtual methods.

This would be ok if the class to be mocked is yours, just add the Virtual keyword to your method and you’re done. You’ll have some size and performance penalty in exchange for your flexibility. Or you could use something like Typemock Isolator (this is a great mocking framework that can mock almost anything)- that would work even if you don’t have access to the source code. But why aren’t non-virtual methods mocked ?

Let’s start with an example. Let’s say I have the code of the class below:

public class ClassNonVirtualMehods
{
    public void NonVirtualMethod()
    {
         Console.WriteLine("Executing Non Virtual Method");
    }

    public virtual void VirtualMethod()
    {
        Console.WriteLine("Executing Virtual Method");
    }
}

My class to test receives an instance of this class:

public class ClassToTest
{
    public void CallVirtualMethod(ClassNonVirtualMehods param)
    {
        param.VirtualMethod(); 
    }

    public void CallNonVirtualMethod(ClassNonVirtualMehods param)
    {
        param.NonVirtualMethod();
    }
}

My tests are (I’m using NSubstitute here):

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void TestVirtualMethod()
    {
        var mock = Substitute.For<ClassNonVirtualMehods>();
        var sut = new ClassToTest();
        sut.CallVirtualMethod(mock);
    }

    [TestMethod]
    public void TestNonVirtualMethod()
    {
        var mock = Substitute.For<ClassNonVirtualMehods>();
        var sut = new ClassToTest();
        sut.CallNonVirtualMethod(mock);
    }
}

If you run these tests, you’ll see this:

You’ll see that the non virtual method is executed (writing to the console), while the virtual method is not executed, as we expected.

Why aren’t non-virtual methods mocked ?

To explain that, we should see how Castle DynamicProxy works and how C# polymorphism works.

From here, you can see that DynamicProxy works with two types of proxies: Inheritance-based and composition-based. In order to put in place their mocks, the mock frameworks use the inheritance-based proxy, and that would be the same as create an inherited class (the mock) that overrides the methods and put the desired behavior in them. It would be something like this:

public class MockClassWithNonVirtualMethods : ClassNonVirtualMehods
{
    public override void VirtualMethod()
    {
        // Don't do anything
    }
}

In this case, as you can see, you can only override virtual methods, so the non-virtual methods will continue to execute the same way. If you have tests like this ones, you will see the same behavior as you did with NSubstitute:

You could use the composition-based proxy, but when you read the documentation, you’ll see that this is not an option:

Class proxy with target – this proxy kind targets classes. It is not a perfect proxy and if the class has non-virtual methods or public fields these can’t be intercepted giving users of the proxy inconsistent view of the state of the object. Because of that, it should be used with caution.

Workaround for non-virtual methods

Now we know why mocking frameworks don’t mock non-virtual methods, we must find a workaround.

My first option was to use the RealProxy class to create the mock and intercept the calls, like shown in my article in the MSDN Magazine. This is a nice and elegant solution, as the impact to the source code would be minimum, just create a generic class to create the proxy and use it instead of the real class.  But it turned on that the RealProxy has two disadvantages:

  • It can only create proxies that inherit of MarshalByRefObject or are interfaces, and our classes are none of them
  • It isn’t available in .NET Core, and it was replaced by DispatchProxy, which is different, and can only proxy interfaces

So, I discarded this option and went for another option. I was looking to change the IL Code at runtime, using Reflection.Emit, but this is prone to errors and not an easy task, so I kept searching until I found Fody. This is a very interesting framework to allow you to add new code at runtime. This is the same thing I was expecting to do, with the difference that the solutions are ready and tested. Just add an add-in and you’re set – the task you are trying to do is there. There are a lot of add-ins for it and it’s very simple to use them. If you don’t find what you are trying to do there, you can develop your own add-in. In our case, there is already an add-in, Virtuosity.

To use it, all we have to do is to install the packages Fody and Fody.Virtuosity to your project, add a xml file named FodyWeavers.xml with this content:

<?xml version="1.0" encoding="utf-8"?>
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
  <Virtuosity />
</Weavers>

Set its Build Action to None and Copy to Output Directory to Copy If Newer and you’re set. This will transform all your non-virtual methods to virtual and you will be able to mock them in any mocking framework, with no need to change anything else in your projects.

Conclusion

The procedure described here is nothing something that I would recommend doing on a daily basis, but when you have some legacy code to test, where changes to the source code are difficult to make, it can be a lifesaver. When you have the time and the means, you should develop an interface and use it in your code, using Dependency Injection. That way, you will reduce coupling and enhance its maintainability.

 

All the source code for this article is at https://github.com/bsonnino/MockNonVirtual

Introduction

Some time ago, I wrote this post  with this accompanying source code. It showed the way to use NTFS file structures to enumerate files and show the files that take more space in your hard disk. Then a user posted a note asking if it would be possible to know if, instead of knowing the files, I could show the directories that consume more space, and I said “of course, you can do it with some minor changes!”, and showed him a sample of the code.

Then I realized that that could be an interesting way to improve the program and show a little bit about working with the data we have – gather the data is important, but work with it to extract every bit of information is even more! So, let’s go to it (if you haven’t already read the original article, do it now).

Working with the data

The original code to retrieve all the files size using the NTFS file structures is:

var ntfsReader =
    new NtfsReader(driveToAnalyze, RetrieveMode.All);
nodes =
    ntfsReader.GetNodes(driveToAnalyze.Name)
        .Where(n => (n.Attributes &
                     (Attributes.Hidden | Attributes.System |
                      Attributes.Temporary | Attributes.Device |
                      Attributes.Directory | Attributes.Offline |
                      Attributes.ReparsePoint | Attributes.SparseFile)) == 0)
        .OrderByDescending(n => n.Size).ToList();

The reader will get all nodes from the selected drive, will filter them, removing the hidden, system, temporary and directories and will sort it by descending order. The ordering will not be necessary for our needs, because we want the directories sizes.

Before working with the data, let’s create a new WPF project that will show our data in a list. In Visual Studio (you should open Visual Studio in elevated mode, because to use the NTFS structures you must have admin rights), create a new WPF project (.NET Framework) and add the dll NTFSReader from this site as a reference to the project. Then, add this code in MainWindow.xaml for our UI:

<Window x:Class="NtfsDirectories.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="40"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="30"/>
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal" Margin="5">
            <TextBlock Text="Drive" VerticalAlignment="Center"/>
            <ComboBox x:Name="DrvCombo" Margin="5,0" Width="100" 
                      VerticalContentAlignment="Center"/>
        </StackPanel>
        <StackPanel Grid.Row="0" HorizontalAlignment="Right" Orientation="Horizontal" Margin="5">
            <TextBlock Text="Level" VerticalAlignment="Center"/>
            <ComboBox x:Name="LevelCombo" Margin="5,0" Width="100" 
                      VerticalContentAlignment="Center" SelectedIndex="3" 
                      SelectionChanged="LevelCombo_OnSelectionChanged">
                <ComboBoxItem>1</ComboBoxItem>
                <ComboBoxItem>2</ComboBoxItem>
                <ComboBoxItem>3</ComboBoxItem>
                <ComboBoxItem>Max</ComboBoxItem>
            </ComboBox>
        </StackPanel>
        <ListBox x:Name="DirectoriesList" Grid.Row="1" 
                 VirtualizingPanel.IsVirtualizing="True"
                 VirtualizingPanel.IsVirtualizingWhenGrouping="True" >
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding Name}" 
                                   Margin="5,0" Width="450"/>
                        <TextBlock Text="{Binding Size,StringFormat=N0}" 
                                   Margin="5,0" Width="150" TextAlignment="Right"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <TextBlock x:Name="StatusTxt" Grid.Row="2" HorizontalAlignment="Center" Margin="5"/>
    </Grid>
</Window>

That will add two comboboxes, to select the drive and level to drill down and a listbox to show the results. Then, we should add the code to get the drives in the constructor of MainWindow:

public MainWindow()
{
    InitializeComponent();
    var ntfsDrives = DriveInfo.GetDrives()
        .Where(d => d.DriveFormat == "NTFS").ToList();
    DrvCombo.ItemsSource = ntfsDrives;
    DrvCombo.SelectionChanged += DrvCombo_SelectionChanged;
}

This code will fill the drives combo with all the NTFS drives in the system. When the user changes the selection, the drive will be scanned and will get all files:

private IEnumerable<INode> nodes = null;
private async void DrvCombo_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (DrvCombo.SelectedItem == null) return;
    var driveToAnalyze = (DriveInfo)DrvCombo.SelectedItem;
    DrvCombo.IsEnabled = false;
    LevelCombo.IsEnabled = false;
    StatusTxt.Text = "Analyzing drive";
    
    await Task.Factory.StartNew(() => { nodes = GetFileNodes(driveToAnalyze); });

    LevelCombo.SelectedIndex = -1;
    LevelCombo.SelectedIndex = 3;

    DrvCombo.IsEnabled = true;
    LevelCombo.IsEnabled = true;
    StatusTxt.Text = "";
}

You will notice the fact that I’m setting the LevelCombo’s SelectedIndex to -1 then to 3.I do that to achieve two things: clean the directories list when the drive changes and trigger the change event for the level combo. The GetFileNodes method will scan the drive and will return a list of files in that drive:

private static IEnumerable<INode> GetFileNodes(DriveInfo driveToAnalyze)
{
    var ntfsReader =
        new NtfsReader(driveToAnalyze, RetrieveMode.All);
    return
        ntfsReader.GetNodes(driveToAnalyze.Name)
            .Where(n => (n.Attributes &
                         (Attributes.Hidden | Attributes.System |
                          Attributes.Temporary | Attributes.Device |
                          Attributes.Directory | Attributes.Offline |
                          Attributes.ReparsePoint | Attributes.SparseFile)) == 0);
}

Now, we must get the directories sizes depending on the level the user selected and add them to the listbox. This is done when the level changes in the combobox:

private void LevelCombo_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (DirectoriesList == null)
        return;
    int level = Int32.MaxValue-1;
    if (LevelCombo.SelectedIndex < 0 || nodes == null)
        DirectoriesList.ItemsSource = null;
    else if (LevelCombo.SelectedIndex < 3)
        level = LevelCombo.SelectedIndex;
    DirectoriesList.ItemsSource = GetDirectorySizes(nodes, level+1);
}

As you can see, when the level is negative, I clean the results list, and when there is a level set, I call the GetDirectoriesSizes method. If the user selects the Max level, I set the level to Int32.MaxValue. The GetDirectoriesSizes method is:

private IList<DirectorySize> GetDirectorySizes(IEnumerable<INode> nodes, int level)
{
     var directories = nodes
        .Select(n => new
        {
            Path = string.Join(Path.DirectorySeparatorChar.ToString(),
                Path.GetDirectoryName(n.FullName)
                    .Split(Path.DirectorySeparatorChar)
                    .Take(level)),
            Node = n
        })
        .GroupBy(g => g.Path)
        .Select(g => new DirectorySize(g.Key, g.Sum(d => (Int64)d.Node.Size)))
        .OrderByDescending(d => d.Size)
        .ToList();
      return directories;
}

In this case, I use LINQ to group the files in the desired level. The first Select will break the path into its parts, then will take the parts only to the level we want and rejoin the parts. This will allow us to group the files according to the wanted level. One gotcha is that, when we select level 3, for example, we will want to show all files grouped to the level 3, but we will also want those that are on the upper levels. For example, for the level 3, we will want to show the files in C:\, C:\Temp, C:\Program Files\Windows and so on. If the file is under a path more than 3 levels down, it will be grouped with the upper directory.

When you run this project, you will have something like this:

Conclusion

As you can see, once we have the data, we can work on it and obtain different views. One thing that helps a lot in C# is the use of LINQ, which allows to transform the data in a simple way. The function that we used can be extended to any level you want and will bring the good results.

All the source code for this project is in https://github.com/bsonnino/NtfsDirectories

A very common issue when dealing with console applications is to parse the command line. I am a huge fan of command line applications, especially when I want to add an operation to my DevOps pipeline that is not provided by the tool I’m using, Besides that, there are several applications that don’t need user interaction or any special UI, so I end up creating a lot of console applications. But one problem that always arises is parsing the command line. Adding switches and help to the usage is always a pain and many times I resort to third party utils (CommandLineUtils is one of them, for example).

But this make me resort to non-standard utilities and, many times, I use different libraries, in a way that it poses me a maintenance issue: I need to remember the way that the library is used in order to make changes in the app. Not anymore. It seems that Microsoft has seen this as a problem and has designed its own library: System.CommandLine.

Using System.CommandLine

The first step to use this library is to create a console application. Open Visual Studio and create a new console application. The default application is a very simple one that prints Hello World on the screen.

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Hello World!");
    }
}

If you want to process the command line arguments, you could do something like this:

static void Main(string[] args)
{
    Console.WriteLine($"Hello {(args.Length > 0 ? args[0] : "World")}!");
}

This program will take the first argument (if it exists) and print a Hello to it:

This is ok, but I’m almost sure that’s not what you want.

If you want to add a switch, you will have  to parse the arguments, check which ones start with a slash or an hyphen, check the following parameter (for something like “–file Myfile.txt”). Not an easy task, especially if there are a lot of switches available.

Then, there is System.CommandLine that comes to the rescue. You must add the System.CommandLine NuGet package to the project. Then, you can add a command handler to add your options and process them, like this:

static async Task Main(string[] args)
{
    var command = new RootCommand
    {
        new Option(new [] {"--verbose", "-v"}),
        new Option("--numbers") { Argument = new Argument<int[]>() }
    };

    command.Handler = CommandHandler.Create(
        (bool verbose, int[] numbers) =>
        {
            if (numbers != null)
            {
                if (verbose)
                    Console.WriteLine($"Adding {string.Join(' ', numbers)}");

                Console.WriteLine(numbers.Sum());
            }
            else
            {
                if (verbose)
                    Console.WriteLine("No numbers to sum");
            }
        });
    await command.InvokeAsync(args);
}

As you can see, there are several parts to the process:

Initially you declare a RootCommand with the options you want. The parameter to the Option constructor has the aliases to the option. The first option is a switch with to aliases, –verbose  and -v. That way, you can enable it using any of the two ways or passing a boolean argument (like in -v false). The second parameter will be an array of ints. This is specified in the generic type of the argument. In our case, we expect to have an array of ints. In this case, if you pass a value that cannot be parsed to an int, you will receive an error:

As you can see in the image above, you get the help and version options for free.

The second part is the handler, a function that will receive the parsed parameters and use them in the program. Finally, you will call the handler with:

await command.InvokeAsync(args);

Once you execute the program, it will parse your command line and will pass the arguments to the handler, where you can use them. In our handler, I am using the verbose switch to show extra info, and I’m summing the numbers. If anything is wrong, like unknown parameters or invalid data, the program will show an error message and the help.

Right now, I’ve used simple parameters, but we can also pass some special ones. As it’s very common to pass file or directory names in the command line, you can accept FileInfo and DirectoryInfo parameters, like in this code:

static async Task Main(string[] args)
{
    var command = new RootCommand
    {
        new Argument<DirectoryInfo>("Directory", 
            () => new DirectoryInfo("."))
            .ExistingOnly()
    };

    command.Handler = CommandHandler.Create(
        (DirectoryInfo directory) =>
        {
            foreach (var file in directory.GetFiles())
            {
               Console.WriteLine($"{file.Name,-40} {file.Length}");
            }
        });
    await command.InvokeAsync(args);
}

Here, you will have only one argument, a DirectoryInfo. If it isn’t passed, the second parameter in the Argument constructor is the function that will get a default argument to the handler, a DirectoryInfo pointing to the current directory. Then, it will list all the files in the selected directory. One interesting thing is the ExistingOnly method. It will ensure that the directory exists. If it doesn’t exist, an error will be generated:

If the class has a constructor receiving a string, you can also use it as an argument, like in the following code:

static async Task Main(string[] args)
{
    var command = new RootCommand
    {
        new Argument<StreamReader>("stream")
    };

    command.Handler = CommandHandler.Create(
        (StreamReader stream) =>
        {
            var fileContent = stream.ReadToEnd();
            Console.WriteLine(fileContent);
        });
    await command.InvokeAsync(args);
}

In this case, the argument is a StreamReader, that is created directly from a file name. Then, I use the ReadToEnd method to read the file into a string, and then show the contents in the console.

You can also pass a complex class and minimize the number of parameters that are passed to the handler.

All this is really fine, we now have a great method to parse the command line, but it’s a little clumsy: create a command, then declare a handler to process the commands and then call the InvokeAsync method to process the parameters and execute the code. Wouldn’t it be better to have something easier to parse the command line? Well, there is. Microsoft went further and created DragonFruit – with it, you can add your parameters directly in the Main method.

Instead of adding the System.CommandLine NuGet package, you must add the System.CommandLine.DragonFruit package and, all of a sudden, your Main method can receive the data you want as parameters. For example, the first program will turn into:

static void Main(bool verbose, int[] numbers)
{
    if (numbers != null)
    {
        if (verbose)
            Console.WriteLine($"Adding {string.Join(' ', numbers)}");

        Console.WriteLine(numbers.Sum());
    }
    else
    {
        if (verbose)
            Console.WriteLine("No numbers to sum");
    }
}

If you run it, you will have something like this:

If you notice the code, it is the same as the handler. What Microsoft is doing, under the hood, is to hide all this boilerplate from you and calling your main program with the parameters you are defining. If you want to make a parameter an argument, with no option, you should name it as argumentargs or arguments:

static void Main(bool verbose, int[] args)
{
    if (args != null)
    {
        if (verbose)
            Console.WriteLine($"Adding {string.Join(' ', args)}");

        Console.WriteLine(args.Sum());
    }
    else
    {
        if (verbose)
            Console.WriteLine("No numbers to sum");
    }
}

 

You can also define default values with the exactly same way you would do with other methods:

static void Main(int number = 10, bool verbose = false)
{
    var primes = GetPrimesLessThan(number);
    Console.WriteLine($"Found {primes.Length} primes less than {number}");
    Console.WriteLine($"Last prime last than {number} is {primes.Last()}");
    if (verbose)
    {
        Console.WriteLine($"Primes: {string.Join(' ',primes)}");
    }
}

private static int[] GetPrimesLessThan(int maxValue)
{
    if (maxValue <= 1)
        return new int[0];
    ;
    var primeArray = Enumerable.Range(0, maxValue).ToArray();
    var sizeOfArray = primeArray.Length;

    primeArray[0] = primeArray[1] = 0;

    for (int i = 2; i < Math.Sqrt(sizeOfArray - 1) + 1; i++)
    {
        if (primeArray[i] <= 0) continue;
        for (var j = 2 * i; j < sizeOfArray; j += i)
            primeArray[j] = 0;
    }

    return primeArray.Where(n => n > 0).ToArray();
}

If you want more help in your app, you can add xml comments in the app, they will be used when the help is requested:

/// <summary>
/// Lists files of the selected directory.
/// </summary>
/// <param name="argument">The directory name</param>
static void Main(DirectoryInfo argument)
{
    argument ??= new DirectoryInfo(".");
    foreach (var file in argument.GetFiles())
    {
        Console.WriteLine($"{file.Name,-40} {file.Length}");
    }
}

As you can see, there are a lot of options for command line processing, and DragonFruit makes it easier to process them. But that is not everything, the same team has also made some enhancements to make use of the new Ansi terminal features, in System.CommandLine.Rendering. For example, if you want to list the files in a table, you can use code like this:

static void Main(InvocationContext context, DirectoryInfo argument= null)
{
    argument ??= new DirectoryInfo(".");
    var consoleRenderer = new ConsoleRenderer(
        context.Console,
        context.BindingContext.OutputMode(),
        true);

    var tableView = new TableView<FileInfo>
    {
        Items = argument.EnumerateFiles().ToList()
    };

    tableView.AddColumn(f => f.Name, "Name");

    tableView.AddColumn(f => f.LastWriteTime, "Modified");

    tableView.AddColumn(f => f.Length, "Size");

    var screen = new ScreenView(consoleRenderer, context.Console) { Child = tableView };
    screen.Render();
}

As you can see, the table is formatted for you. You may have noticed another thing: the first parameter, context, is not entered by the user. This is because you can have some variables injected for you by DragonFruit.

There are a lot of possibilities with System.CommandLine and this is a very welcome addition. I really liked it and, although it’s still on preview, it’s very nice and I’m sure I’ll use it a lot. And you, what do you think?

All the source code for this article is at https://github.com/bsonnino/CommandLine

Introduction

Learning a new programming language is always hard. You have to read the documentation, stop, go to the IDE of your choice, start a new project, compile and run the program and see the results. Stop. Restart. After some time, you have no more patience for the process and start to skip some steps (maybe I don’t need to run this sample here…) until the point that you try to find an easier way, but most of the time there is no easier way.

Well, now there is an easier way. The dotnet team has created a new tool, called dotnet try that allows you to have documentation mixed with a code sample window, where you can try the code while reading the documentation. Cool, no?

Starting with dotnet try

To use this tool, you must install dotnet core in your machine and then install dotnet try with:

dotnet tool install -g dotnet-try

Once you do that, you can use the tool with dotnet try. But, the better way to start is to use the tool’s own demo with dotnet try demo. This will load the tutorial, create a server and open the browser with the tutorial:

There you can see the documentation side-by-side with the code. You can change the code and click on the arrow key and look at the results. Nice! You can  follow the tutorial and learn how to create your documentation easily. Or you can continue reading and I will tell you how to use it (sorry, no dotnet try here :-)).

If you want to learn something about .NET, you can run the samples. Just clone the dotnet/try-samples repository, change to the cloned folder and type dotnet try to open it.

There you can see some tutorials:

  • Beginner’s guide
  • 101 Linq Samples
  • C# 7
  • C# 8

With that, you can start playing with the tools. I recommend checking the Linq samples, there is always something to learn about Linq, and the new features in C#7 and 8.

Once you’ve played with the tool, it’s the time to create your own documentation.

Creating documentation for your code

All documentation is based on the MarkDown language. It’s a very simple way to create your documentation, If you don’t know how to use the MarkDown language, you can learn it here.

Create a new folder and, in this folder, create a readme.md file and add this code:

# Documenting your code with **dotnet try**
## Introduction
To document your code with dotnet try, you must mix your markdown text with some fences

Once you save it and run dotnet try, you can see something like this in the browser:

Now you can start adding code. To add the code window, we must add a code fence, a block of code delimited by triple backticks. You can add code directly to the code fence with something like this:

using System;

namespace dotnettrypost
{
    class Program
    {
        static void Main(string[] args)
        {
            #region HelloWorld
            Console.WriteLine("Hello World!");
            #endregion
        }
    }
}

This is great, but can pose a problem: if you need to change something in your code, you must also change the markdown file, and this is less than optimum, as at some point you will forget to update both places. But there is a better way: you can use the source code as the only source of truth and you don’t have to update both places – when you change the source code, the code in the page changes accordingly. To do this, you can create a console app with

dotnet new console

This creates a simple project that writes Hello World in the console. It’s pretty simple, but fits our needs. Now, we will document it. You can add the following code at the end of the readme.md file to show the contents of the program in editable form:

Below, you should see the code in the program.cs file:
```cs --source-file ./Program.cs --project ./dotnettrypost.csproj
```

In this code fence, you must add the name of the file and the project. If you run dotnet try again, you will see something like this:

You can click the arrow to run the code, the first time, it will take a little bit to compile the code, but the other times, it will be faster. You can change the code and see the results:

You have here a safe environment, where you can try changes in the code. If you make a mistake, the window will point it, so you can fix it.

As you can see, the code for the entire file is shown, but sometimes that’s not what you want. Sometimes you just want to show a code snippet from the file. To do that, you must create regions in your code. In the command prompt, type code . to open Visual Studio Code (you must have it installed. If you don’t have it installed, go to https://code.visualstudio.com/download). Then add a region to your code:

using System;

namespace dotnettrypost
{
    class Program
    {
        static void Main(string[] args)
        {
            #region HelloWorld
            Console.WriteLine("Hello World!");
            #endregion
        }
    }
}

 

Save the file and then edit the Readme.md file to change the code fence:

```cs --source-file ./Program.cs --project ./dotnettrypost.csproj --region HelloWorld
```

When you save the file and refresh the page, you will see something like this:

Creating regions in your code and referencing them in the code fence allows you to separate the code you want in the code window. That way, a single file can provide code for many code windows.

If you don’t want to have a runnable window, but still synchronized with the code, you can use the editable parameter:

```cs --source-file ./Program.cs --project ./dotnettrypost.csproj --region HelloWorld --editable false
```

When you are running the code, even if you aren’t showing the full code, everything in the program is executed. For example, if you change the source code to:

using System;
namespace dotnettrypost
{
    class Program
    {
        static void Main(string[] args)
        {
            #region HelloWorld
            Console.WriteLine("Hello World!");
            #endregion
            Console.WriteLine("This won't be shown in the snippet but will be shown when you run");
        }
    }
}

You will see the same code snippet but when you run it you will see something like this:

If you don’t want this, you need to do some special treatment in your code: you must make each snippet of code run alone. You can do that by processing the parameters passed to the program:

using System;

namespace dotnettrypost
{
    class Program
    {
        static void Main(string[] args)
        {
            for (var i = 0; i < args.Length; i++)
                if (args[i] == "--region")
                {
                    if (args[i + 1] == "HelloWorld")
                        HelloWorld();
                    else if (args[i + 1] == "ByeBye")
                        ByeBye();
                }
        }

        private static void HelloWorld()
        {
            #region HelloWorld
            Console.WriteLine("Hello World!");
            #endregion
        }

        private static void ByeBye()
        {
            #region ByeBye
            Console.WriteLine("ByeBye!");
            #endregion
        }
    }
}

Now, when you run the code, it will run only the code for the HelloWorld snippet. As you can see there are a lot of options to show and run code in the web page. I see many ways to improve documentation and experimentation of new APIs. And you, don’t this bring you new ideas?

All the source code for this project is at https://github.com/bsonnino/dotnettry

One of the perks of being an MVP is to receive some tools for my own use, so I can evaluate them and if, I find them valuable, I can use them on a daily basis. On my work as an architect/consultant, one task that I often find is to analyse an application and suggest changes to make it more robust and maintainable. One tool that can help me in this task is NDepend (https://www.ndepend.com/). With this tool, you can analyse your code, verify its dependencies, set coding rules and verify if the how code changes are affecting the quality of your application. For this article, we will be analyzing eShopOnWeb (https://github.com/dotnet-architecture/eShopOnWeb), a sample application created by Microsoft to demonstrate architectural patterns on Dotnet Core. It’s an Asp.NET MVC Core 2.2 app that shows a sample shopping app, that can be run on premises or in Azure or in containers, using a Microservices architecture. It has a companion book that describes the application architecture and can be downloaded at https://aka.ms/webappebook.

When you download, compile and run the app, you will see something like this:

You have a full featured Shopping app, with everything you would expect to have in this kind of app. The next step is to start analyzing it.

Download NDepend (you have a 14 day trial to use and evaluate it), install it in your machine, it will install as an Add-In to Visual Studio. The, in Visual Studio, select Extensions/NDepend/Attach new NDepend Project to current VS Solution. A window like this opens:

It has the projects in the solution selected, you can click on Analyze 3 .NET Assemblies. After it runs, it opens a web page with a report of its findings:

This report has an analysis of the project, where you can verify the problems NDepend found, the project dependencies, and drill down in the issues. At the same time, a window like this opens in Visual Studio:

If you want a more dynamic view, you can view the dashboard:

 

In the dashboard, you have a full analysis of your application: lines of code, methods, assemblies and so on. One interesting metric there is the technical debt, where you can see how much technical debt there is in this app and how long will it take to fix this debt (in this case, we have 3.72% of technical debt and 2 days to fix it. We have also the code metrics and violated coding rules. If you click in some item in the dashboard, like the # of Lines, you will see the detail in the properties window:

If we take a look at the dashboard, we’ll see some issues that must be improved. In the Quality Gates, we have two issues that fail. By clicking on the number 2 there, we see this in the Quality Gates evolution window:

If we hover the mouse on one of the failed issues, we get a tooltip that explains the failure:

If we double-click in the failure we drill-down to what caused it:

If we click in the second issue, we see that there are two names used in different classes: Basket and IEmailSender:

Basket is the name of the class in Microsoft.eShopWeb.Web.Pages.Shared.Components.BasketComponent and in Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate

One other thing that you can see is the dependency graph:

With it, you can see how the assemblies relate to each other and give a first look on the architecture. If you filter the graph to show only the application assemblies, you have a good overview of what’s happening:

The largest assembly is Web, followed by Infrastructure and ApplicationCore. The application is well layered, there are no cyclic calls between assemblies (Assembly A calling assembly B that calls A), there is a weak relation between Web and Infrastructure (given by the width of the line that joins them) and a strong one between Web and ApplicationCore. If we have a large solution with many assemblies, just with that diagram, we can take a look of what’s happening and if we are doing the things right. The next step is go to the details and look at the assemblies dependencies. You can hover the assembly and get some info about it:

For example, the Web assembly has 58 issues detected and has an estimated time to fix them in 1 day. This is an estimate calculated by NDepend using the complexity of the methods that must be fixed, but you can set your own formula to calculate the technical debt if this isn’t ok for your team.

Now that we got an overview of the project, we can start fixing the issues. Let’s start with the easiest ones :-).  The Infrastructure assembly has only two issues and a debt of 20 minutes. In the diagram, we can right-click on the Infrastructure assembly and select Select Issues/On Me and on Child Code elements. This will open the issues in the Queries and Rules Edit window, at the right:

We can then open the tree and double click on the second issue. It will point you to a rule “Non-static classes should be instantiated or turned to static”, pointing to the SpecificatorEvaluator<T> class. This is a class that has only one static method and is referenced only one time, so there’s no harm to make it static.  Once you make it static, build the app and run the dependency check again, you will see this:

Oh-Oh. We fixed an issue and introduced another one – an API Breaking Change – when we made that class static, we removed the constructor. In this case, it wasn’t really an API change, because nobody would instantiate a class with only static methods, so we should do a restart, here. Go to the Dashboard, in Choose Baseline and select define:

Then select the most recent analysis and click OK.  That will open the NDepend settings, where you will see the new baseline. Save the settings and rerun the analysis and the error is gone.

We can then open the tree again and double click on another issue that remains in Infrastructure. That will open the source code, pointing to a readonly declaration for a DBContext. This is not a big issue, it’s only telling us that we are declaring the variable as readonly, but the object it’s storing is mutable, so it can be changed. There is a mention of this issue in the Framework Design Guidelines, by Microsoft – https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/. If you hover the mouse on the issue, there is a tooltip on how to fix it:

We have three ways to fix this issue:

  • Remove the readonly from the field
  • Make the field private and not protected
  • Use an attribute to say “ok, I am aware of this, but I don’t mind”

The first option will suppress the error, but will remove what we want to do – show that this field should not be entirely replaced with another dbcontext. The second option will remove the possibility to use the dbcontext in derived classes, so I’ll choose the third option and add the attribute. If I right-click on the issue in the Rules and Queries window and select Suppress Issue, a window opens:

All I have to do is to copy the attribute to the clipboard and paste it into the source code. I also have to declare the symbol CODE_ANALYSIS in the project (Project/Properties/Build). That was easy! Let’s go to the next one.

This is an obsolete method used. Fortunately, the description shows us to use the UseHiLo method. We change the method, run the app to see if there’s nothing broken, and we’re good. W can run the analysis again and see what happened:

We had a slight decrease in the technical debt, we solved one high issue and one violated rule. As you can see, NDepend not only analyzes your code, but it also gives you a position on what you are doing with your code changes. This is a very well architected code (as it should be – it’s an architecture sample), so the issues are minor, but you can see what can be done with NDepend. When you have a messy project, this will be surely an invaluable tool!

 

One thing that comes to me sometimes when I am debugging a program is to see a variable that has changed value, but I don’t know where and when it was changed. That leaded me to long and boring sessions of debugging, checking where the variable could have its changed.

I just wanted some kind of breakpoint that I could set that told me “break when this variable changes value”, but Visual Studio never had such a feature. Until now.

With .Net Core 3.0, Microsoft introduced a new feature in Visual Studio that will enhance a lot the debugging experience. The only drawback is that it only works with .Net Core 3.0 apps. But, nevertheless, it’s a great, long waited feature!

This feature is a little buried in Visula Studio (no, there is no right-click in a variable name with something like “break when this variable value changes”), so how do I set a data breakpoint?

To show this feature, I will use a .Net Core console app that gets the prime numbers:

static void Main(string[] args)
{
    if (args.Length < 1)
    {
        Console.WriteLine("usage: GetPrimesLessThan <number>");
        return;
    }

    if (!int.TryParse(args[0], out int number))
    {
        Console.WriteLine("parameter should be a valid integer number");
        return;
    }
    var primes = GetPrimesLessThan(number);
    Console.WriteLine($"Found {primes.Length} primes less than {number}");
    Console.WriteLine($"Last prime last than 10000 is {primes.Last()}");
}
private static int[] GetPrimesLessThan(int maxValue)
{
    if (maxValue <= 1)
        return new int[0];
    ;
    var primeArray = Enumerable.Range(0, maxValue).ToArray();
    var sizeOfArray = primeArray.Length;

    primeArray[0] = primeArray[1] = 0;

    for (int i = 2; i < Math.Sqrt(sizeOfArray - 1) + 1; i++)
    {
        if (primeArray[i] <= 0) continue;
        for (var j = 2 * i; j < sizeOfArray; j += i)
            primeArray[j] = 0;
    }

    return primeArray.Where(n => n > 0).ToArray();
}

This algorithm uses an array of integers that are set to zero if the number is not prime. At the end, all non prime numbers in the array are set to zero, so I can extract the primes with this linq line:

return primeArray.Where(n => n > 0).ToArray();

Let’s say that I want to know when the number 10 changes to 0. If I didn’t have this feature, I would have to follow the code, set breakpoints and analyze the data for every hit. Not anymore.

Here’s what must be done:

  • Set a breakpoint in the line just after the PrimeArray declaration
  • Run the code and let it break.
  • Open the Locals Window and expand the PrimeArray node
  • Right Click on the node with the value 10
  • Select Break when Value Changes

That’s it! You have set a data breakpoint for the value 10 in the array. Now let the code run, when the value changes, the code will break:

There you can analyze your code and see what has changed the variable. Nice, no?

This is a great feature and will enhance a lot my debugging, I’m glad that Visual Studio people could add it. I hope that you’ll enjoy it as much as I do.

The source code for the used project is at https://github.com/bsonnino/DataBreakpoint

Recently, I went to a client, where they assigned me a machine to work, but there was no software installed, and there was no admin rights to install any software. I had to set up my box to start development before all the red tape could be set, licenses assigned, software installed and so on.

So I thought to setup a box where I could install the software as standard user, with no need of any special licenses to get up und running for development in Dotnet Core and React with Typescript (the same steps can be used to develop with Angular or any other Javascript environment).

Initially, I installed Google Chrome (its developer tools are great and you can debug your Javascript apps in it). I went to Chrome site (https://www.google.com/chrome/), downloaded the installer and ran it. When it asked me the admin credentials, I clicked Cancel and the installer offered to install as Standard User.

Then I installed Visual Studio Code. It’s a terrific IDE to edit your programs (in any language) and even debug your apps. There are extensions to do everything, it’s really great. I used a direct link to download the portable version and unzip the zip file and add the path to VSCode with this Powershell script:

Set-ExecutionPolicy Bypass -Scope Process -Force;
$remoteFile = 'https://go.microsoft.com/fwlink/?Linkid=850641';
$downloadFile = $env:Temp+'\vscode.zip';
$vscodePath = $env:LOCALAPPDATA+"\VsCode";

(New-Object Net.WebClient).DownloadFile($remoteFile, $downloadFile);
Expand-Archive $downloadFile -DestinationPath $vscodePath -Force
$env:Path += ";"+$vscodePath
[Environment]::SetEnvironmentVariable
     ("Path", $env:Path, [System.EnvironmentVariableTarget]::User);

The next step is to install Node.js (https://nodejs.org/). Node.js is a JavaScript runtime, where you can run your Javascript Apps. I went to the Node site saw that there is a zip file for Windows (https://nodejs.org/dist/v12.13.0/node-v12.13.0-win-x64.zip). I created this Powershell script to download and install Node LTS:

Set-ExecutionPolicy Bypass -Scope Process -Force;
$remoteFile = 'https://nodejs.org/dist/v12.13.0/node-v12.13.0-win-x64.zip';
$downloadFile = $env:Temp+'\node-v12.13.0-win-x64.zip';
$nodePath = $env:LOCALAPPDATA+"\Node";

(New-Object Net.WebClient).DownloadFile($remoteFile, $downloadFile);
Expand-Archive $downloadFile -DestinationPath $nodePath -Force
$env:Path += ";"+$nodePath
[Environment]::SetEnvironmentVariable
     ("Path", $env:Path, [System.EnvironmentVariableTarget]::User);

You can open a Powershell window and type these commands or you can downlad and run the script file from my GitHub (link below).

The next step is to install Yarn (https://yarnpkg.com/lang/en/), a dependency manager running this command in the command line

npm install yarn -g

The last step is to install Dotnet core. There is no package to install it as a standar user, but there is a Powershell script to install it at https://dot.net/v1/dotnet-install.ps1. If you open a Powershell window and run the commands:

Set-ExecutionPolicy Bypass -Scope Process -Force;
$remoteFile = 'https://dot.net/v1/dotnet-install.ps1';
$downloadFile = 'dotnet-install.ps1';
$dotnetPath = $env:LOCALAPPDATA+"\Microsoft\Dotnet";

(New-Object Net.WebClient).DownloadFile($remoteFile, $downloadFile);
$env:Path += ";"+$dotnetPath
[Environment]::SetEnvironmentVariable("Path", $env:Path, [System.EnvironmentVariableTarget]::User);

Now you are all set, your development machine is ready to go. To create a React app, just open a command line window and type these commands:

yarn create react-app reactapp --typescript
cd reactapp
yarn start

If you point your browser to http://localhost:3000, you will see:

Running Code . will open VS Code with the project folder open:

You can also create an Asp.NET Core MVC with the commands:

dotnet new mvc -o AspNetApp
cd AspNetApp
dotnet run

And open http://localhost:5000 in the browser to see the app running:

As you can see, with a few steps, you can setup a developer machine with no need of admin rights nor the need of any license, you can now start developing your full apps and debug them in VS Code.

That way, I could start and run from day one, and when the full install came, I was already developing.

After finishing the setting, I noticed that all the scripts are very similar, so I created a single script, Install-FromWeb:

[CmdletBinding()]
Param (
  $RemoteFile,
  $DownloadFile,
  [bool]$DoExtractFile = $False,
  [string]$ExecutePath = $null,
  $AddedPath
)
Write-Host $RemoteFile
Write-Host $DownloadFile

Invoke-WebRequest -Uri $RemoteFile -OutFile $DownloadFile
If ($DoExtractFile){
  Expand-Archive $DownloadFile -DestinationPath $AddedPath -Force
}
If (-Not ([string]::IsNullOrEmpty($ExecutePath))){
  & "$ExecutePath"
}  
$env:Path += ";"+$AddedPath
[Environment]::SetEnvironmentVariable("Path", $env:Path, [System.EnvironmentVariableTarget]::User);

When I was pushing the data to my GitHub, I noticed that Git for Windows (https://gitforwindows.org/) wasn’t installed. I checked the Git for Windows site and there is a portable version in https://github.com/git-for-windows/git/releases/download/v2.24.0.windows.2/PortableGit-2.24.0.2-64-bit.7z.exe. With it, you can use Git as a version control system. With that, you can install your machine with a single set of instructions:

Set-ExecutionPolicy Bypass -Scope Process -Force;
.\Install-FromWeb.ps1 -RemoteFile "https://go.microsoft.com/fwlink/?Linkid=850641" -DownloadFile $env:Temp"\vscode.zip" -DoExtractFile $true -AddedPath $env:LOCALAPPDATA"\VsCode"
.\Install-FromWeb.ps1 -RemoteFile 'https://nodejs.org/dist/v12.13.0/node-v12.13.0-win-x64.zip' -DownloadFile $env:Temp'\node-v12.13.0-win-x64.zip' -DoExtractFile $true -AddedPath $env:LOCALAPPDATA"\Node"
Move-Item -Path $env:LOCALAPPDATA"\Node\node-v12.13.0-win-x64" -Destination $env:LOCALAPPDATA"\Node1"
Remove-item -Path $env:LOCALAPPDATA"\Node"
Rename-item -Path $env:LOCALAPPDATA"\Node1" -NewName $env:LOCALAPPDATA"\Node"
npm install yarn -g
.\Install-FromWeb.ps1 -RemoteFile 'https://dot.net/v1/dotnet-install.ps1' -DownloadFile $env:Temp'\dotnet-install.ps1' -DoExtractFile $false -AddedPath $env:LOCALAPPDATA'\Microsoft\Dotnet' -ExecutePath $env:Temp'\dotnet-install.ps1'
.\Install-FromWeb.ps1 -RemoteFile 'https://github.com/git-for-windows/git/releases/download/v2.24.0.windows.2/PortableGit-2.24.0.2-64-bit.7z.exe' -DownloadFile $env:TEMP'\PortableGit.exe' -DoExtractFile $false -AddedPath $env:LOCALAPPDATA'\Git' -Debug
& $env:TEMP'\PortableGit.exe' -o $env:LOCALAPPDATA'\Git' -y

All the scripts are in my Github : https://github.com/bsonnino/DevMachine

So, happy development!

You need to create customized reports based on Sharepoint data and you don’t have the access to the server to create a new web part to access it directly. You need to resort to other options to generate the report. Fortunately, Sharepoint allows some methods to access its data, so you can get the information you need. If you are using C#, you can use one of these three methods:

  • Client Side Object Model (CSOM) – this is a set of APIs that allow access to the Sharepoint data and allow you to maintain lists and documents in Sharepoint
  • REST API – with this API you can access Sharepoint data, not only using C#, but with any other platform that can perform and process REST requests, like web or mobile apps
  • SOAP Web Services – although this kind of access is being deprecated, there are a lot of programs that depend on it, so it’s being actively used until now. You can use this API with any platform that can process SOAP web services.

This article will show how to use these three APIs to access lists and documents from a Sharepoint server and put them in a WPF program, with an extra touch: the user will be able to export the data to Excel, for further manipulation.

To start, let’s create a WPF project in Visual Studio and name it SharepointAccess. We will use the MVVM pattern to develop it, so right click the References node in the Solution Explorer and select Manage NuGet Packages and add the MVVM Light package. That will add a ViewModel folder, with two files in it to your project. The next step is to make a correction in the ViewModelLocator.cs file. If you open it, you will see an error in the line

using Microsoft.Practices.ServiceLocation;

Just replace this using clause with

using CommonServiceLocator;

The next step is to link the DataContext of MainWindow to the MainViewModel, like it’s described in the header of ViewModelLocator:

<Window x:Class="SharepointAccess.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:SharepointAccess"
        mc:Ignorable="d"
        Title="Sharepoint Access" Height="700" Width="900"
        DataContext="{Binding Source={StaticResource Locator}, Path=Main}">

Then, let’s add the UI to MainWindow.xaml:

<Grid>
    <Grid.Resources>
        <DataTemplate x:Key="DocumentsListTemplate">
            <StackPanel>
                <TextBlock Text="{Binding Title}" />
            </StackPanel>
        </DataTemplate>
        <DataTemplate x:Key="DocumentTemplate">
            <StackPanel Margin="0,5">
                <TextBlock Text="{Binding Title}" />
                <TextBlock Text="{Binding Url}" />

            </StackPanel>
        </DataTemplate>
        <DataTemplate x:Key="FieldsListTemplate">
            <Grid >
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="150" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <TextBlock Text="{Binding Key}" TextTrimming="CharacterEllipsis"/>
                <TextBlock Text="{Binding Value}" Grid.Column="1" TextTrimming="CharacterEllipsis"/>
            </Grid>
        </DataTemplate>
    </Grid.Resources>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="40" />
        <RowDefinition Height="*" />
        <RowDefinition Height="40" />
    </Grid.RowDefinitions>
    <StackPanel Grid.Row="0" Grid.ColumnSpan="3" HorizontalAlignment="Stretch" Orientation="Horizontal">
        <TextBox Text="{Binding Address}" Width="400" Margin="5" HorizontalAlignment="Left"
                 VerticalContentAlignment="Center"/>
        <Button Content="Go" Command="{Binding GoCommand}" Width="65" Margin="5" HorizontalAlignment="Left"/>
    </StackPanel>
    <StackPanel Orientation="Horizontal"  Grid.Row="0" Grid.Column="3" 
                      HorizontalAlignment="Right" Margin="5,5,10,5">
        <RadioButton Content=".NET Api" IsChecked="True" GroupName="ApiGroup" Margin="5,0"
                     Command="{Binding ApiSelectCommand}" CommandParameter="NetApi" />
        <RadioButton Content="REST" GroupName="ApiGroup"
                     Command="{Binding ApiSelectCommand}" CommandParameter="Rest" Margin="5,0"/>
        <RadioButton Content="SOAP"  GroupName="ApiGroup"
                     Command="{Binding ApiSelectCommand}" CommandParameter="Soap" Margin="5,0"/>
    </StackPanel>
    <ListBox Grid.Column="0" Grid.Row="1" ItemsSource="{Binding DocumentsLists}" 
             ItemTemplate="{StaticResource DocumentsListTemplate}"
             SelectedItem="{Binding SelectedList}"/>
    <ListBox Grid.Column="1" Grid.Row="1" ItemsSource="{Binding Documents}"
             ItemTemplate="{StaticResource DocumentTemplate}"
             SelectedItem="{Binding SelectedDocument}"/>
    <ListBox Grid.Column="2" Grid.Row="1" ItemsSource="{Binding Fields}"
             ItemTemplate="{StaticResource FieldsListTemplate}"
             />
    <TextBlock Text="{Binding ListTiming}" VerticalAlignment="Center" Margin="5" Grid.Row="2" Grid.Column="0" />
    <TextBlock Text="{Binding ItemTiming}" VerticalAlignment="Center" Margin="5" Grid.Row="2" Grid.Column="1" />
  </Grid>

The first line in the grid will have a text box to enter the address of the Sharepoint site, a button to go to the address and three radiobuttons to select the kind of access you want.

The main part of the window will have three listboxes to show the lists on the selected site, the documents in each list and the properties of the document.

As you can see, we’ve used data binding to fill the properties of the UI controls. We’re using the MVVM pattern and all these propperties should be bound to properties in the ViewModel. The buttons and radiobuttons have their Command properties bound to a property in the ViewModel, so we don`t have to add code to the code behind file. We’ve also used templates for the items in the listboxes, so the data is properly presented.

The last line will show the timings for getting the data.

If you run the program, it will run without errors and you will get an UI like this, that doesn’t do anything:

 

It’s time to add the properties in the MainViewModel to achieve the functionality we want. Before that, we’ll add two classes to manipulate the data that comes from the Sharepoint server, no matter the access we are using. Create a new folder named Model, and add these two files, Document.cs and DocumentsList.cs:

public class Document
{
    public Document(string id, string title, 
        Dictionary<string, object> fields,
        string url)
    {
        Id = id;
        Title = title;
        Fields = fields;
        Url = url;
    }
    public string Id { get; }
    public string Title { get; }
    public string Url { get; }
    public Dictionary<string, object> Fields { get; }
}<span id="mce_marker" data-mce-type="bookmark" data-mce-fragment="1">​</span>
public class DocumentsList
{
    public DocumentsList(string title, string description)
    {
        Title = title;
        Description = description;
    }

    public string Title { get; }
    public string Description { get; }
}

These are very simple classes, that will store the data that comes from the Sharepoint server. The next step is to get the data from the server. For that we will use a repository that gets the data and returns it using these two classes. Create a new folder, named Repository and add a new interface, named IListRepository:

public interface IListRepository
{
    Task<List<Document>> GetDocumentsFromListAsync(string title);
    Task<List<DocumentsList>> GetListsAsync();
}

This interface declares two methods, GetListsAsync and GetDocumentsFromListAsync. These are asynchronous methods because we don want them to block the UI while they are being called. Now, its time to create the first access to Sharepoint, using the CSOM API. For that, you must add the NuGet package Microsoft.SharePointOnline.CSOM . This package will provide all the APIs to access Sharepoint data. We can now create our first repository. In the Repository folder, add a new class and name it CsomListRepository.cs:

public class CsomListRepository : IListRepository
{
    private string _sharepointSite;

    public CsomListRepository(string sharepointSite)
    {
        _sharepointSite = sharepointSite;
    }

    public Task<List<DocumentsList>> GetListsAsync()
    {
        return Task.Run(() =>
        {
            using (var context = new ClientContext(_sharepointSite))
            {
                var web = context.Web;
                
                var query = web.Lists.Include(l => l.Title, l => l.Description)
                     .Where(l => !l.Hidden && l.ItemCount > 0);

                var lists = context.LoadQuery(query);
                context.ExecuteQuery();

                return lists.Select(l => new DocumentsList(l.Title, l.Description)).ToList();
            }
        });
    }

    public Task<List<Document>> GetDocumentsFromListAsync(string listTitle)
    {
        return Task.Run(() =>
        {
            using (var context = new ClientContext(_sharepointSite))
            {
                var web = context.Web;
                var list = web.Lists.GetByTitle(listTitle);
                var query = new CamlQuery();

                query.ViewXml = "<View />";
                var items = list.GetItems(query);
                context.Load(list,
                    l => l.Title);
                context.Load(items, l => l.IncludeWithDefaultProperties(
                    i => i.Folder, i => i.File, i => i.DisplayName));
                context.ExecuteQuery();

                return items
                    .Where(i => i["Title"] != null)
                    .Select(i => new Document(i["ID"].ToString(), 
                    i["Title"].ToString(), i.FieldValues, i["FileRef"].ToString()))
                    .ToList();
            }
        });
    }
}

For accessing the Sharepoint data, we have to create a ClientContext, passing the Sharepoint site to access. Then, we get a reference to the Web property of the context and then we do a query for the lists that aren’t hidden and that have any items in them. The query should return with the titles and description of the lists in the website. To get the documents from a list we use a similar way: create a context, then a query and load the items of a list.

We can call this code in the creation of the ViewModel:

private IListRepository _listRepository;

public MainViewModel()
{
    Address = ConfigurationManager.AppSettings["WebSite"];
    GoToAddress();
}

This code will get the initial web site url from the configuration file for the app and call GoToAddress:

private async void GoToAddress()
{
    var sw = new Stopwatch();
    sw.Start();
    _listRepository = new CsomListRepository(Address);

    DocumentsLists = await _listRepository.GetListsAsync();
    ListTiming = $"Time to get lists: {sw.ElapsedMilliseconds}";
    ItemTiming = "";
}

The method calls the GetListsAsync method of the repository to get the lists and sets the DocumentsLists property with the result.

We must also declare some properties that will be used to bind to the control properties in the UI:

public List<DocumentsList> DocumentsLists
{
    get => _documentsLists;
    set
    {
        _documentsLists = value;
        RaisePropertyChanged();
    }
}
public string ListTiming
{
    get => _listTiming;
    set
    {
        _listTiming = value;
        RaisePropertyChanged();
    }
}
public string ItemTiming
{
    get => itemTiming;
    set
    {
        itemTiming = value;
        RaisePropertyChanged();
    }
}
public string Address
{
    get => address;
    set
    {
        address = value;
        RaisePropertyChanged();
    }
}

These properties will trigger the PropertyChanged event handler when they are modified, so the UI can be notified of their change.

If you run this code, you will have something like this:

Then, we must get the documents when we select a list. This is done when the SelectedList property changes:

public DocumentsList SelectedList
{
    get => _selectedList;
    set
    {
        if (_selectedList == value)
            return;
        _selectedList = value;
        GetDocumentsForList(_selectedList);
        RaisePropertyChanged();
    }
}

GetDocumentsForList is:

private async void GetDocumentsForList(DocumentsList list)
{
    var sw = new Stopwatch();
    sw.Start();
    if (list != null)
    {
        Documents = await _listRepository.GetDocumentsFromListAsync(list.Title);
        ItemTiming = $"Time to get items: {sw.ElapsedMilliseconds}";
    }
    else
    {
        Documents = null;
        ItemTiming = "";
    }
}

You have to declare the Documents and the Fields properties:

public List<Document> Documents
{
    get => documents;
    set
    {
        documents = value;
        RaisePropertyChanged();
    }
}

public Dictionary<string, object> Fields => _selectedDocument?.Fields;

One other change that must be made is to create a property named SelectedDocument that will be bound to the second list. When the user selects a document, it will fill the third list with the document’s properties:

public Document SelectedDocument
{
    get => _selectedDocument;
    set
    {
        if (_selectedDocument == value)
            return;
        _selectedDocument = value;
        RaisePropertyChanged();
        RaisePropertyChanged("Fields");
    }
}

Now, when you click on a list, you get all documents in it. Clicking on a document, opens its properties:

Everything works with the CSOM access, so it’s time to add the other two ways to access the data: REST and SOAP. We will implement these by creating two new repositories that will be selected at runtime.

To get items using the REST API, we must do HTTP calls to http://<website>/_api/Lists and  http://<website>/_api/Lists/GetByTitle(‘title’)/Items. In the Repository folder, create a new class and name it RestListRepository. This class should implement the IListRepository interface:

public class RestListRepository : IListRepository
{
    public Task<List<Document>> GetDocumentsFromListAsync(string title)
    {
        throw new NotImplementedException();
    }

    public Task<List<DocumentsList>> GetListsAsync()
    {
        throw new NotImplementedException();
    }
}

GetListsAsync will be:

private XNamespace ns = "http://www.w3.org/2005/Atom";
public Task<List<DocumentsList>> GetListsAsync()
{
    var doc = await GetResponseDocumentAsync(_sharepointSite + "Lists");
    if (doc == null)
        return null;

    var entries = doc.Element(ns + "feed").Descendants(ns + "entry");
    return entries.Select(GetDocumentsListFromElement)
        .Where(d => !string.IsNullOrEmpty(d.Title)).ToList();
}

GetResponseDocumentAsync will issue a HTTP Get request, will process the response and will return an XDocument:

public async Task<XDocument> GetResponseDocumentAsync(string url)
{
    var handler = new HttpClientHandler
    {
        UseDefaultCredentials = true
    };
    HttpClient httpClient = new HttpClient(handler);
    var headers = httpClient.DefaultRequestHeaders;
    var header = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " +
        "(KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36";
    if (!headers.UserAgent.TryParseAdd(header))
    {
        throw new Exception("Invalid header value: " + header);
    }
    Uri requestUri = new Uri(url);

    try
    {
        var httpResponse = await httpClient.GetAsync(requestUri);
        httpResponse.EnsureSuccessStatusCode();
        var httpResponseBody = await httpResponse.Content.ReadAsStringAsync();
        return XDocument.Parse(httpResponseBody);
    }
    catch
    {
        return null;
    }
}

The response will be a XML String. We could get the response as a Json object if we pass the accept header as application/json.  After the document is parsed, we process all entry elements, retrieving the lists, in GetDocumentsListFromElement:

private XNamespace mns = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata";
private XNamespace dns = "http://schemas.microsoft.com/ado/2007/08/dataservices";
private DocumentsList GetDocumentsListFromElement(XElement e)
{
    var element = e.Element(ns + "content")?.Element(mns + "properties");
    if (element == null)
        return new DocumentsList("", "");
    bool.TryParse(element.Element(dns + "Hidden")?.Value ?? "true", out bool isHidden);
    int.TryParse(element.Element(dns + "ItemCount")?.Value ?? "0", out int ItemCount);
    return !isHidden && ItemCount > 0 ?
      new DocumentsList(element.Element(dns + "Title")?.Value ?? "",
        element.Element(dns + "Description")?.Value ?? "") :
      new DocumentsList("", "");
}

Here we filter the list by parsing the Hidden and ItemCount properties and returning an empty document if the document is hidden or has no items. GetDocumentsFromListAsync is:

private async Task<Document> GetDocumentFromElementAsync(XElement e)
{
    var element = e.Element(ns + "content")?.Element(mns + "properties");
    if (element == null)
        return new Document("", "", null, "");
    var id = element.Element(dns + "Id")?.Value ?? "";
    var title = element.Element(dns + "Title")?.Value ?? "";
    var description = element.Element(dns + "Description")?.Value ?? "";
    var fields = element.Descendants().ToDictionary(el => el.Name.LocalName, el => (object)el.Value);
    int.TryParse(element.Element(dns + "FileSystemObjectType")?.Value ?? "-1", out int fileType);
    string docUrl = "";

    var url = GetUrlFromTitle(e, fileType == 0 ? "File" : "Folder");
    if (url != null)
    {
        var fileDoc = await GetResponseDocumentAsync(_sharepointSite + url);
        docUrl = fileDoc.Element(ns + "entry")?.
            Element(ns + "content")?.
            Element(mns + "properties")?.
            Element(dns + "ServerRelativeUrl")?.
            Value;
    }

    return new Document(id, title, fields, docUrl);
}

It parses the XML and extracts a document and its properties. GetUrlFromTitle gets the Url from the Title property and is:

private string GetUrlFromTitle(XElement element, string title)
{
    return element.Descendants(ns + "link")
            ?.FirstOrDefault(e1 => e1.Attribute("title")?.Value == title)
            ?.Attribute("href")?.Value;
}

The third access method is using the Soap service that Sharepoint makes available. This access method is listed as deprecated, but it’s still alive. You have to create a reference to the http://<website>/_vti_bin/Lists.asmx and create a WCF client for it. I preferred to create a .NET 2.0 Client instead of a WCF service, as I found easier to authenticate with this service.

In Visual Studio, right-click the References node and select the Add Service Reference. Then, click on the Advanced button and then, Add Web Reference. Put the url in the box and click the arrow button:

When you click the Add Reference button, the reference will be added to the project and it can be used. Create a new class in the Repository folder and name it SoapListRepository. Make the class implement the IListRepository interface. The GetListsAsync method will be:

XNamespace ns = "http://schemas.microsoft.com/sharepoint/soap/";
private Lists _proxy;

public async Task<List<DocumentsList>> GetListsAsync()
{
    var tcs = new TaskCompletionSource<XmlNode>();
    _proxy = new Lists
    {
        Url = _address,
        UseDefaultCredentials = true
    };
    _proxy.GetListCollectionCompleted += ProxyGetListCollectionCompleted;
    _proxy.GetListCollectionAsync(tcs);
    XmlNode response;
    try
    {
        response = await tcs.Task;
    }
    finally
    {
        _proxy.GetListCollectionCompleted -= ProxyGetListCollectionCompleted;
    }

    var list = XElement.Parse(response.OuterXml);
    var result = list?.Descendants(ns + "List")
        ?.Where(e => e.Attribute("Hidden").Value == "False")
        ?.Select(e => new DocumentsList(e.Attribute("Title").Value,
        e.Attribute("Description").Value)).ToList();
    return result;
}

private void ProxyGetListCollectionCompleted(object sender, GetListCollectionCompletedEventArgs e)
{
    var tcs = (TaskCompletionSource<XmlNode>)e.UserState;
    if (e.Cancelled)
    {
        tcs.TrySetCanceled();
    }
    else if (e.Error != null)
    {
        tcs.TrySetException(e.Error);
    }
    else
    {
        tcs.TrySetResult(e.Result);
    }
}

As we are using the .NET 2.0 web service, in order to convert the call to an asynchronous method, we must use a TaskCompletionSource to detect when the call to the service returns. Then we fire the call to the service. When it returns, the completed event is called and sets the TaskCompletionSource to the desired state: cancelled, if the call was cancelled, exception, if there was an error or set the result if the call succeeds. Then, we remove the event handler for the completed event and process the result (a XmlNode), to transform into a list of DocumentsList.

The call to GetDocumentsFromListAsync is very similar to GetListsAsync:

XNamespace rs = "urn:schemas-microsoft-com:rowset";
XNamespace z = "#RowsetSchema";

public async Task<List<Document>> GetDocumentsFromListAsync(string title)
{
    var tcs = new TaskCompletionSource<XmlNode>();
    _proxy = new Lists
    {
        Url = _address,
        UseDefaultCredentials = true
    };
    _proxy.GetListItemsCompleted += ProxyGetListItemsCompleted;
    _proxy.GetListItemsAsync(title, "", null, null, "", null, "", tcs);
    XmlNode response;
    try
    {
        response = await tcs.Task;
    }
    finally
    {
        _proxy.GetListItemsCompleted -= ProxyGetListItemsCompleted;
    }

    var list = XElement.Parse(response.OuterXml);

    var result = list?.Element(rs + "data").Descendants(z + "row")
        ?.Select(e => new Document(e.Attribute("ows_ID")?.Value,
        e.Attribute("ows_LinkFilename")?.Value, AttributesToDictionary(e),
        e.Attribute("ows_FileRef")?.Value)).ToList();
    return result;
}

private Dictionary<string, object> AttributesToDictionary(XElement e)
{
    return e.Attributes().ToDictionary(a => a.Name.ToString().Replace("ows_", ""), a => (object)a.Value);
}

private void ProxyGetListItemsCompleted(object sender, GetListItemsCompletedEventArgs e)
{
    var tcs = (TaskCompletionSource<XmlNode>)e.UserState;
    if (e.Cancelled)
    {
        tcs.TrySetCanceled();
    }
    else if (e.Error != null)
    {
        tcs.TrySetException(e.Error);
    }
    else
    {
        tcs.TrySetResult(e.Result);
    }
}

The main difference is the processing of the response, to get the documents list. Once you have the two methods in place, the only thing to do is select the correct repository in MainViewModel. For that, we create an enum for the API Selection:

public enum ApiSelection
{
    NetApi,
    Rest,
    Soap
};

Then, we need to declare a command bound to the radiobuttons, that will receive a string with the enum value:

public ICommand ApiSelectCommand =>
    _apiSelectCommand ?? (_apiSelectCommand = new RelayCommand<string>(s => SelectApi(s)));

private void SelectApi(string s)
{
    _selectedApi = (ApiSelection)Enum.Parse(typeof(ApiSelection), s, true);
    GoToAddress();
}

The last step is to select the repository in the GoToAddress method:

private async void GoToAddress()
{
    var sw = new Stopwatch();
    sw.Start();
    _listRepository = _selectedApi == ApiSelection.Rest ?
        (IListRepository)new RestListRepository(Address) :
        _selectedApi == ApiSelection.NetApi ?
        (IListRepository)new CsomListRepository(Address) :
        new SoapListRepository(Address);

    DocumentsLists = await _listRepository.GetListsAsync();
    ListTiming = $"Time to get lists: {sw.ElapsedMilliseconds}";
    ItemTiming = "";
}

With the code in place, you can run the app and see the data shown for each API.

One last change to the program is to add a command bound to the Go button, so you can change the address of the web site and get the lists and documents for the new site:

public ICommand GoCommand =>
            _goCommand ?? (_goCommand = new RelayCommand(GoToAddress, () => !string.IsNullOrEmpty(Address)));

This command has an extra touch: it will only enable the button if there is an address in the address box. If it’s empty, the button will be disabled. Now you can run the program, change the address of the website, and get the lists for the new website.

Conclusions

As you can see, we’ve created a WPF program that uses the MVVM pattern and accesses Sharepoint data using three different methods – it even has a time measuring feature, so you can check the performance difference and choose the right one for your case.

The full source code for this project is at https://github.com/bsonnino/SharepointAccess