Pages

20 August, 2021

Sitecore Upgrade - Codebase Upgrade - Migrate to SDK Project Format

The older style of csproj has lot of cumbersome tasks like adding files, excluding files, complex in adding references and packages.config file maintenance. 

The new SDK project format was introduced along with Visual Studio 2017. Some of the pros includes:

  • Clean csproj file with the ability to modify the .NET framework or .NET core version
  • Ability to view/edit the csproj file without excluding the project in Visual Studio
  • No need of Packages.json file and include the nuget references as part of csproj file itself
  • No need to include each and every .cs file to the project. 
A typical old csproj format file: You will see so many lines which could have been defaulted somewhere else. 
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="..\..\..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\build\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props" Condition="Exists('..\..\..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\build\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props')" />
  <Import Project="..\..\..\packages\Microsoft.Net.Compilers.1.0.0\build\Microsoft.Net.Compilers.props" Condition="Exists('..\..\..\packages\Microsoft.Net.Compilers.1.0.0\build\Microsoft.Net.Compilers.props')" />
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProductVersion>
    </ProductVersion>
    <SchemaVersion>2.0</SchemaVersion>
    <ProjectGuid>{30EB7022-CCB9-48AB-96C1-3556565A51C1}</ProjectGuid>
    <ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>SitecoreDemo.Feature.Accounts</RootNamespace>
    <AssemblyName>SitecoreDemo.Feature.Accounts</AssemblyName>
    <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
    <UseIISExpress>true</UseIISExpress>
    <IISExpressSSLPort />
    <IISExpressAnonymousAuthentication />
    <IISExpressWindowsAuthentication />
    <IISExpressUseClassicPipelineMode />
    <UseGlobalApplicationHostFile />
    <SccProjectName>SAK</SccProjectName>
    <SccLocalPath>SAK</SccLocalPath>
    <SccAuxPath>SAK</SccAuxPath>
    <SccProvider>SAK</SccProvider>
    <NuGetPackageImportStamp>
    </NuGetPackageImportStamp>
    <Use64BitIISExpress />
    <TargetFrameworkProfile />
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\</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\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="Microsoft.AI.Agent.Intercept, Version=1.2.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
      <HintPath>..\..\..\packages\Microsoft.ApplicationInsights.Agent.Intercept.1.2.1\lib\net45\Microsoft.AI.Agent.Intercept.dll</HintPath>
      <Private>True</Private>
    </Reference>
    <Reference Include="Microsoft.AI.DependencyCollector, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
      <HintPath>..\..\..\packages\Microsoft.ApplicationInsights.DependencyCollector.2.1.0\lib\net45\Microsoft.AI.DependencyCollector.dll</HintPath>
      <Private>True</Private>
    </Reference>
    <Reference Include="Microsoft.AI.PerfCounterCollector, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
      <HintPath>..\..\..\packages\Microsoft.ApplicationInsights.PerfCounterCollector.2.1.0\lib\net45\Microsoft.AI.PerfCounterCollector.dll</HintPath>
      <Private>True</Private>
    </Reference>
    <Reference Include="Microsoft.AI.ServerTelemetryChannel, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
      <HintPath>..\..\..\packages\Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel.2.1.0\lib\net45\Microsoft.AI.ServerTelemetryChannel.dll</HintPath>
      <Private>True</Private>
    </Reference>
    <Reference Include="Microsoft.AI.Web, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
      <HintPath>..\..\..\packages\Microsoft.ApplicationInsights.Web.2.1.0\lib\net45\Microsoft.AI.Web.dll</HintPath>
      <Private>True</Private>
    </Reference>
    <Reference Include="Microsoft.AI.WindowsServer, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
      <HintPath>..\..\..\packages\Microsoft.ApplicationInsights.WindowsServer.2.1.0\lib\net45\Microsoft.AI.WindowsServer.dll</HintPath>
      <Private>True</Private>
    </Reference>
    <Reference Include="Microsoft.ApplicationInsights, Version=2.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
      <HintPath>..\..\..\packages\Microsoft.ApplicationInsights.2.1.0\lib\net46\Microsoft.ApplicationInsights.dll</HintPath>
      <Private>True</Private>
    </Reference>
    <Reference Include="Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
      <HintPath>..\..\..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll</HintPath>
      <Private>True</Private>
    </Reference>
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
      <HintPath>..\..\..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
      <Private>True</Private>
    </Reference>
    <Reference Include="RestSharp, Version=105.2.3.0, Culture=neutral, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>..\..\..\packages\RestSharp.105.2.3\lib\net46\RestSharp.dll</HintPath>
    </Reference>
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Runtime.Serialization" />
    <Reference Include="System.Web.DynamicData" />
    <Reference Include="System.Web.Entity" />
    <Reference Include="System.Web.ApplicationServices" />
    <Reference Include="System.ComponentModel.DataAnnotations" />
    <Reference Include="System" />
    <Reference Include="System.Data" />
    <Reference Include="System.Core" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="System.Web.Extensions" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Drawing" />
    <Reference Include="System.Web" />
    <Reference Include="System.Xml" />
    <Reference Include="System.Configuration" />
    <Reference Include="System.Web.Services" />
    <Reference Include="System.EnterpriseServices" />
  </ItemGroup>
  <ItemGroup>
    <Reference Include="System.Web.Razor">
      <HintPath>..\..\..\packages\Microsoft.AspNet.Razor.3.2.3\lib\net45\System.Web.Razor.dll</HintPath>
    </Reference>
    <Reference Include="System.Web.Webpages">
      <HintPath>..\..\..\packages\Microsoft.AspNet.Webpages.3.2.3\lib\net45\System.Web.Webpages.dll</HintPath>
    </Reference>
    <Reference Include="System.Web.Webpages.Deployment">
      <HintPath>..\..\..\packages\Microsoft.AspNet.Webpages.3.2.3\lib\net45\System.Web.Webpages.Deployment.dll</HintPath>
    </Reference>
    <Reference Include="System.Web.Webpages.Razor">
      <HintPath>..\..\..\packages\Microsoft.AspNet.Webpages.3.2.3\lib\net45\System.Web.Webpages.Razor.dll</HintPath>
    </Reference>
    <Reference Include="System.Web.Helpers">
      <HintPath>..\..\..\packages\Microsoft.AspNet.Webpages.3.2.3\lib\net45\System.Web.Helpers.dll</HintPath>
    </Reference>
    <Reference Include="Microsoft.Web.Infrastructure">
      <HintPath>..\..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
    </Reference>
    <Reference Include="System.Web.Mvc">
      <HintPath>..\..\..\packages\Microsoft.AspNet.Mvc.5.2.3\lib\net45\System.Web.Mvc.dll</HintPath>
    </Reference>
  </ItemGroup>
  <ItemGroup>
    <Content Include="Views\ChangePassword\_ChangePassword.cshtml" />
    <Content Include="Views\ChangePassword\_ChangePasswordEdit.cshtml" />
    <Content Include="Views\Common\_SiteDownAlert.cshtml" />    
    <Content Include="Views\Common\_WelcomeBanner.cshtml" />
    <Content Include="Views\Notification\Preference.cshtml" />
    <Content Include="Views\Notification\UserInfo.cshtml" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Attribute\SessionExpireFilterAttribute.cs" />    
    <Compile Include="BusinessLogic\ChangePassword.cs" />
    <Compile Include="Controllers\ChangePasswordController.cs" />
    <Compile Include="Constants.cs" />
    <Compile Include="Controllers\BaseController.cs" />
    <Compile Include="Controllers\NotificationController.cs" />
    <Compile Include="Controllers\ToolsController.cs" />
    <Compile Include="Helper\EnumHelper.cs" />
    <Compile Include="Models\Customer.cs" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\Marketing\Marketing.csproj">
      <Project>{1117786e-0ae1-44b0-af00-1ec3adc173ee}</Project>
      <Name>Marketing</Name>
    </ProjectReference>
  </ItemGroup>
  <ItemGroup>
    <Content Include="web.config" />
    <None Include="web.Debug.config">
      <DependentUpon>web.config</DependentUpon>
    </None>
  </ItemGroup>
  <PropertyGroup>
    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
    <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'DEV|AnyCPU'">
    <OutputPath>bin\</OutputPath>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PROD|AnyCPU'">
    <OutputPath>bin\</OutputPath>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'UAT|AnyCPU'">
    <OutputPath>bin\</OutputPath>
  </PropertyGroup>
  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
  <Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" Condition="false" />
  <ProjectExtensions>
    <VisualStudio>
      <FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
        <WebProjectProperties>
          <UseIIS>False</UseIIS>
          <AutoAssignPort>True</AutoAssignPort>
          <DevelopmentServerPort>58420</DevelopmentServerPort>
          <DevelopmentServerVPath>/</DevelopmentServerVPath>
          <IISUrl>http://localhost:57567/</IISUrl>
          <NTLMAuthentication>False</NTLMAuthentication>
          <UseCustomServer>False</UseCustomServer>
          <CustomServerUrl>
          </CustomServerUrl>
          <SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
        </WebProjectProperties>
      </FlavorProperties>
    </VisualStudio>
  </ProjectExtensions>
  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
    <PropertyGroup>
      <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
    </PropertyGroup>
    <Error Condition="!Exists('..\..\..\packages\Microsoft.Net.Compilers.1.0.0\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.Net.Compilers.1.0.0\build\Microsoft.Net.Compilers.props'))" />
    <Error Condition="!Exists('..\..\..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\build\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\build\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props'))" />
  </Target>
  <!-- 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>

Migrated to SDK format: A simple SDL format csproj file. Readable and easily modifiable. 
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net48</TargetFramework>
	<ApplicationIcon />
	<OutputType>Library</OutputType>
	<StartupObject />
	<Authors>Author</Authors>
	<Company>Company Name</Company>
	<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
	<CodeAnalysisRuleSet>$(SolutionDir)\StyleCop.ruleset</CodeAnalysisRuleSet>
  </PropertyGroup> 
  <ItemGroup>
    <Content Include="Views\ChangePassword\_ChangePassword.cshtml" />
    <Content Include="Views\ChangePassword\_ChangePasswordEdit.cshtml" />
    <Content Include="Views\Common\_SiteDownAlert.cshtml" />    
    <Content Include="Views\Common\_WelcomeBanner.cshtml" />
    <Content Include="Views\Notification\Preference.cshtml" />
    <Content Include="Views\Notification\UserInfo.cshtml" />
    <Content Include="Web.config" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="Glass.Mapper.Sc.93.Core">
      <Version>5.6.160</Version>
    </PackageReference>
    <PackageReference Include="Sitecore.Mvc" Version="9.3.0" />
    <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
    <PackageReference Include="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" Version="3.6.0" />
    <PackageReference Include="Sitecore.Kernel" Version="9.3.0" />
    <PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>
  <ItemGroup>
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System" />
  </ItemGroup>
  <ItemGroup>
    <Folder Include="Models\ExtensionModels\" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\..\..\Foundation\Extensions\website\SitecoreDemo.Foundation.Extensions.csproj">
      <Project>{a53176ca-ff2g-4b2s-h6j3-1tfs2g94jfb5}</Project>
      <Name>SitecoreDemo.Foundation.Extensions</Name>
    </ProjectReference>
    <ProjectReference Include="..\..\..\Foundation\Marketing\website\SitecoreDemo.Foundation.Marketing.csproj" />
  </ItemGroup>
 </Project>

How to migrate:
There are few options available to migrate to SDK format.

Option 1:
An open source module which does most of the task for us. By simply calling the console app followed by our csproj file will convert most of the project files. We may need to clean up few reference related tasks which will discuss in the next post of the Upgrade series. 


Option 2:
Manually change the csproj file to SDK format. 
  1. Replace entire PropertyGroup with the simple SDK version.
    1. TargetFramework
    2. OutputType
    3. GenerateAssemblyInfo
  2. Remove all compiler related <Import />
  3. Remove all <ItemGroup /> containing .cs files
  4. Remove <ProjectExtensions /> and <Target />
Migrating from packages.json to PackageReferences will be explained in the next blog post. 

18 August, 2021

Sitecore Upgrade - Codebase Upgrade Series - Framework upgrade - .NET

As part of the Sitecore codebase upgrade series, in this blog, we are going to deal with upgrading .NET Framework version. 

Option 1: Using PowerShell, we can quickly update the version in all the csproj files. The script has been added in this blog. This script will update both legacy and SDK project format.

Option 2: To upgrade .NET framework, you can open the csproj files in Notepad++ and replace the version number from 4.x to 4.8 (or desired version as per Sitecore). 

  • SDK Style Projects
    Should replace <TargetFramework>net4xx</TargetFramework> with the targeted version. 
  • Legacy Style Projects
    Should replace <TargetFrameworkVersion>v4.x.x</TargetFrameworkVersion> with the targeted version. 

Option 3: There is a Visual Studio marketplace module called TargetFrameworkMigrator. Once you install it, you can change the target framework to the desired version. This will work only for the Legacy Style Projects. SDK Style Project support will be added in the next version as per the roadmap. 

  • Once you install the VS Extension. Open the extension from Tools menu --> Target Framework Migrator.
  • Choose the projects you want to migrate, then click Migrate. 


17 August, 2021

Update .NET Version in SDK and Legacy Project Format Type

As part of Sitecore Upgrade, we had to upgrade .NET version in 100+ Helix projects. The below script was used to update the version in the projects quickly. 

Since Microsoft introduced SDK project format, the XML tag in the project file and also the version format are different. The below updates .NET Target Framework version in both SDK and Legacy Project format types.