I made a Incremental Source Generator:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<IsRoslynComponent>true</IsRoslynComponent>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.2.0" />
<PackageReference Include="System.Text.Json" Version="6.0.0" />
</ItemGroup>
</Project>
It generates code that has an obvious syntax error
[Generator]
public class SourceGenerator : IIncrementalGenerator {
public void Initialize(IncrementalGeneratorInitializationContext context) {
context.RegisterSourceOutput(context.AdditionalTextsProvider, static (context, name) => {
string sourceCode = $#"
public class {"AAA"}{{
{{
}}
";
context.AddSource("AAA" + ".g.cs", sourceCode);
});
}
After building the solution for the first time and errors were expected. I restarted Visual Studio. I can see following code was generated:
public class AAA{
{
}
When viewing this AAA.g.cs, the text editor didn't highlight the fault of less closing braket.
Then I build the solution for the second time. It was succeeded. How can this be possible?
Related
I am trying to use Roslyn to compile a trivial project but it fails.
Consider the following setup (assuming c:\temp exists and you have .NET 6 installed):
mkdir c:\temp\TestLib
notepad c:\temp\TestLib\TestLib.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
notepad c:\temp\TestLib\SomeClass.cs
namespace TestLib
{
public class SomeClass
{
void DoThings()
{
Console.WriteLine("Things!");
}
}
}
cd c:\temp\TestLib
dotnet build
Result: Build succeeded
mkdir c:\temp\RoslynTrouble
notepad c:\temp\RoslynTrouble\RoslynTrouble.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build.Locator" Version="1.5.5" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.8.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="3.8.0" />
</ItemGroup>
</Project>
notepad c:\temp\RoslynTrouble\Program.cs
using Microsoft.Build.Locator;
using Microsoft.CodeAnalysis.MSBuild;
class TestProgram
{
public static async Task Main(string[] args)
{
string csprojPath = args[0];
var instance = MSBuildLocator.RegisterDefaults();
Console.WriteLine(instance.Name + ": " + instance.Version);
var workspace = MSBuildWorkspace.Create();
var project = await workspace.OpenProjectAsync(csprojPath);
var compilation = await project.GetCompilationAsync();
if (compilation == null)
{
Console.WriteLine("Error: unexpected null compilation");
return;
}
foreach (var diagnostic in compilation.GetDiagnostics())
{
Console.WriteLine(diagnostic);
}
}
}
cd c:\temp\RoslynTrouble
dotnet run c:\temp\TestLib\TestLib.csproj
Expected result: no errors
Actual result: lots of compilation errors:
.NET Core SDK: 6.0.203
c:\open\prototypes\TestLib\obj\Debug\net6.0\TestLib.GlobalUsings.g.cs(2,1): error CS0116: A namespace cannot directly contain members such as fields or methods
c:\open\prototypes\TestLib\obj\Debug\net6.0\TestLib.GlobalUsings.g.cs(3,1): error CS0116: A namespace cannot directly contain members such as fields or methods
c:\open\prototypes\TestLib\obj\Debug\net6.0\TestLib.GlobalUsings.g.cs(4,1): error CS0116: A namespace cannot directly contain members such as fields or methods
c:\open\prototypes\TestLib\obj\Debug\net6.0\TestLib.GlobalUsings.g.cs(5,1): error CS0116: A namespace cannot directly contain members such as fields or methods
c:\open\prototypes\TestLib\obj\Debug\net6.0\TestLib.GlobalUsings.g.cs(6,1): error CS0116: A namespace cannot directly contain members such as fields or methods
c:\open\prototypes\TestLib\obj\Debug\net6.0\TestLib.GlobalUsings.g.cs(7,1): error CS0116: A namespace cannot directly contain members such as fields or methods
c:\open\prototypes\TestLib\obj\Debug\net6.0\TestLib.GlobalUsings.g.cs(8,1): error CS0116: A namespace cannot directly contain members such as fields or methods
c:\open\prototypes\TestLib\SomeClass.cs(7,13): error CS0103: The name 'Console' does not exist in the current context
c:\open\prototypes\TestLib\obj\Debug\net6.0\TestLib.GlobalUsings.g.cs(2,8): hidden CS8019: Unnecessary using directive.
c:\open\prototypes\TestLib\obj\Debug\net6.0\TestLib.GlobalUsings.g.cs(8,8): hidden CS8019: Unnecessary using directive.
c:\open\prototypes\TestLib\obj\Debug\net6.0\TestLib.AssemblyInfo.cs(11,1): hidden CS8019: Unnecessary using directive.
c:\open\prototypes\TestLib\obj\Debug\net6.0\TestLib.GlobalUsings.g.cs(7,8): hidden CS8019: Unnecessary using directive.
c:\open\prototypes\TestLib\obj\Debug\net6.0\TestLib.GlobalUsings.g.cs(6,8): hidden CS8019: Unnecessary using directive.
c:\open\prototypes\TestLib\obj\Debug\net6.0\.NETCoreApp,Version=v6.0.AssemblyAttributes.cs(2,1): hidden CS8019: Unnecessary using directive.
c:\open\prototypes\TestLib\obj\Debug\net6.0\TestLib.GlobalUsings.g.cs(3,8): hidden CS8019: Unnecessary using directive.
c:\open\prototypes\TestLib\obj\Debug\net6.0\TestLib.AssemblyInfo.cs(12,1): hidden CS8019: Unnecessary using directive.
c:\open\prototypes\TestLib\obj\Debug\net6.0\TestLib.GlobalUsings.g.cs(4,8): hidden CS8019: Unnecessary using directive.
c:\open\prototypes\TestLib\obj\Debug\net6.0\TestLib.GlobalUsings.g.cs(5,8): hidden CS8019: Unnecessary using directive.
c:\open\prototypes\TestLib\obj\Debug\net6.0\.NETCoreApp,Version=v6.0.AssemblyAttributes.cs(3,1): hidden CS8019: Unnecessary using directive.
What am I missing and how can I fix those errors?
Below you'll find the modifications described in a Note. Try the following:
mkdir c:\temp\TestLib
notepad c:\temp\TestLib\TestLib.csproj
Click Yes
TestLib.csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
Save As (save with "UTF-8" encoding)
notepad c:\temp\TestLib\SomeClass.cs
Click Yes
SomeClass.cs:
Note: Made the method public - not much point in having a class with one method and making it private.
namespace TestLib
{
public class SomeClass
{
public void DoThings()
{
Console.WriteLine("Things!");
}
}
}
Save As (save with "UTF-8" encoding)
mkdir c:\temp\RoslynTrouble
notepad c:\temp\RoslynTrouble\RoslynTrouble.csproj
Click Yes
RoslynTrouble.csproj:
Note: Use version 4.4.0 for Microsoft.CodeAnalysis.CSharp.Workspaces and Microsoft.CodeAnalysis.Workspaces.MSBuild.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build.Locator" Version="1.5.5" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.4.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.4.0" />
</ItemGroup>
</Project>
Save As (save with "UTF-8" encoding)
notepad c:\temp\RoslynTrouble\Program.cs
Click Yes
Program.cs:
Note: Added namespace RoslynTrouble. Changed class name to Program to match the filename (Program.cs).
using Microsoft.Build.Locator;
using Microsoft.CodeAnalysis.MSBuild;
namespace RoslynTrouble
{
public class Program
{
public static async Task Main(string[] args)
{
string csprojPath = args[0];
var instance = MSBuildLocator.RegisterDefaults();
Console.WriteLine(instance.Name + ": " + instance.Version);
var workspace = MSBuildWorkspace.Create();
var project = await workspace.OpenProjectAsync(csprojPath);
var compilation = await project.GetCompilationAsync();
if (compilation == null)
{
Console.WriteLine("Error: unexpected null compilation");
return;
}
foreach (var diagnostic in compilation.GetDiagnostics())
{
Console.WriteLine(diagnostic);
}
}
}
}
Save As (save with "UTF-8" encoding)
cd c:\temp\RoslynTrouble
dotnet run "C:\temp\TestLib\TestLib.csproj"
Note: The double-quotes in the command above are optional since there aren't any spaces in the path.
Result:
Note: The result has warnings, but no errors.
.NET Core SDK: 6.0.301
C:\temp\TestLib\obj\Debug\net6.0\.NETCoreApp,Version=v6.0.AssemblyAttributes.cs(2,7): hidden CS8933: The using directive for 'System' appeared previously as global using
C:\temp\TestLib\obj\Debug\net6.0\TestLib.AssemblyInfo.cs(10,7): hidden CS8933: The using directive for 'System' appeared previously as global using
C:\temp\TestLib\obj\Debug\net6.0\TestLib.GlobalUsings.g.cs(5,1): hidden CS8019: Unnecessary using directive.
C:\temp\TestLib\obj\Debug\net6.0\TestLib.GlobalUsings.g.cs(3,1): hidden CS8019: Unnecessary using directive.
C:\temp\TestLib\obj\Debug\net6.0\TestLib.GlobalUsings.g.cs(4,1): hidden CS8019: Unnecessary using directive.
C:\temp\TestLib\obj\Debug\net6.0\TestLib.AssemblyInfo.cs(11,1): hidden CS8019: Unnecessary using directive.
C:\temp\TestLib\obj\Debug\net6.0\.NETCoreApp,Version=v6.0.AssemblyAttributes.cs(2,1): hidden CS8019: Unnecessary using directive.
C:\temp\TestLib\obj\Debug\net6.0\TestLib.GlobalUsings.g.cs(8,1): hidden CS8019: Unnecessary using directive.
C:\temp\TestLib\obj\Debug\net6.0\.NETCoreApp,Version=v6.0.AssemblyAttributes.cs(3,1): hidden CS8019: Unnecessary using directive.
C:\temp\TestLib\obj\Debug\net6.0\TestLib.AssemblyInfo.cs(10,1): hidden CS8019: Unnecessary using directive.
C:\temp\TestLib\obj\Debug\net6.0\TestLib.GlobalUsings.g.cs(7,1): hidden CS8019: Unnecessary using directive.
C:\temp\TestLib\obj\Debug\net6.0\TestLib.GlobalUsings.g.cs(6,1): hidden CS8019: Unnecessary using directive.
If one would like to avoid displaying warnings, one could use the following alternative code for Program.cs:
using Microsoft.Build.Locator;
using Microsoft.CodeAnalysis.MSBuild;
namespace RoslynTrouble
{
public class Program
{
public static async Task Main(string[] args)
{
bool errorsExist = false;
string csprojPath = args[0];
var instance = MSBuildLocator.RegisterDefaults();
Console.WriteLine(instance.Name + ": " + instance.Version);
var workspace = MSBuildWorkspace.Create();
var project = await workspace.OpenProjectAsync(csprojPath);
var compilation = await project.GetCompilationAsync();
if (compilation == null)
{
Console.WriteLine("Error: unexpected null compilation");
return;
}
foreach (Microsoft.CodeAnalysis.Diagnostic diagnostic in compilation.GetDiagnostics())
{
if (diagnostic.Severity != Microsoft.CodeAnalysis.DiagnosticSeverity.Hidden &&
diagnostic.Severity != Microsoft.CodeAnalysis.DiagnosticSeverity.Warning)
{
Console.WriteLine(diagnostic);
errorsExist = true;
}
}
if (!errorsExist)
Console.WriteLine($"Successfully compiled '{args[0]}'.");
}
}
}
Resources:
Roslyn Code Analysis returns false build errors from an error free solution
I have the following exception:
I was using .NET 6 (I still had the error) and thought that it could be some conflict with the nugets I am using in my solution. It turns out that even after updating to .NET 7 the error persists when I run the test. For testing I'm using MSTest Framework and using an inmemory database (sqlite) to make integration tests. The error is happening when executing the line await context.Database.EnsureCreatedAsync();. The test classes are the following:
public class SQLiteDatabaseContextFactory : IDisposable
{
private DbConnection _connection;
private DbContextOptions<DataContext> CreateOptions()
{
return new DbContextOptionsBuilder<DataContext>()
.UseSqlite(_connection).Options;
}
public DataContext CreateContext()
{
if (_connection == null)
{
_connection = new SqliteConnection("DataSource=:memory:");
_connection.Open();
var options = CreateOptions();
using var context = new DataContext(options);
context.Database.EnsureCreated();
}
return new DataContext(CreateOptions());
}
public void Dispose()
{
if (_connection != null)
{
_connection.Dispose();
_connection = null;
}
}
}
And:
[TestClass]
public class SQLiteIntegrationTests
{
[TestMethod]
public async Task TestMethod_UsingSqliteInMemoryProvider_Success()
{
using var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
var options = new DbContextOptionsBuilder<DataContext>()
.UseSqlite(connection) // Set the connection explicitly, so it won't be closed automatically by EF
.Options;
// Create the dabase schema
// You can use MigrateAsync if you use Migrations
using (var context = new DataContext(options))
{
//await context.Database.MigrateAsync();
await context.Database.EnsureCreatedAsync();
} // The connection is not closed, so the database still exists
using (var context = new DataContext(options))
{
var user = new ManualClassifier()
{
FirstName = "First",
LastName = "Last",
Email = "example#gmail.com",
Username = "firstlast123",
PasswordHash = "5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf",
PasswordSalt = "5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf"
};
context.ManualClassifiers.Add(user);
await context.SaveChangesAsync();
}
using (var context = new DataContext(options))
{
var count = await context.ManualClassifiers.CountAsync();
Assert.AreEqual(1, count);
var u = await context.ManualClassifiers.FirstOrDefaultAsync(user => user.Email == "example#gmail.com");
Assert.IsNotNull(u);
}
}
}
EDIT: The full error is the following:
The .csproj of the project where I'm running the tests:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.13" />
<PackageReference Include="MSTest.TestFramework" Version="3.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\VSC.Repo\VSC.Repo.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
<PackageReference Include="MSTest.TestAdapter" Version="2.1.1" />
<PackageReference Include="MSTest.TestFramework" Version="2.1.1" />
<PackageReference Include="coverlet.collector" Version="1.3.0" />
</ItemGroup>
</Project>
Dbcontext class library .csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<Folder Include="Services\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Configuration.ConfigurationManager" Version="7.0.0" />
</ItemGroup>
</Project>
Any help figuring what is happening would be much appreciated. I honestly have no clue what is causing this.
Looks a lot like package version mismatch (in my practice it is the most common source of such errors). Update Microsoft.EntityFrameworkCore.Sqlite to the latest 7th version (my guess is that EF project uses that version) to match major version of EF Core packages in the tested solution.
I've started migrating company owned application from UWP 10 to WinUI (on NET5) using upgrade-assistant tool. Give or take, the most conflicts are gone, but the the nasty one remains in .xaml files. The underlying code can't be source-generated due to missing namespace prefixes:
<ResourceDictionary
xmlns:xaml="clr-namespace:Windows.UI.Xaml;assembly=Windows"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Ifs.Uma.UI.Controls">
<Style TargetType="local:Dialog">
<Setter Property="Background" Value="#FFEDEDED" />
</Style>
</ResourceDictionary>
I'm not sure if I'm missing some package references. Or if it's possible to just define them somewhere (XmlnsDefinitionAttribute is not available)? Is it even possible without going through hundreds of .xaml files and fixing all them manually or via find-replace?
For example, one of .csproj files with referenced packages below:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<OutputType>Library</OutputType>
<RootNamespace>SomeUI</RootNamespace>
<DefaultLanguage>en</DefaultLanguage>
<CodeAnalysisRuleSet>..\FrameworkRules.ruleset</CodeAnalysisRuleSet>
<AppxBundlePlatforms>x86|x64</AppxBundlePlatforms>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<WarningLevel>4</WarningLevel>
<CodeAnalysisRuleSet>..\FrameworkRules.Release.ruleset</CodeAnalysisRuleSet>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup>
<NetStandardImplicitPackageVersion>2.0.3</NetStandardImplicitPackageVersion>
</PropertyGroup>
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\AssemblyInfo.cs">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Compile>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers" Version="0.3.330701">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<!-- I had to add those manually to get the code working -->
<ItemGroup>
<PackageReference Include="System.Runtime.InteropServices.WindowsRuntime" Version="4.3.0" />
<PackageReference Include="System.Runtime.WindowsRuntime" Version="4.7.0" />
<PackageReference Include="System.Runtime.WindowsRuntime.UI.Xaml" Version="4.7.0" />
<Reference Include="Windows">
<HintPath Condition="Exists('C:\Program Files (x86)\Windows Kits\10\UnionMetadata\Facade\Windows.winMD')">C:\Program Files (x86)\Windows Kits\10\UnionMetadata\Facade\Windows.winMD</HintPath>
</Reference>
<Reference Include="Windows.Foundation.FoundationContract">
<HintPath Condition="Exists('C:\Program Files (x86)\Windows Kits\10\References\10.0.19041.0\Windows.Foundation.FoundationContract\4.0.0.0\Windows.Foundation.FoundationContract.winmd')">C:\Program Files (x86)\Windows Kits\10\References\10.0.19041.0\Windows.Foundation.FoundationContract\4.0.0.0\Windows.Foundation.FoundationContract.winmd</HintPath>
</Reference>
<Reference Include="Windows.Foundation.UniversalApiContract">
<HintPath Condition="Exists('C:\Program Files (x86)\Windows Kits\10\References\10.0.19041.0\Windows.Foundation.UniversalApiContract\10.0.0.0\Windows.Foundation.UniversalApiContract.winmd')">C:\Program Files (x86)\Windows Kits\10\References\10.0.19041.0\Windows.Foundation.UniversalApiContract\10.0.0.0\Windows.Foundation.UniversalApiContract.winmd</HintPath>
</Reference>
</ItemGroup>
</Project>
I've just went an obvious way, made a simple UWP program to add prefixes and namespaces to those tags (just in case someone's interested):
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.Loaded += MainPage_Loaded;
}
private async void MainPage_Loaded(object sender, RoutedEventArgs e)
{
// 1. run the following command to make your .xaml files accessible to UWP:
// XCOPY <your_solution_dir>/*.xaml C:\Users\<you>\Pictures\XAML /E
// 2. Delete \bin and \obj directories from there (optional)
// 3. Add Pictures capability to your manifest (imho the easiest one to use)
// <Capabilities>
// <uap:Capability Name = "picturesLibrary" />
// </Capabilities>
var assembly = typeof(ResourceDictionary).Assembly;
var regulars = assembly
.GetExportedTypes()
.Where(type => typeof(DependencyObject).IsAssignableFrom(type) && type.IsClass)
.Select(type => new { Namespace = type.Namespace, TypeName = type.Name, Prefix = type.Namespace.Split('.').Last().ToLowerInvariant() })
.GroupBy(x => x.Prefix)
.Select(x => new {
Regex = new Regex($"(</?)(?={string.Join("|", x.Select(n => n.TypeName))})"),
Replacements = x.Select(y => new Replacement(y.Namespace, y.Prefix)).First() })
.ToDictionary(x => x.Regex, x => x.Replacements);
var namespaceAttributes = new Regex("<(?<=[\b\\w\\d\\D]).*(xmlns)[^>]+", RegexOptions.Singleline);
var picturesDirectory = KnownFolders.PicturesLibrary;
var sourceDirectory = await picturesDirectory.GetFolderAsync("XAML");
var xamlFiles = await GetXamlFilesAsync(sourceDirectory);
var fixedFiles = 0;
foreach (var xamlFile in xamlFiles)
{
var markup = await FileIO.ReadTextAsync(xamlFile);
var matchedReplacements = new List<Replacement>();
foreach (var replacer in regulars)
{
if (replacer.Key.IsMatch(markup))
{
matchedReplacements.Add(replacer.Value);
markup = replacer.Key.Replace(markup, $"$&{replacer.Value.Prefix}:");
}
}
if (matchedReplacements.Count == 0) continue;
var nsAttributes = namespaceAttributes.Match(markup);
if (nsAttributes.Success && markup.Length > nsAttributes.Length)
{
markup = nsAttributes.Result("$&" + Environment.NewLine +
string.Join(Environment.NewLine,
matchedReplacements.Where(replacement => !markup.Contains($"xmlns:{replacement.Prefix}")).Select(replacement =>
$"xmlns:{replacement.Prefix}=\"clr-namespace:{replacement.Namespace};assembly=Windows.Foundation.UniversalApiContract\"")))
+ markup.Substring(nsAttributes.Length);
}
await FileIO.WriteTextAsync(xamlFile, markup);
fixedFiles++;
}
await new Windows.UI.Popups.MessageDialog($"{fixedFiles} of {xamlFiles.Count} xaml files were fixed").ShowAsync();
}
async Task<IList<StorageFile>> GetXamlFilesAsync(StorageFolder topFolder)
{
var xamlFiles = new List<StorageFile>();
var folders = await topFolder.GetFoldersAsync();
foreach (var folder in folders)
{
xamlFiles.AddRange((await folder.GetFilesAsync()).Where(f => StringComparer.OrdinalIgnoreCase.Equals(f.FileType, ".xaml")));
xamlFiles.AddRange(await GetXamlFilesAsync(folder));
}
return xamlFiles;
}
private readonly struct Replacement
{
public Replacement(string #namespace, string prefix)
{
Namespace = #namespace; Prefix = prefix;
}
public readonly string Namespace;
public readonly string Prefix;
}
}
<xaml:ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Ifs.Uma.UI.Controls"
xmlns:nav="using:Ifs.Uma.UI.Navigation"
xmlns:xaml="clr-namespace:Windows.UI.Xaml;assembly=Windows.Foundation.UniversalApiContract"
xmlns:controls="clr-namespace:Windows.UI.Xaml.Controls;assembly=Windows.Foundation.UniversalApiContract"
xmlns:shapes="clr-namespace:Windows.UI.Xaml.Shapes;assembly=Windows.Foundation.UniversalApiContract">
<xaml:Style TargetType="local:PageHeader">
<xaml:Setter Property="Height" Value="48" />
<xaml:Setter Property="Background" Value="{ThemeResource IfsAppBackgroundBrush}" />
<xaml:Setter Property="Template">
<xaml:Setter.Value>
<controls:ControlTemplate TargetType="local:PageHeader">
<controls:Grid Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<!-- Grid and other controls -->
</controls:Grid>
</controls:ControlTemplate>
</xaml:Setter.Value>
</xaml:Setter>
</xaml:Style>
</xaml:ResourceDictionary>
It all went fine, but resulted in a message that type <controls:ControlTemplate /> does not support direct content. This type is pretty much within every .xaml file. TemplateBinding and other binding types also not found. Perhaps, it's not the way to fix the issue, there might be an SDK missing or something.
Given a working source generator and a working test project for the generator.
Generator
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<IncludeBuildOutput>false</IncludeBuildOutput>
</PropertyGroup>
[...]
<ItemGroup>
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.0.1" />
</ItemGroup>
<ItemGroup Condition="!$(DefineConstants.Contains('NET5_0_OR_GREATER'))">
<PackageReference Include="System.Memory" Version="4.5.4" />
</ItemGroup>
[...]
</Project>
namespace Rustic.DataEnumGen;
[Generator(LanguageNames.CSharp)]
[CLSCompliant(false)]
public class DataEnumGen : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
context.RegisterPostInitializationOutput(ctx => ctx.AddSource($"{GenInfo.DataEnumSymbol}.g.cs", SourceText.From(GenInfo.DataEnumSyntax, Encoding.UTF8)));
var enumDecls = context.SyntaxProvider.CreateSyntaxProvider(
static (s, _) => IsEnumDecl(s),
static (ctx, _) => CollectTreeInfo(ctx))
.Where(static m => m.HasValue)
.Select(static (m, _) => m!.Value);
var compilationEnumDeclUnion = context.CompilationProvider.Combine(enumDecls.Collect());
context.RegisterSourceOutput(compilationEnumDeclUnion, static (spc, source) => Generate(source.Right, spc));
}
private static bool IsEnumDecl(SyntaxNode node)
{
return node is EnumDeclarationSyntax;
}
private static GenInfo? CollectTreeInfo(GeneratorSyntaxContext context)
{
[...]
}
private static EnumDeclInfo CollectEnumDeclInfo(GeneratorSyntaxContext context, EnumMemberDeclarationSyntax memberDecl)
{
[...]
}
private static void Generate(ImmutableArray<GenInfo> members, SourceProductionContext context)
{
if (members.IsDefaultOrEmpty)
{
return;
}
foreach (var info in members.Distinct())
{
SrcBuilder text = new(2048);
GenInfo.Generate(text, in info);
context.AddSource($"{info.EnumName}Value.g.cs", SourceText.From(text.ToString(), Encoding.UTF8));
}
}
}
Test project
namespace Rustic.DataEnumGen.Tests;
[TestFixture]
public class GeneratorTests
{
private readonly StreamWriter _writer;
public GeneratorTests()
{
_writer = new StreamWriter($"GeneratorTests-{typeof(string).Assembly.ImageRuntimeVersion}.log", true);
_writer.AutoFlush = true;
Logger = new Logger(nameof(GeneratorTests), InternalTraceLevel.Debug, _writer);
}
~GeneratorTests()
{
_writer.Dispose();
}
internal Logger Logger { get; }
[Test]
public void SimpleGeneratorTest()
{
// Create the 'input' compilation that the generator will act on
Compilation inputCompilation = CreateCompilation(#"
using System;
using System.ComponentModel;
using Rustic;
namespace Rustic.DataEnumGen.Tests.TestAssembly
{
using static DummyValue;
public enum Dummy : byte
{
[Description(""The default value."")]
Default = 0,
[Rustic.DataEnum(typeof((int, int)))]
Minimum = 1,
[Rustic.DataEnum(typeof((long, long)))]
Maximum = 2,
}
public enum NoAttr
{
[Description(""This is a description."")]
This,
Is,
Sparta,
}
[Flags]
public enum NoFlags : byte
{
Flag = 1 << 0,
Enums = 1 << 1,
Are = 1 << 2,
Not = 1 << 3,
Supported = 1 << 4,
}
public static class Program
{
public static void Main()
{
DummyValue empty = default!;
DummyValue default = Default();
DummyValue min = Minimum((12, 43));
DummyValue min = Maximum((12, 43));
}
}
}
");
const int TEST_SOURCES_LEN = 1;
const int GEN_SOURCES_LEN = 3; // Attribute + Dummy + NoAttr
DataEnumGen generator = new();
GeneratorDriver driver = CSharpGeneratorDriver.Create(generator);
driver = driver.RunGeneratorsAndUpdateCompilation(inputCompilation, out var outputCompilation, out var diagnostics);
[...Validation...]
}
private void Logging(Compilation comp, ImmutableArray<Diagnostic> diagnostics)
{
foreach (var diag in diagnostics)
{
Logger.Debug("Initial diagnostics {0}", diag.ToString());
}
foreach (var tree in comp.SyntaxTrees)
{
Logger.Debug("SyntaxTree\nName=\"{0}\",\nText=\"{1}\"", tree.FilePath, tree.ToString());
}
var d = comp.GetDiagnostics();
foreach (var diag in d)
{
Logger.Debug("Diagnostics {0}", diag.ToString());
}
}
private static Compilation CreateCompilation(string source)
=> CSharpCompilation.Create("compilation",
new[] { CSharpSyntaxTree.ParseText(source) },
new[]
{
MetadataReference.CreateFromFile(typeof(System.String).GetTypeInfo().Assembly.Location),
MetadataReference.CreateFromFile(typeof(System.ComponentModel.DescriptionAttribute).GetTypeInfo().Assembly.Location),
MetadataReference.CreateFromFile(typeof(ReadOnlySpan<char>).GetTypeInfo().Assembly.Location),
MetadataReference.CreateFromFile(typeof(System.Collections.Generic.List<char>).GetTypeInfo().Assembly.Location),
MetadataReference.CreateFromFile(typeof(System.Runtime.CompilerServices.MethodImplAttribute).GetTypeInfo().Assembly.Location),
MetadataReference.CreateFromFile(typeof(System.Runtime.Serialization.ISerializable).GetTypeInfo().Assembly.Location),
MetadataReference.CreateFromFile(typeof(System.Runtime.InteropServices.StructLayoutAttribute).GetTypeInfo().Assembly.Location),
MetadataReference.CreateFromFile(#"C:\Program Files (x86)\dotnet\shared\Microsoft.NETCore.App\6.0.1\System.Runtime.dll"),
},
new CSharpCompilationOptions(OutputKind.ConsoleApplication));
}
The test runs without error, the types and methods are generated correctly.
But I absolutely hate writing tests in plain text, plus executing the tests like so doesnt yield test coverage or unit test cases, so I want to write a production test for the source generator. As per usual I create a .Run.Tests project and add the Rustic.DataEnumGen nuget project as an analyzer. Like so
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;net50;net60</TargetFrameworks>
<LangVersion>10.0</LangVersion>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AltCover" Version="8.2.835" />
<PackageReference Include="bogus" Version="33.0.2" />
<PackageReference Include="fluentassertions" Version="5.10.3" />
<PackageReference Include="NUnit" Version="3.13.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Rustic.DataEnumGen" Version="0.5.0" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup>
</Project>
using System;
using System.ComponentModel;
using NUnit.Framework;
using Rustic;
namespace Rustic.DataEnumGen.Run.Tests
{
using static DummyValue;
public enum Dummy : byte
{
[Description("The default value.")]
Default = 0,
[DataEnum(typeof((int, int)))]
Minimum = 1,
[DataEnum(typeof((long, long)))]
Maximum = 2,
}
public enum NoAttr
{
[Description("This is a description.")]
This,
Is,
Sparta,
}
[Flags]
public enum NoFlags : byte
{
Flag = 1 << 0,
Enums = 1 << 1,
Are = 1 << 2,
Not = 1 << 3,
Supported = 1 << 4,
}
[TestFixture]
public static class DataEnumRunTests
{
[Test]
public static void TestFactory()
{
DummyValue empty = default!;
DummyValue default = Default();
DummyValue min = Minimum((12, 43));
DummyValue min = Maximum((12, 43));
}
[Test]
public static void TestImplicitEnumCast()
{
Dummy default = Default();
Dummy min = Minimum((12, 43));
Dummy min = Maximum((12, 43));
}
}
}
This is the exact same code as in the previous test, but wrapped in a TestFixture instead of a console application. So I build the project with the analyzer, so that the DataEnumAttribute generated and then add the code above. But the code does not compile, because the type DataEnum or DataEnumAttribute does not exist.
First I thought that I needed to (I) ReferenceOutputAssembly, but that didn't change anything either, then I tried combinations of removing OutputItemType="Analyzer" and hoping that would result in the analyzer being called; nothing helped.
I conclude, that in this example the imported source generator, the very same that works in the first test case with the plain text compilation, is not executed before building the project, because if that was the case then type that is always added by the generator would be available in the project, and I would see some Rusic.*.g.cs in the obj/ directory. That is not the case.
So maybe the generator is not packed in the nuget package? As you can see the analyzer is being packed. Maybe I need to IncludeBuildOutput aswell? Nope not working either.
Now my question is why that is the case? Is there some specific thing, some specific attribute, I need to pay attention to when importing IIncrementalGenerator into a project compared to ISourceGenerator, because using an ISourceGenerator in a project works in the exact same way?
Is there anything else I might try to get the incremental source generator to work, or should I just revert to using a regular source generator?
References to good articles help as well, because there is effectively no doc to be found. When working I referenced most of Andrew Lock`s source generator related articles, specifically this one.
I tested this mit net 6.0.101 and a build of 6.0.2xx from like a week ago.
I'm setting up a new project with Entity Framework Core 5 and Postgres. All my project and my context are in the same project.
When adding a migration, I'm getting this error:
/src/Api.csproj : error MSB4057: The target "GetEFProjectMetadata" does not exist in the project. Unable to retrieve project metadata. Ensure it's an MSBuild-based .NET Core project. If you're using custom BaseIntermediateOutputPath or MSBuildProjectExtensionsPath values, Use the --msbuildprojectextensionspath option.
EF Core version: 5.0.1
Target framework: net5.0
These are all the commands I tried:
dotnet ef migrations add NewMigration
dotnet ef migrations add NewMigration --msbuildprojectextensionspath
dotnet ef migrations add NewMigration -p ".\src\Api.csproj"
dotnet ef migrations add NewMigration -p ".\src\Api.csproj" --msbuildprojectextensionspath*
I have tried a lot of options I found on the internet, but none worked.
if i add -v , the last lines are:
Using project '/media/pablo/l/Projetos/Web/backend/boilerplate/dotnet_csharp/src/Api.csproj'.
Using startup project '/media/pablo/l/Projetos/Web/backend/boilerplate/dotnet_csharp/src/Api.csproj'.
Writing '/media/pablo/l/Projetos/Web/backend/boilerplate/dotnet_csharp/src/obj/Api.csproj.EntityFrameworkCore.targets'...
dotnet msbuild /target:GetEFProjectMetadata /property:EFProjectMetadataFile=/tmp/tmpbscOwZ.tmp /verbosity:quiet /nologo /media/pablo/l/Projetos/Web/backend/boilerplate/dotnet_csharp/src/Api.csproj
/media/pablo/l/Projetos/Web/backend/boilerplate/dotnet_csharp/src/Api.csproj : error MSB4057: O destino "GetEFProjectMetadata" não existe no projeto.
Microsoft.EntityFrameworkCore.Tools.CommandException: Unable to retrieve project metadata. Ensure it's an SDK-style project. If you're using a custom BaseIntermediateOutputPath or MSBuildProjectExtensionsPath values, Use the --msbuildprojectextensionspath option.
at Microsoft.EntityFrameworkCore.Tools.Project.FromFile(String file, String buildExtensionsDir, String framework, String configuration, String runtime)
at Microsoft.EntityFrameworkCore.Tools.RootCommand.Execute(String[] _)
at Microsoft.EntityFrameworkCore.Tools.Commands.CommandBase.<>c__DisplayClass0_0.<Configure>b__0(String[] args)
at Microsoft.DotNet.Cli.CommandLine.CommandLineApplication.Execute(String[] args)
at Microsoft.EntityFrameworkCore.Tools.Program.Main(String[] args)
Unable to retrieve project metadata. Ensure it's an SDK-style project. If you're using a custom BaseIntermediateOutputPath or MSBuildProjectExtensionsPath values, Use the --msbuildprojectextensionspath option.
Api.csproj:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<DocumentationFile>bin\Debug\net5.0\comments.xml</DocumentationFile>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<noWarn>1591;1572;1573</noWarn>
<PublishWithAspNetCoreTargetManifest>false</PublishWithAspNetCoreTargetManifest>
<CodeAnalysisRuleSet>../roslynator.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentValidation" Version="9.4.0" />
<PackageReference Include="FluentValidation.AspNetCore" Version="9.4.0" />
<PackageReference Include="Mapster" Version="7.0.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.5.1" />
<PackageReference Include="Scrutor" Version="3.3.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.1" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="5.0.1" />
<PackageReference Include="jaeger" Version="0.4.2" />
<PackageReference Include="OpenTracing.Contrib.NetCore" Version="0.7.1" />
<PackageReference Include="RestSharp" Version="106.2.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="5.0.1" />
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.7" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="5.0.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="5.0.0" />
<PackageReference Include="Microsoft.OpenApi" Version="1.2.3" />
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="5.6.3" />
</ItemGroup>
<ItemGroup>
<None Include="*.json" CopyToPublishDirectory="Always" CopyToOutputDirectory="Always" />
<None Include="Locales\**\*.json" CopyToPublishDirectory="Always" CopyToOutputDirectory="Always" />
</ItemGroup>
</Project>
Program.cs:
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
namespace Linear.Service_Name.Api
{
public static class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args).UseStartup<Startup>();
}
}
Startup.cs:
using Linear.Service_Name.DataBase.Entities;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
[assembly: Microsoft.AspNetCore.Mvc.ApiController]
namespace Linear.Service_Name.Api
{
public class Startup
{
private IWebHostEnvironment _environment { get; }
public Startup(IConfiguration _, IWebHostEnvironment environment)
{
_environment = environment;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<Context>(opt => opt
.UseNpgsql(EnvVariables.LINEAR_API_DOMAIN_NAME_DB_CONNECTION_STRING)
);
services.AddCommonsServices<Context>(new CommonServices
{
Env = _environment,
ConnectionString = EnvVariables.LINEAR_API_DOMAIN_NAME_DB_CONNECTION_STRING,
Assembly = Assembly.GetExecutingAssembly(),
Swagger = new CommonServices.SwaggerSettings{
Version = "v1",
Title = "Service_Name",
Description = "API REST do Módulo de " + ("Service_Name").ToUpper()
+ " da solução SG Web."
}
});
}
public void Configure(IApplicationBuilder app)
{
app.UserCommonsMiddlewares(new CommonMiddlewares
{
Env = _environment,
PathBase = EnvVariables.LINEAR_API_SERVICE_NAME_BASE_PATH,
Swagger = new CommonMiddlewares.SwaggerSettings
{
Version = "v1",
Title = "Service_Name"
}
});
}
}
}
Context.cs:
using Linear.Infrastructure.Data.Audit;
using Linear.Infrastructure.Data.MultiTenancy;
using Microsoft.EntityFrameworkCore;
namespace Linear.Service_Name.DataBase.Entities
{
public class Context : DbContext
{
public virtual DbSet<Sample_NamesEntity> Sample_Names { get; set; }
private readonly IAuditEntity _auditEntity;
private readonly IMultiTenancy _multiTenancy;
public Context(DbContextOptions<Context> options, IAuditEntity auditEntity,
IMultiTenancy multiTenancy)
: base(options)
{
_auditEntity = auditEntity;
_multiTenancy = multiTenancy;
}
#region Métodos Protegidos
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
=> modelBuilder.ApplyConfigurationsFromAssembly(typeof(Context).Assembly);
#endregion
#region Métodos Públicos
public override int SaveChanges()
{
_multiTenancy.OnSaveChanges(this);
_auditEntity.OnSaveChanges(this);
return base.SaveChanges();
}
public int SaveChangesWithoutMultiTenancy()
{
_auditEntity.OnSaveChanges(this);
return base.SaveChanges();
}
public int SaveChangesWithoutMultiTenancyAndAudit() => base.SaveChanges();
#endregion
}
}
Project link to download:
https://drive.google.com/file/d/1YWlm_teyGMjJe193AwrFqe3VAeVVKWLq/view?usp=sharing
I've been stuck with this problem for over 3 hours, tried a lot of suggestions on internet and still no sucess, i appreciate if i have some help.
After doing some research, it seems that there is a problem with projects with docker integration and the EF Core tooling.
I have downloaded your code and this is the content of your Directory.Build.props
<Project>
<PropertyGroup>
<DefaultItemExcludes>$(DefaultItemExcludes);$(MSBuildProjectDirectory)/obj/**/*</DefaultItemExcludes>
<DefaultItemExcludes>$(DefaultItemExcludes);$(MSBuildProjectDirectory)/bin/**/*</DefaultItemExcludes>
</PropertyGroup>
<PropertyGroup Condition="'$(DOTNET_RUNNING_IN_CONTAINER)' == 'true'">
<BaseIntermediateOutputPath>$(MSBuildProjectDirectory)/obj/container/</BaseIntermediateOutputPath>
<BaseOutputPath>$(MSBuildProjectDirectory)/bin/container/</BaseOutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(DOTNET_RUNNING_IN_CONTAINER)' != 'true'">
<BaseIntermediateOutputPath>$(MSBuildProjectDirectory)/obj/local/</BaseIntermediateOutputPath>
<BaseOutputPath>$(MSBuildProjectDirectory)/bin/local/</BaseOutputPath>
</PropertyGroup>
</Project>
According to #bricelam in this issue the root of the problem lies here:
Changing BaseIntermediateOutputPath from its default when not running inside a
container breaks the EF Core tooling experience (*)
Since your BaseIntermediateOutputPath was changed from its default to obj/local when running outside the docker container, what you need to do is let the EF Core CLI know where to find that folder. You can accomplish that by using the --msbuildprojectextensionspath parameter. In your case, it would like like this (as suggested here):
dotnet ef migrations add NewMigration --msbuildprojectextensionspath obj/local
If you are still unable to make it work, you could follow the discussion in this other issue. In there, it was suggested that another possible fix is to change a bit your Directory.Build.props and *.csproj files so that the latter looks like the following:
<Project> <!-- Note: No Sdk="" here. -->
<PropertyGroup>
<!-- Don't use $(Configuration) since it isn't set yet. -->
<MSBuildProjectExtensionsPath>$(MSBuildProjectDirectory)\_intermediate_\</MSBuildProjectExtensionsPath>
</PropertyGroup>
<!-- MSBuildProjectExtensionsPath must be set before this gets imported. Directory.Build.props is too late. -->
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
<!-- (Project content goes here) -->
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
</Project>