Is there a way to compile a WebAPI app from console app in .NET Core 3.1?
I have tried many different approaches, such as:
1.
var collection = ProjectCollection.GlobalProjectCollection;
var project = collection.LoadProject($#"{path}\Project.csproj");
project.SetProperty("Configuration", configuration);
project.Build();
(Path to .sln)
ERROR: MSB4041: The default XML namespace of the project must be the MSBuild XML namespace.
2.
new Microsoft.Build.Execution.ProjectInstance("PathToProject.sln").Build();
(Path to .sln)
ERROR: Microsoft.Build.Exceptions.InvalidProjectFileException: 'The project file could not be loaded. Data at the root level is invalid.
(Path to .csproj)
ERROR: Microsoft.Build.Exceptions.InvalidProjectFileException: 'The SDK 'Microsoft.NET.Sdk.Web' specified could not be found.
3.
ProjectCollection pc = new ProjectCollection();
Dictionary<string, string> GlobalProperty = new Dictionary<string, string>();
GlobalProperty.Add("Configuration", "Debug");
GlobalProperty.Add("Platform", "Any CPU");
GlobalProperty.Add("OutputPath", Directory.GetCurrentDirectory() + "\\MyOutput");
BuildParameters bp = new BuildParameters(pc);
BuildManager.DefaultBuildManager.BeginBuild(bp);
BuildRequestData BuildRequest = new BuildRequestData(projectFilePath, GlobalProperty, null, new string[] { "Build" }, null);
BuildSubmission BuildSubmission = BuildManager.DefaultBuildManager.PendBuildRequest(BuildRequest);
BuildSubmission.Execute();
BuildManager.DefaultBuildManager.EndBuild();
if (BuildSubmission.BuildResult.OverallResult == BuildResultCode.Failure)
{
throw new Exception();
}
(Both path to .sln and .csproj)
ERROR: Build result is a failure without reported exception
However, none of the approaches worked.
Therefore, I am wondering is it even possible to compile the .NET Core 3.1 WebAPI code?
The only way I could get this to work was to use the following code snippet (.NET 6):
var commandText = $"myProject.csproj";
p.StartInfo = new ProcessStartInfo(#"C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\MSBuild\Current\Bin\msbuild.exe", commandText);
p.StartInfo.WorkingDirectory = projectFileLocation;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardOutput = true;
p.Start();
while (!p.StandardOutput.EndOfStream)
{
var line = p.StandardOutput.ReadLine();
sb.Append(line);
}
p.WaitForExit();
I hope this saves somebody a lot more time.
Related
My new projects generated by VS 2019 cannot read or write files. Even the following simple code does not work.
public class Program
{
public static void Main(string[] args)
{
Console.WriteLine("hello");
string codeBase = Assembly.GetExecutingAssembly().CodeBase;
UriBuilder uri = new UriBuilder(codeBase);
string path = Uri.UnescapeDataString(uri.Path);
var folder = Path.GetDirectoryName(path);
var filePath = Path.Combine(folder, "test.txt");
Console.WriteLine(filePath);
// File.WriteAllLines(filePath, new[] { "hello" });
using (var f = new FileStream(filePath, FileMode.OpenOrCreate))
{
f.Write(new byte[] { 0x39 }, 0, 1);
}
}
}
Accessing files using
.Net Core 3.0 + debugger
.Net Core 3.0 + generated exe
.Net Framework 4.6.2, 4.8 (have not tested others)
gives the exception
Unhandled exception. System.UnauthorizedAccessException: Access to the path 'C:\Users\user\Documents\ConsoleApp1\ConsoleApp1\Debug\netcoreapp3.1\test.txt' is denied.
However, it works in .Net Core 2.2 or below or using dotnet ConsoleApp1.dll.
My other few years old projects work. I have never encountered such a problem before. What is the problem and how to fix it?
It turns out my anti-virus blocked the file access. There were no pop-ups the first day so I could not know. Later, there were finally popups.
My goal is to test this code to make sure that Stanford Core NLP installed properly.
First I installed StanfordCOreNLP package using NuGet package manager and then I downloaded a zip file that contained a jar file that needed to be installed using jar -xf command , and then I ran the code.
At (var pipeline = new StanfordCoreNLP(props);)
I'm getting an error that says:
edu.stanford.nlp.io.RuntimeIOException: Error while loading a tagger model(probably missing model file)"
Inner Exception IOException:Unable to open"edu/stanford/nlp/models/pos-tagger/english-left3words/english-left3words-distsim.tagger" as class path, filename or URL
var jarRoot = #"D:/VisualStudioProjects/C#MachineLearningProjects/Chapter3TwiterSentiment/CoreNLPTest2/CoreNLPTest2/edu/stanford/nlp/models/pos-tagger";
var text = "We're going to test our CoreNLP instalation!!";
Properties props = new Properties();
props.setProperty("annotators", "tokenize, ssplit, pos, lemma, ner, parse, dcoref");
props.setProperty("ner.useSUTime", "0");
var curDir = Environment.CurrentDirectory;
Directory.SetCurrentDirectory(jarRoot);
var pipeline = new StanfordCoreNLP(props);
Directory.SetCurrentDirectory(curDir);
var annotation = new Annotation(text);
pipeline.annotate(annotation);
using (var stream = new ByteArrayOutputStream())
{
pipeline.prettyPrint(annotation, new PrintWriter(stream));
Console.WriteLine(stream.toString());
stream.close();
}
Console.ReadKey();
Please follow the below steps:
Step 1: Download Core NLP
Step 2: Unzip d:\stanford-corenlp-full-2018-10-05
Step 3: Unzip d:\stanford-corenlp-full-2018-10-05\stanford-corenlp-3.9.2-models.jar
Step 4: Change var jarRoot = #"d:/stanford-corenlp-full-2018-10-05/stanford-corenlp-3.9.2-models";
Step 5: Change props.setProperty("ner.useSUTime", "0"); to props.setProperty("sutime.binders", "0")
I installed the three following packages into my console application:
Microsoft.Build
Microsoft.Build.Framework
Microsoft.Build.Tasks.Core
Microsoft.Build.Utilities.Core
And I tried to use the following method to build a project:
static void Build(string projectPath)
{
var logger = new ConsoleLogger(LoggerVerbosity.Normal);
logger.ShowSummary = true;
var manager = BuildManager.DefaultBuildManager;
var projectInstance = new ProjectInstance(projectPath);
var result = manager.Build(
new BuildParameters()
{
DetailedSummary = true,
Loggers = new List<ILogger>() { logger }
},
new BuildRequestData(projectInstance, new string[] { "Build" }));
var buildResult = result.ResultsByTarget["Build"];
var buildResultItems = buildResult.Items;
}
However, after I ran the code, I got the error that described in the following image:
Why is this happening and how can I fix it?
I think you're not using tht right MSBuild version. Try to set the variable explicitly in your .proj :
<MSBuildExtensionsPath>C:\Program Files (x86)\MSBuild</MSBuildExtensionsPath>
It seems the best solution is to use MSBuild command line in Process class. A working sample is as follows:
var buildOutput = new List<string>();
var buildError = new List<string>();
var buildProcess = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Enterprise\\MSBuild\\15.0\\Bin\\MSBuild.exe",
Arguments = projectPath + " /t:Rebuild /p:Configuration=Debug",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
}
};
buildProcess.Start();
while (!buildProcess.StandardOutput.EndOfStream)
{
buildOutput.Add(buildProcess.StandardOutput.ReadLine());
}
while (!buildProcess.StandardError.EndOfStream)
{
buildError.Add(buildProcess.StandardError.ReadLine());
}
And then you could use the output to determine whether the build was successful or not. The important note is that you have to find the correct path of MSBuild.exe file as there are several versions of this file and in my case (VS 2017) the correct path is the one in the sample code.
One of BuildRequestData constructor overloads supports a parameter called "toolVersion". Since you are using Visual Studio 2017, set it as "15.0".
EDIT: I quitted using the .Net Framework provided MSBuild version (the one located here):
System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory()
C:\Windows\Microsoft.NET\Framework\v4.0.30319
Instead, I'm using the one located here:
C:\Program Files (x86)\MSBuild\{version}\Bin
This version provide extra parameters as LangVersion or DeployOnBuild.
EDIT: Never mind, it turns that out the task does not actually implement the Microsoft.Build.Framework.ITask interface. I had made the erroneous assumption that it did. D'oh!
I use Visual Studio 2015.
Relevant code:
string solutionFilePath = args.Single();
var fileLogger = new FileLogger { Verbosity = LoggerVerbosity.Detailed, Parameters = #"logfile=C:\MSBuildResults.txt" };
var projectCollection = new ProjectCollection {DefaultToolsVersion = "14.0"};
var buildParameters = new BuildParameters(projectCollection) { Loggers = new List<ILogger> { fileLogger } };
var globalProperties = new Dictionary<string, string> { { "Configuration", "Debug" }, { "Platform", "Any CPU" } };
var buildRequestData = new BuildRequestData(solutionFilePath,
globalProperties,
targetsToBuild: new[] { "Build" },
toolsVersion: "14.0",
hostServices: null);
BuildResult buildResult = BuildManager.DefaultBuildManager.Build(buildParameters, buildRequestData);
buildResult.OverallResult is BuildResultCode.Failure, with error messages like the following printed to my log file:
C:\Workspaces\SomeProject.csproj(67,5): error MSB4127: The "DummyTask"
task could not be instantiated from the assembly
"\networkDrive\Dummy.dll". Please verify the task assembly has been
built using the same version of the Microsoft.Build.Framework assembly
as the one installed on your computer and that your host application
is not missing a binding redirect for Microsoft.Build.Framework.
Unable to cast object of type 'DummyTask' to type
'Microsoft.Build.Framework.ITask'.
Following the advice here, I added the <bindingRedirect> tag in the .exe.config file of my program. The same error messages were thrown nonetheless.
Instead of using Microsoft.Build libraries, I decided to invoke MSBuild.exe using Process.Start() (which I know is not recommended):
var processStartInfo = new ProcessStartInfo(#"C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe")
{
Arguments = $"{solutionFilePath} /t:Build",
RedirectStandardOutput = false,
UseShellExecute = true,
CreateNoWindow = false
};
var process = Process.Start(processStartInfo);
process.WaitForExit();
process.Close();
This time, the build was successful and the process ran to completion with exit code 0.
What did I do wrong when using Microsoft.Build libraries to build my solution? What should I do instead to ensure that the build succeeds when I use BuildManager?
Let me know if you need any more information to help me with my issue.
Is it possible to compile and run C# code at runtime in the new .NET Core (better .NET Standard Platform)?
I have seen some examples (.NET Framework), but they used NuGet packages that are not compatible with netcoreapp1.0 (.NETCoreApp,Version=v1.0)
Option #1: Use the full C# compiler to compile an assembly, load it and then execute a method from it.
This requires the following packages as dependencies in your project.json:
"Microsoft.CodeAnalysis.CSharp": "1.3.0-beta1-20160429-01",
"System.Runtime.Loader": "4.0.0-rc2-24027",
Then you can use code like this:
var compilation = CSharpCompilation.Create("a")
.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
.AddReferences(
MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location))
.AddSyntaxTrees(CSharpSyntaxTree.ParseText(
#"
using System;
public static class C
{
public static void M()
{
Console.WriteLine(""Hello Roslyn."");
}
}"));
var fileName = "a.dll";
compilation.Emit(fileName);
var a = AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.GetFullPath(fileName));
a.GetType("C").GetMethod("M").Invoke(null, null);
Option #2: Use Roslyn Scripting. This will result in much simpler code, but it currently requires more setup:
Create NuGet.config to get packages from the Roslyn nightly feed:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="Roslyn Nightly" value="https://www.myget.org/F/roslyn-nightly/api/v3/index.json" />
</packageSources>
</configuration>
Add the following package as a dependency to project.json (notice that this is package from today. You will need different version in the future):
"Microsoft.CodeAnalysis.CSharp.Scripting": "1.3.0-beta1-20160530-01",
You also need to import dotnet (obsolete "Target Framework Moniker", which is nevertheless still used by Roslyn):
"frameworks": {
"netcoreapp1.0": {
"imports": "dotnet5.6"
}
}
Now you can finally use Scripting:
CSharpScript.EvaluateAsync(#"using System;Console.WriteLine(""Hello Roslyn."");").Wait();
I am just adding to svick's answer. If you want to keep the assembly in memory (rather than writing to a file) you can use the following method:
AssemblyLoadContext context = AssemblyLoadContext.Default;
Assembly assembly = context.LoadFromStream(ms);
This is different than in .NET 4.5.1 where the code is:
Assembly assembly = Assembly.Load(ms.ToArray());
My code targets both .NET 4.5.1 and .NET Standard, so I had to use directives to get around this problem. The full code example is here:
string code = CreateFunctionCode();
var syntaxTree = CSharpSyntaxTree.ParseText(code);
MetadataReference[] references = new MetadataReference[]
{
MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location),
MetadataReference.CreateFromFile(typeof(Hashtable).GetTypeInfo().Assembly.Location)
};
var compilation = CSharpCompilation.Create("Function.dll",
syntaxTrees: new[] { syntaxTree },
references: references,
options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
StringBuilder message = new StringBuilder();
using (var ms = new MemoryStream())
{
EmitResult result = compilation.Emit(ms);
if (!result.Success)
{
IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
diagnostic.IsWarningAsError ||
diagnostic.Severity == DiagnosticSeverity.Error);
foreach (Diagnostic diagnostic in failures)
{
message.AppendFormat("{0}: {1}", diagnostic.Id, diagnostic.GetMessage());
}
return new ReturnValue<MethodInfo>(false, "The following compile errors were encountered: " + message.ToString(), null);
}
else
{
ms.Seek(0, SeekOrigin.Begin);
#if NET451
Assembly assembly = Assembly.Load(ms.ToArray());
#else
AssemblyLoadContext context = AssemblyLoadContext.Default;
Assembly assembly = context.LoadFromStream(ms);
#endif
Type mappingFunction = assembly.GetType("Program");
_functionMethod = mappingFunction.GetMethod("CustomFunction");
_resetMethod = mappingFunction.GetMethod("Reset");
}
}
Both previous answers didn't work for me in a .NET Core 2.2 environment on Windows. More references are needed.
So with the help of the https://stackoverflow.com/a/39260735/710069 solution, I have ended up with this code:
var dotnetCoreDirectory = System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();
var compilation = CSharpCompilation.Create("LibraryName")
.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
.AddReferences(
MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location),
MetadataReference.CreateFromFile(typeof(Console).GetTypeInfo().Assembly.Location),
MetadataReference.CreateFromFile(Path.Combine(dotnetCoreDirectory, "mscorlib.dll")),
MetadataReference.CreateFromFile(Path.Combine(dotnetCoreDirectory, "netstandard.dll")),
MetadataReference.CreateFromFile(Path.Combine(dotnetCoreDirectory, "System.Runtime.dll")))
.AddSyntaxTrees(CSharpSyntaxTree.ParseText(
#"public static class ClassName
{
public static void MethodName() => System.Console.WriteLine(""Hello C# Compilation."");
}"));
// Debug output. In case your environment is different it may show some messages.
foreach (var compilerMessage in compilation.GetDiagnostics())
Console.WriteLine(compilerMessage);
Than output library to file:
var fileName = "LibraryName.dll";
var emitResult = compilation.Emit(fileName);
if (emitResult.Success)
{
var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(Path.GetFullPath(fileName));
assembly.GetType("ClassName").GetMethod("MethodName").Invoke(null, null);
}
or to memory stream:
using (var memoryStream = new MemoryStream())
{
var emitResult = compilation.Emit(memoryStream);
if (emitResult.Success)
{
memoryStream.Seek(0, SeekOrigin.Begin);
var context = AssemblyLoadContext.Default;
var assembly = context.LoadFromStream(memoryStream);
assembly.GetType("ClassName").GetMethod("MethodName").Invoke(null, null);
}
}