Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / portable

How to Convert a Portable Class Library (PCL) to .NET Standard

3.40/5 (2 votes)
23 Oct 2017CPOL4 min read 15.7K  
Convert a portable class library (PCL) to a .NET Standard project, change the csproj file to the new format

Introduction

In this tip, I will show you how I could convert my old csproj file into the new version of csproj. I will also tell you what kind of failures and stumbling blocks you can get on the way.

Before You Begin

If you use some 3rd-party DLLs, you have to ensure that they support the chosen netstandard version. It is unusual but possible that one of them may support PCL but not netstandard. If they are installed through NuGet, you can check them by NuGet. If they are coming from an other provider, you have to check it yourself.

The Original csproj

The original csproj file looks like:

XML
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" 
 xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" 
   Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <MinimumVisualStudioVersion>10.0</MinimumVisualStudioVersion>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{53561B15-7A94-4375-B9F5-4615EF3E6DB6}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>***********</RootNamespace>
    <AssemblyName>************</AssemblyName>
    <DefaultLanguage>en-US</DefaultLanguage>
    <FileAlignment>512</FileAlignment>
    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};
     {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
    <TargetFrameworkProfile>Profile44</TargetFrameworkProfile>
    <TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
    <SccProjectName>SAK</SccProjectName>
    <SccLocalPath>SAK</SccLocalPath>
    <SccAuxPath>SAK</SccAuxPath>
    <SccProvider>SAK</SccProvider>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="Common\CloseRequest.cs" />
    <Compile Include="Common\IAsyncOperations.cs" />

    .............

  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="***************">
      <Project>{4C9A9142-43F2-4FDB-B041-C073AED6C4D6}</Project>
      <Name>*****************</Name>
    </ProjectReference>
    .............
  </ItemGroup>
  <ItemGroup>
    <Folder Include="Events\" />
  </ItemGroup>
  <ItemGroup>
    <None Include="packages.config" />
  </ItemGroup>
  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\
   Microsoft.Portable.CSharp.targets" />
  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
       Other similar extension points exist, see Microsoft.Common.targets.
  <Target Name="BeforeBuild">
  </Target>
  <Target Name="AfterBuild">
  </Target>
  -->
</Project>

As you can see, it is a portable class library, which targets the profile44.

The New csproj

The new csproj file is really simple:

XML
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <SccProjectName>SAK</SccProjectName>
    <SccLocalPath>SAK</SccLocalPath>
    <SccAuxPath>SAK</SccAuxPath>
    <SccProvider>SAK</SccProvider>
  </PropertyGroup>
  <PropertyGroup>
    <TargetFramework>netstandard1.6</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\DS.Publications.Common\DS.Publications.Common.csproj"/>
  </ItemGroup>
  
</Project>

Now we are targeting .NET-Standard 1.6!

So you have to replace everything until the ItemGroup section with the ProjectReference entries in it with this:

XML
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <SccProjectName>SAK</SccProjectName>
    <SccLocalPath>SAK</SccLocalPath>
    <SccAuxPath>SAK</SccAuxPath>
    <SccProvider>SAK</SccProvider>
  </PropertyGroup>
  <PropertyGroup>
    <TargetFramework>netstandard1.6</TargetFramework>
  </PropertyGroup>

Note that the files are not listed in the csproj any more.

In my last projects, I am using TFS as source control, so I need these SAK (Should Already Know) lines, too. If you use an other source code control, you can delete those lines, too.

Then, you can simplify the ProjectReference entries. Name and the GUID are unnecessary.

XML
<ProjectReference Include="..\DS.Publications.Common\DS.Publications.Common.csproj"/>

Embedded Resources

Do not delete ItemGroup-s with EmbeddedResource or Content!

XML
<ItemGroup>
  <EmbeddedResource Include="Resources\Welle320x480.png" />
</ItemGroup>

<ItemGroup>
  <Content Include="ConverterExtensions.tt">
    <Generator>TextTemplatingFileGenerator</Generator>
    <LastGenOutput>ConverterExtensions.cs</LastGenOutput>
  </Content>
</ItemGroup>

If you delete these lines, the png file will still be there in the project, but it is not an embedded resource any more, and the T4-Template is not a Content any more. But do not worry, you can set these properties in Visual Studio again.

The NuGet Packages

It is recommended that you reinstall your NuGet packages. So take a look at your old packages.config file what entries you had before. Notice them and delete the packages.config file. The new csproj format can store NuGet-Entries in the csproj file itself.

So I had the following entries:

XML
<packages>
  <package id="CommonServiceLocator" version="1.3" 
  targetFramework="portable45-net45+win8" />
  <package id="MvvmLightLibs" version="5.3.0.0" 
  targetFramework="portable45-net45+win8" />
</packages>

After I deleted my packages.config, I open my solution and install the NuGet packages again. Visual Studio will ask you where the NuGet-entries should be stored. Choose the option for the csproj.

Other References

If you had other 3rd party DLLs referenced, you have to keep those lines also, but you can simplify them.

XML
<Reference Include="SuperThirdParty.dll">
    <HintPath>..\Dependencies\netstandard1.6\SuperThirdParty.dll</HintPath>
</Reference>

Or you can notice them and assign them again in Visual Studio.

Test Projects

You can also convert a testproject to the new format. But it cannot target netstandard1.6. It has to target netcoreapp1.1. Then it can reference the netstandard1.6 assemblies.

AssemblyInfo.cs Problem

I suppose you have had an AssemblyInfo.cs file in your old project. The new csproj format works another way, and it does not need any assemblyinfo.cs. The assembly's properties are stored in the csproj file. There are some default values, and the AssemblyInfo.cs will be generated at compile time. So if you also have your original AssemblyInfo.cs, you get errors at compile, because you have the properties twice. E.g.:

error CS0579: Duplicate 'AssemblyVersion' attribute

To solve this problem, you can choose one of the following:

  • Delete your original AssemblyInfo.cs and set the properties manually on the project-properties page
  • or you can keep the original AssemblyInfo.cs and disable the automatic generation in the csproj file with:
XML
<PropertyGroup>
   <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>

The Other Projects

Normally, you have some other projects, which reference your new netstandard projects. In those projects, you will need an entry.

XML
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>

If you do not have this entry, you can get some failures only at runtime. For example:

Unhandled Exception: System.IO.FileLoadException: Could not load file or assembly 
'System.Runtime.InteropServices.RuntimeInformation, Version=4.0.1.0, Culture=neutral, 
PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. 
The located assembly's manifest definition does not match the assembly reference. 
(Exception from HRESULT: 0x80131040)

Older csproj files do not have this tag, you have to insert it manually! Keep it in mind, if you update your existing projects.

See this link.

References of the Referenced Assemblies

There is still a bug in Visual Studio: the references of the referenced netstandard projects will not be copied to the output folder. You have to explicitly reference them! At least in your main / startup project. Otherwise, you get exceptions only at runtime, that a DLL cannot be found.

It is recommended that you install the NuGet package "NetStandard Library" in your normal project too.

netstandard1.3

I've tried to use a netstandard1.6 library in a WPF project. I've always got some errors in the XAML designer. I've got the experience, that WPF can only work with netstandard1.3 libraries.

See this link.

Thank you for reading!

I wonder what kind of experiences you get by converting your projects. Do not hesitate to write them in the comments.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)