March 2015

Volume 30 Number 3


.NET Core Framework - Go Cross-Platform with the .NET Framework

By Cheryl Simmons | March 2015

Most developers prefer to write their business logic code once and reuse it later. This is a much easier approach than building different apps to target multiple platforms. Recent announcements about .NET Core—a componentized version of the Microsoft .NET Framework—and the close partnership of Microsoft and Xamarin mean that if you create Portable Class Libraries (PCLs) compatible with .NET Core, you’re closer to this reality than you’ve ever been.

What about your existing .NET Framework libraries, though? How much work will it take to make these compatible across platforms and convert them to PCLs? Enter the .NET Portability Analyzer. Using a couple of simple techniques and making some project file changes, this could help ease the process.

The .NET Portability Analyzer tool is a Visual Studio extension created by the .NET Framework team. You can use it with any recent version of Visual Studio that supports extensions. Simply point the Portability Analyzer at your assemblies or projects and the tool provides a summary, detailed report and recommendations about the APIs you should use to increase compatibility. For projects, the tool lists error messages, and takes you to the lines of code you need to change. The tool also provides results for key Microsoft platforms, and you can configure it to provide results for other platforms such as Mono and Xamarin.

The .NET Portability Analyzer has a console app sibling called the API Portability Analyzer (you can download it at aka.ms/w1vcle) that generates results similar to what the Portability Analyzer generates. For this article, I’ll focus on using the Visual Studio extension. It’s also important to note there might be some updates to the .NET Portability Analyzer extension between when this article was written and its publish date, so the appearance of the tool might differ from the images you see here.

Get Set for Success

For a library to be successfully taken across platforms, it should be well factored and contain mostly business logic. The UI code should be separated into other projects. However, because .NET Core is a subset of the .NET Framework, even if your code is well factored, your libraries might be using APIs that aren’t supported in .NET Core.

There are alternative APIs in some cases that accomplish the same thing. In these cases, the Portability Analyzer will suggest an alternative API. In other cases, there’s no substitute and you’ll need to factor out platform-specific code. Finally, even if you have no idea how well an assembly is factored, you can use Portability Analyzer to perform a quick assessment.

To use the extension, you’ll need Visual Studio 2013 or 2014. The next step is to install the extension. You can find it by searching for .NET Portability Analyzer in the Visual Studio Gallery or go directly to aka.ms/lah74y.

Click the Download button and choose Open. The next dialog lets you choose the version of Visual Studio to which you want to apply the extension. Click Install, which starts the installation and then Close to dismiss the dialog. Now you’re ready to choose your target platforms and analyze assemblies or projects.

Choose Your Target Platforms

The Portability Analyzer provides results for the .NET Framework, ASP.NET vNext (aka .NET Core), Windows and Windows Phone, by default. You can specify additional options by accessing the .NET Portability Analyzer entry from the Tools | Options menu in Visual Studio and selecting the set of platforms you’d like to target, as shown in Figure 1.

Select the Target Platforms for Your Project
Figure 1 Select the Target Platforms for Your Project

Run the Portability Analyzer

There are two ways you can analyze your assemblies and projects:

  • To analyze already built assemblies or executable files, access the Portability Analyzer from the Analyze menu in Visual Studio and browse to the assembly location. With this option, the tool generates a summary and detailed report.
  • To analyze projects, right-click the target project in Solution Explorer. Choose Analyze | Analyze Assembly Portability (see Figure 2), which is specific to the project you selected. With this option, the tool generates a summary, a detailed report, and outputs a message to the Error List that provides a file name and line number where the issue occurs. You can also double-click each message and the tool navigates to the specified line of code.

Selecting Analyze Assembly Portability for a Specific Project
Figure 2 Selecting Analyze Assembly Portability for a Specific Project

To test the tool, I opened a project I had worked on about a year ago—a word game that targets Windows Phone Silverlight 8. I started with my business logic in a Portable Class Library (PCL) targeting Windows Phone 8 and Windows 8. The idea was to reuse the library to implement the same app for Windows. However, I ran into some issues and was in a hurry, so I changed my approach and my library now targets only Windows Phone Silverlight 8.

My plan is to analyze the library’s portability, make the necessary code changes, and then convert the project to a PCL project and retarget it at Windows 8.1, Windows Phone 8.1, and Windows Phone Silverlight 8.1. I also want to test its compatibility with .NET Core. The Portability Analyzer gives me a peek at the work I need to do without actually converting the project, changing the targets and trying to sort out the compile errors. I’m also curious to see if I can use my library to build an Android app, so I’ve configured the tool to provide results for Xamarin.Android, as well.

I run the tool and the results are encouraging. Figure 3 shows the summary, detailed report, error messages and report URL. According to the summary, I can see my library is pretty compatible across all of the platforms. It’s 100 percent compatible with Windows Phone Silverlight 8.1, which isn’t surprising considering Windows Phone Silverlight 8 was the original target.

The Detailed Compatibility Report Showing Compatible Platforms
Figure 3 The Detailed Compatibility Report Showing Compatible Platforms

The detailed results are a spreadsheet-like display of only the APIs that aren’t supported by one or more of my target platforms. The details are easy to scan. They’re marked with a red X to indi­cate where an API isn’t supported and a green checkmark to indicate support. It’s important to note APIs supported across all of the platforms, and therefore not requiring any refactoring, won’t be listed in this report.

The details also include a recommended changes column, which points me to alternative APIs that will work across multiple platforms. At the bottom of the details, the report includes a Back to Summary link. This navigates back to the summary at the top. My results are very short, but for longer reports, this “return to top” functionality is useful.

Because I’ve analyzed a project, my report contains Error List messages that indicate the file and line number where the usage occurs. If I click the message, the tool takes me to the file and line indicated by the message.

If you want to access the results outside Visual Studio, they’re stored in an HTML file (ApiPortability­Analysis.htm) located in the same project directory as the target assembly. The location is indi­cated in the URL section at the top of the report, as shown in Figure 4.

Portability Analysis Results Stored for Access Outside Visual Studio
Figure 4 Portability Analysis Results Stored for Access Outside Visual Studio

Work in Progress

The .NET Portability Analyzer, its results and guidance are expected to change as the .NET team gathers more information (see Figure 5). The team collects anonymous data about API usage when you use the tool or its console app counterpart and it’s summarized at bit.ly/14vciaD.

This Site Shows .NET API Usage Levels
Figure 5 This Site Shows .NET API Usage Levels

This site defaults to show you the APIs that require the most code changes. You can change this by changing the value in the Show dropdown (not shown in the image). You can also hover over the “R” or “S” icons and see the same code recommendations shown by Portability Analyzer. 

Each API name in the list is a link that takes you to a page reporting on which platforms that API is supported. The team plans to continue to update the site and will hopefully open it to customer contributions, so check back periodically.

Take the Library Cross-Platform

Now it’s time to fix my library. I know I want my library to work with Windows Phone 8.1 and Windows 8.1, and I see some issues. I’ll take a different approach for each of the issues.

Use Platform Abstraction There are two resource stream-related entries. According to the report, these APIs aren’t supported on Windows 8.1 or Windows Phone 8.1. The tool doesn’t recommend any API I can substitute, so I need to remove this code from my library. The report tells me these two members are used in the same few lines of code, which load a static dictionary file:

WordList = new ObservableCollection<string>();
StreamResourceInfo info =
  Application.GetResourceStream(new
  Uri("/WordLibWP;component/US.dic", UriKind.Relative));
StreamReader reader = new StreamReader(info.Stream);
string line;
while ((line = reader.ReadLine()) != null)
{
  WordList.Add(line);
}
reader.close();

I know I can use the Windows.Storage APIs on Windows 8.1 and Windows Phone 8.1 to load a resource file, but then my library wouldn’t be compatible with Windows Phone Silverlight. For the broadest reach, I decide to abstract this bit of platform-specific code. Platform abstraction is a useful technique for providing a library that defines behavior, but implementing that behavior differently in the platform-specific code. If you want your code to be compatible across platforms, it’s a technique with which you should be familiar.

Here are the basic steps I took to perform this:

  • Define the behavior in the cross-platform library: To do this, I create an abstract class in the the library that defines a method for reading the dictionary file. I also defined a property that’s an instance of my DictionaryReader class. Something like this:
public abstract class DictionaryReader
{
  public abstract Task<ObservableCollection<string>>
    ReadDictionaryAsync(string path);
  public static DictionaryReader Instance { get; set; }
}
  • Implement the behavior in the platform-specific code: To do this, I derive from the DictionaryReader class in the Windows Phone Silverlight project. I provide an implementation of the ReadDictionaryAsync method that loads and reads the dictionary file as an app resource. Notice this code is essentially the same as the code in my library, with some error checking on the resource path as I need a specific format for my phone code to work. My implementation for other platforms will be different depending on the techniques for reading an app-local resource for those platforms. However, with the abstraction I’ve added—as shown in Figure 6—this shouldn’t be an issue.
  • Initialize the library-defined instance to the platform-­specific implementation: To do this, I add code to the App class for my Windows Phone project. This code initializes the instance of the DictionaryReader to my phone-specific implementation that reads the file as a resource. Again, this code is in my platform-specific project, and not in the project I analyzed:
public App()
{
  DictionaryReader.Instance = new DictionaryReaderPhone();
...
}

Figure 6 Phone Implementation to Load and Read Dictionary

class DictionaryReaderPhone : DictionaryReader
{
  public override async Task<ObservableCollection<string>>
  ReadDictionaryAsync(string resourcePath)
{
  ObservableCollection<string> wordList =
    new ObservableCollection<string>();
  StreamResourceInfo info =
    Application.GetResourceStream(new Uri(resourcePath,
    UriKind.Relative));
  if (info == null)
    throw new ArgumentException("Could not load resource: " +
      resourcePath +
      ". For Windows Phone this should be in the
      Format:[project];component/[filename]");
  else
  {
    StreamReader reader = new StreamReader(info.Stream);
    string line;
    while ((line = await reader.ReadLineAsync()) != null)
    {
    wordList.Add(line);
    }
    reader.Close();
    return wordList;
    }
  }
}

For another example that shows how to abstract out platform-­specific code, see the MSDN Library article, “Platform Abstraction with the Portable Class Library,” at aka.ms/q4sq7l.

Use a Different API Next, I evaluate the entry for getting the name of the current culture. I double-click the error message and find this method:

public string CheckLanguage()
{
  return Thread.CurrentThread.CurrentCulture.Name.Split('-')[0];
}

According to Portability Analyzer, I can use CultureInfo.CurrentCulture. Because CurrentCulture is a static property of CultureInfo and provides the same information I was getting from the current thread, it should work just fine. I replace the code that relies on getting Thread class with the following code, which uses CultureInfo:

public string CheckLanguage()
{
  return System.Globalization.CultureInfo.CurrentCulture.Name.Split('-')[0];
}

So Far, So Good

I test my changes and everything looks good. Next, I rerun Portability Analyzer. My report is clean, with only a few code changes, and I’ve picked up support for Xamarin.Android in addition to my original platform targets, as shown in Figure 7.

Rerun the Analysis Report After Code Changes
Figure 7 Rerun the Analysis Report After Code Changes

The final step for my cross-platform conversion is to convert the library project from a phone-specific library project to a PCL I can reference from multiple apps. Initially, I thought the easiest and quickest way to do this would be to do an “in-place” conversion by manually modifying the project file.

After doing some research and trying some of the steps I found, I came to the conclusion there are no discernible advantages to modifying the project file manually. I double-checked with a member of the .NET team and he agreed. Much of the help you’ll find for manually modifying a project assumes a certain starting point.

Depending on your project history and any other upgrades you’ve done, you could end up with a broken project. It’s likely you’ll need to create a new PCL project after spending time trying to manually convert it. Also, even if your manually converted project works initially, it could break if you make other changes later.

As a result, I created a new PCL project, and recommend you do the same. I copied my code files over to the new project. Once I copied the code, I got a few compile errors because of using statements that no longer applied to the new PCL. I removed these and my library was ready to go. I switched my Windows Phone Silverlight app to reference my new PCL and my app was up and running again.

Try It Out

Using Portability Analyzer not only helped me quickly evaluate the work I needed to do to make my library cross-platform, but identi­fied any platform-specific issues within my code down to method calls and property usage. It also made API suggestions where alternatives were available.

I’ve also shown you techniques for factoring out platform-specific code. My new Portability Analyzer results indicate my library will run on the additional platforms I wanted to target, as well as Xamarin.Android. Finally, I was able to create a new PCL with the new targets and reference this PCL from my existing app. The new .NET Portability Analyzer will help you take your libraries to new platforms.


Cheryl Simmons has been a programming writer at Microsoft for 10 years and has written about the .NET Base Class Libraries, Windows Forms, Windows Presentation Foundation, Silverlight, Windows Phone and related tools.

Thanks to the following Microsoft technical experts for reviewing this article: Matt Galbraith, Daniel Plaisted and Taylor Southwick