await using not working in net standard 2 - c#

I have an issue I can't solve, I have a library with this definition
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
</ItemGroup>
</Project>
And after a http call with HttpClient I try to do this :
await using var stream = await response.Content.ReadAsStreamAsync();
Which produce this error :
[CS8417] 'Stream': type used in an asynchronous using statement must be implicitly convertible to 'System.IAsyncDisposable' or implement a suitable 'DisposeAsync' method. Did you mean 'using' rather than 'await using'?
The issue is clearly with NETSANDARD2_0, if I remove it, it works well.
I don't understand why it fails, as I have added the Microsoft.Bcl.AsyncInterfaces.

The problem is that in .NET Standard 2.0, Stream doesn't implement IAsyncDisposable.
It's entirely feasible to use await using within a .NET Standard 2.0 project if you're using types that actually implement IAsyncDisposable - although you also need to specify a LangVersion in the project file, as otherwise the default version of C# is used, which is 7.3 for .NET Standard 2.0. For example:
using System;
using System.IO;
using System.Threading.Tasks;
class Test
{
static async void TestAwaitUsing()
{
// This is fine
await using var working = new Sample();
// This fails, because MemoryStream doesn't implement IAsyncDisposable
await using var failing = new MemoryStream();
}
class Sample : IAsyncDisposable
{
public ValueTask DisposeAsync()
{
throw new NotImplementedException();
}
}
}
Project file:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" />
</ItemGroup>
</Project>
Now it's entirely possible that in whatever platform the code is running on, Stream does actually implement IAsyncDisposable - but it's hard to make use of that in an elegant way.
For example, this compiles and will do the right thing if the application platform supports it:
await using var awkward = (IAsyncDisposable) new MemoryStream();
var stream = (MemoryStream) awkward;
// Now use things from stream
This will throw InvalidCastException if you're running on a platform which doesn't support it, however. If you can possibly change your target to .NET Standard 2.1 or a more recent one (e.g. .NET 6) that would be better. If you actually need to run your code with .NET Framework, you just won't be able to use await using with Stream.

await using has been added in C# 8. The default C# version for netstandard2.0 is C# 7.3. In netstandard2.1 it is C# 8.

Related

Why do I keep getting 'Predefined type IsExternalInit it not defined' even after upgrading to .net 5?

I removed my IsExternalInit definition, as we are now using .net 5.0:
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<WarningsAsErrors>nullable</WarningsAsErrors>
</PropertyGroup>
However I still get this error
CS0518 Predefined type 'System.Runtime.CompilerServices.IsExternalInit' is not defined or imported
In files like this
public sealed record TestDTO
{
public string SomePropert{ get; private init; } = default!; // error occurs here
}
I can see this is expected behaviour when targeting older frameworks, but shouldn't it work in my case? What am I doing wrong?
I think this is happening because one of the projects I reference has a dependency on some library using .net standard
To avoid adding IsExternalInit.cs to every project, I just added one to the solution root folder and also added a file called Directory.Build.Props with these contents
<?xml version="1.0" encoding="utf-8"?>
<Project>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)IsExternalInit.cs" Visible="false" />
</ItemGroup>
</Project>

ActiproSoftware controls breaks Moq unit tests

Moq does not want to work with ActiroSoftware on net core 3.1
I'm having the following issue: creating a net core 3.1 project with the following structure:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>netcoreapp3.1</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Moq" Version="4.14.7" />
<PackageReference Include="Actiprosoftware.Controls.WPF" Version="20.1.0" />
</ItemGroup>
</Project>
then in Program.cs try to write the following:
using Moq;
namespace moqtest
{
class Program
{
static void Main(string[] args)
{
var q = It.IsAny<string>();
}
}
}
Note that this won't compile, due to the following error:
error CS0234: The type or namespace name 'IsAny' does not exist in the namespace 'It' (are you missing an assembly reference?)
Furthermore, specifying the namespace implicitly, will work:
var q = Moq.It.IsAny<string>();
I have looked into msbuild diagnostics and it seems everything is compatible with netcoreapp3.1, but when you compile it, it seems it does not recognize It class anymore.
Please help!
Open up View > Object Browser, and search for It. You'll notice that first result is a namespace called It brought in by ActiproSoftware.BarCode.Wpf.dll
It also happens to be an empty namespace, but that's irrelevant. If it did contain anything under it, you'd refer to them as It.Something. So what happens now is that, even after you do using Moq, It is still ambiguous to the compiler.
The presence of that silly empty namespace is what's forcing to qualify your calls with Moq.

Read *.csproj property values using Roslyn APIs?

I am currently building a tool which will support the development of an ASP.NET Core project. This tool uses the Roslyn APIs and other methods for verifying some development requirements (such as project-specific attributes being applied on API Controllers, enforcing naming conventions, and generating some source code for the JavaScript SPA which accesses an API written using the ASP.NET Core Web API template).
In order to do that, I am currently using hardcoded paths to generate code for the SPA app. But in the app's *.csproj file there is actually a "SpaRoot" property specifying where the SPA application is located inside the project:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
<IsPackable>false</IsPackable>
<SpaRoot>ClientApp\</SpaRoot>
...
</PropertyGroup>
...
</Project>
My question is: how can I read the "SpaRoot" property's value using the Roslyn APIs?
I have written a minimum code sample to create a Workspace, open the Solution, and retrieve the Project's reference, which resembles the following:
static async Task Main(string[] args)
{
string solutionFile = #"C:\Test\my-solution.sln";
using (var workspace = MSBuildWorkspace.Create())
{
var solution = await workspace.OpenSolutionAsync(solutionFile);
string projectName = "some-project";
var project = solution.Projects.Single(p => p.Name == projectName);
// How to extract the value of "SpaRoot" from the Project here?
}
I've tried searching on how to extract the "SpaRoot" property from the Project reference, and even went as far as debugging to see if I could spot a way myself. Unfortunately, I came up with no answers to that, and I'm still using hardcoded paths in my original code.
Is it even possible to retrieve the value of .csproj properties of a Project using the current Roslyn APIs?
This is more difficult that you would think :) The Roslyn apis only know what the compiler knows and the compiler is not going to be given anything regarding the SpaRoot property. We can use the MSBuild apis to figure this out though. specifically the Microsoft.Build.Evaluation.Project class.
Some assumptions I am making
You only want to examine .NET Core projects
You will have the .NET Core SDK installed on which ever system runs this tool
So first we want a project file that looks like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<!--NOTE: If the project you are analyzing is .NET Core then the commandline tool must be as well.
.NET Framework console apps cannot load .NET Core MSBuild assemblies which is required
for what we want to do.-->
<TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>Latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<!-- NOTE: We put ExcludeAssets="runtime" on all direct MSBuild references so that we pick up whatever
version is being used by the .NET SDK instead. This is accomplished with the Microsoft.Build.Locator
referenced further below. -->
<PackageReference Include="Microsoft.Build" Version="16.4.0" ExcludeAssets="runtime" />
<PackageReference Include="Microsoft.Build.Locator" Version="1.2.6" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="2.9.8" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.4.0" />
<PackageReference Include="Microsoft.CodeAnalysis.VisualBasic.Workspaces" Version="3.4.0" />
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="3.4.0" />
<!-- NOTE: A lot of MSBuild tasks that we are going to load in order to analyze a project file will implicitly
load build tasks that will require Newtonsoft.Json version 9. Since there is no way for us to ambiently
pick these dependencies up like with MSBuild assemblies we explicitly reference it here. -->
<PackageReference Include="Newtonsoft.Json" Version="9.0.1" />
</ItemGroup>
</Project>
and a Program.cs file that looks like this:
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Xml;
using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Locator;
using Microsoft.CodeAnalysis.MSBuild;
// I use this so I don't get confused with the Roslyn Project type
using MSBuildProject = Microsoft.Build.Evaluation.Project;
namespace loadProject {
class Program {
static async Task Main(string[] args) {
MSBuildWorkspaceSetup();
// NOTE: we need to make sure we call MSBuildLocator.RegisterInstance
// before we ask the CLR to load any MSBuild types. Therefore we moved
// the code that uses MSBuild types to its own method (instead of being in
// Main) so the CLR is not forced to load them on startup.
await DoAnalysisAsync(args[0]);
}
private static async Task DoAnalysisAsync(string solutionPath) {
using var workspace = MSBuildWorkspace.Create();
// Print message for WorkspaceFailed event to help diagnosing project load failures.
workspace.WorkspaceFailed += (o, e) => Console.WriteLine(e.Diagnostic.Message);
Console.WriteLine($"Loading solution '{solutionPath}'");
// Attach progress reporter so we print projects as they are loaded.
var solution = await workspace.OpenSolutionAsync(solutionPath, new ConsoleProgressReporter());
Console.WriteLine($"Finished loading solution '{solutionPath}'");
// We just select the first project as a demo
// you will want to use your own logic here
var project = solution.Projects.First();
// Now we use the MSBuild apis to load and evaluate our project file
using var xmlReader = XmlReader.Create(File.OpenRead(project.FilePath));
ProjectRootElement root = ProjectRootElement.Create(xmlReader, new ProjectCollection(), preserveFormatting: true);
MSBuildProject msbuildProject = new MSBuildProject(root);
// We can now ask any question about the properties or items in our project file
// and get the correct answer
string spaRootValue = msbuildProject.GetPropertyValue("SpaRoot");
}
private static void MSBuildWorkspaceSetup() {
// Attempt to set the version of MSBuild.
var visualStudioInstances = MSBuildLocator.QueryVisualStudioInstances().ToArray();
var instance = visualStudioInstances.Length == 1
// If there is only one instance of MSBuild on this machine, set that as the one to use.
? visualStudioInstances[0]
// Handle selecting the version of MSBuild you want to use.
: SelectVisualStudioInstance(visualStudioInstances);
Console.WriteLine($"Using MSBuild at '{instance.MSBuildPath}' to load projects.");
// NOTE: Be sure to register an instance with the MSBuildLocator
// before calling MSBuildWorkspace.Create()
// otherwise, MSBuildWorkspace won't MEF compose.
MSBuildLocator.RegisterInstance(instance);
}
private static VisualStudioInstance SelectVisualStudioInstance(VisualStudioInstance[] visualStudioInstances) {
Console.WriteLine("Multiple installs of MSBuild detected please select one:");
for (int i = 0; i < visualStudioInstances.Length; i++) {
Console.WriteLine($"Instance {i + 1}");
Console.WriteLine($" Name: {visualStudioInstances[i].Name}");
Console.WriteLine($" Version: {visualStudioInstances[i].Version}");
Console.WriteLine($" MSBuild Path: {visualStudioInstances[i].MSBuildPath}");
}
while (true) {
var userResponse = Console.ReadLine();
if (int.TryParse(userResponse, out int instanceNumber) &&
instanceNumber > 0 &&
instanceNumber <= visualStudioInstances.Length) {
return visualStudioInstances[instanceNumber - 1];
}
Console.WriteLine("Input not accepted, try again.");
}
}
private class ConsoleProgressReporter : IProgress<ProjectLoadProgress> {
public void Report(ProjectLoadProgress loadProgress) {
var projectDisplay = Path.GetFileName(loadProgress.FilePath);
if (loadProgress.TargetFramework != null) {
projectDisplay += $" ({loadProgress.TargetFramework})";
}
Console.WriteLine($"{loadProgress.Operation,-15} {loadProgress.ElapsedTime,-15:m\\:ss\\.fffffff} {projectDisplay}");
}
}
}
}

Can .csproj Switch native code by platform?

I'm learning C#. (sorry I'm not native English speeker.)
I'm creating multi platform library in dotnet core.
I want to switch platform in one method.
I tryed RuntimeInformation.IsOSPlatform() then successed I want to do.
I heard that I can change the code to be executed for each OS by the property of project.csproj.
but I couldn't do that.
I want to do this.
project.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>library</OutputType>
<TargetFramework>netstandard2.0</TargetFramework>
<PackageId>MemoryInfo</PackageId>
<Version>1.0.0</Version>
<Authors>foobar</Authors>
<Company>foobar</Company>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
</PropertyGroup>
<ItemGroup>
<Compile Include="Program.cs" />
</ItemGroup>
//(add comment) Switch Native Code
<ItemGroup Condition="'$(TargetsWindows)' == 'true'">
<Compile Include="Linux/native.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetsLinux)' == 'true'">
<Compile Include="Windows/native.cs" />
</ItemGroup>
</Project>
Program.cs
~~
public long GetMemorySize() //library method
{
NativeMemoryinfo mem = new NativeMemoryinfo();
return mem.GetMemorySize(); //access to each platform code
}
~~
Linux/native.cs
~~
class NativeMemoryinfo
{
public long GetMemorySize()
{
Code for Linux(/proc/meminfo...)
}
}
~~
Windows/native.cs
~~
class NativeMemoryinfo
{
public long GetMemorySize()
{
Code for Windows(Kernel32....)
}
}
~~
Maybe, I think I'm misunderstood or in the wrong way.
Can I do this way?
my research, I found something called "interop" this, but what is the relationship with this?
Thank you.
The feature you are trying to use with csproj is normally used to target the different frameworks in .NET, not to target different OS. See this: https://learn.microsoft.com/en-us/dotnet/standard/frameworks
What you are trying to do can be achieved by interfaces. Have an interface INativeMemoryinfo and initialize it to the windows or linux version based on if (RuntimeInformation.IsOSPlatform() == "").
Side note: since you are working o a library, I suggest using .net standard instead of .net core.
if (RuntimeInformation.IsOSPlatform() == "").

Assembly versioning in .NET Core 1.1 (SDK Preview 3)

There are plenty of resources online for bumping and reading version numbers using project.json. Given its deprecation and the re-introduction of .csproj, how do I go about setting the version number for a web project?
I've been able to read it with:
Microsoft.Extensions.PlatformAbstractions
.PlatformServices.Default.Application.ApplicationVersion
However, it always outputs 1.0.0.0, and I haven't been able to find where that version number is set.
Use the <VersionPrefix>5.4.3.2</VersionPrefix> property.
temp.csproj
...
<PropertyGroup>
<VersionPrefix>5.4.3.2</VersionPrefix>
<TargetFramework>netcoreapp1.1</TargetFramework>
<AssemblyName>temp</AssemblyName>
<OutputType>Exe</OutputType>
</PropertyGroup>
...
Program.cs
public class Program
{
public static void Main()
{
var version = Microsoft
.Extensions
.PlatformAbstractions
.PlatformServices
.Default
.Application
.ApplicationVersion;
System.Console.WriteLine(version); // 5.4.3.2
}
}

Categories

Resources