C# 7 Tuples and names in .NET Core - c#

With C# 7 new Tuple feature we should be able to access fields by it's names derived from the type.
public (double lat, double lng) GetLatLng(string address) { ... }
var ll = GetLatLng("some address");
Console.WriteLine($"Lat: {ll.lat}, Long: {ll.lng}");
This is not possible in .NET Core. Why? -> Works only with Item1; Item2. Not with .lat .lng.
Thanks

UPDATE
Visual Studio 2017 Intellisense may be slow to update itself after adding the System.ValueTuple package and keep displaying error squigglies even when there is no compilation error. Compiling the project though shows that named tuples are working. A quick fix is to re-open the source file or solution.
ORIGINAL
The error message explains that 'Predefined type System.ValueTuple'2 is not defined or imported. You need to add the System.ValueTuple package from NuGet in order to use named tuples.
Once you add the package, the code compiles:
class Program
{
static (double lat, double lng) GetLatLng(string address)
{
return (1, 1);
}
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
var ll = GetLatLng("some address");
Console.WriteLine($"Lat: {ll.lat}, Long: {ll.lng}");
}
}
Scott Hanselman shows how to configure Visual Studio 2017 to automatically suggest NuGet packages for missing types by enabling the settings in Options > Text Editor > C# > Advanced > Using Directives.
After you enable the Suggest usings for types in NuGet packages setting, the Quick Fix menu for the missing tuples shows Install package 'System.ValueTuple' :
The Find this type on nuget.org menu is a similar ReSharper feature

Related

Blazor proj from scratch+nuget: "There was no runtime pack for Microsoft.AspNetCore.App available for the specified RuntimeIdentifier 'browser-wasm'"

[Final update 07.12.2020 23:50 --> for everyone seeking for the anwser]
You are probably adding some nuget package which has in fields DEPENDENCIES .NET Core 3.1. For example Serilog.AspNetCore 3.4.0. In this very example you can include Serilog.AspNetCore 3.2.0 because it has in DEPENDENCIES only .NetStandard 2.0. See more explanation in #Ogglas answer.
There is not enough space on earth to ask the one and only question: "Why, o why Microsoft?":) The message is not clearly correlating issue with the solution - this is the lightest euphemism i am capable of making :)
For me, Blazor is very promising but it is still more a beta version. And not only for me. The startblazoring guys are claiming, that Blazor can be fickle sometimes. And I cannot agree more;) I am keeping finger crossed for it. But I suppose it would be a "ready product" only after .NET 6.0.
[Original Topic]
This is so frustrating :)
I hate errors, when I cannot simply interact and understood where it is coming from. I know, I am ignorant, and If I would pay more attention to "how is the blazor app created" I would understand in eye blink.
But for now it is only frustrating. What is? The error message:
Severity Code Description Project File Line Suppression State
Error NETSDK1082 There was no runtime pack for Microsoft.AspNetCore.App available for the specified RuntimeIdentifier 'browser-wasm'. TestBlazorAppNoRuntimeError.Client C:\Program Files\dotnet\sdk\5.0.100\Sdks\Microsoft.NET.Sdk\targets\Microsoft.NET.Sdk.FrameworkReferenceResolution.targets 387
Really, what can you fix here?:D Ehhhhh. I understand that .NET 5 is a "new thing". I understand Blazor is a "new thing" But really? It is really easy to "break" ;) (with a message that is saying nothing)
So what did I do?
Did I create very complicated project with hundreds of thousands of files?
No
Did I add hundreds of thousands of nuget packages.
No
All it takes is to create Blazor Webassembly app and add one nuget package to the shared (common) library (FYI: IdentityServer4 package). That is all folks. Kudos if you would now, that this package specificly is causing this issue (because i found out only by mistake):
There was no runtime pack for Microsoft.AspNetCore.App available for the specified RuntimeIdentifier 'browser-wasm'.
Really, there is a great correlation here: between the error message and the package being added (this was sarcasm if anyone missed it:) )
So if you want to reproduce it create create "Blazor Webassembly":
I don't suppose the options chosen are making the difference. But if this is the case - you can see what option I did choose. Then just add IdentityServer4 nuget package to shared library:
https://www.nuget.org/packages/IdentityServer4/
That is all
If you are too lazy - download this project and check for yourself:
https://ufile.io/751l5wgq
I saw hundreds of SOF topics regarding this message:
There was no runtime pack for Microsoft.AspNetCore.App available for the specified RuntimeIdentifier 'browser-wasm'.
But none of those was so easy to reproduce.
Why I am creating this topic?
Because I did try to implement (by copy-pasting to my solution) what guys did create in Blazor boilerplate here:
https://github.com/enkodellc/blazorboilerplate
At the beginning, by mistake, I've added Identity4 NuGet instead of Identity4.storage and that is why I did observe such behavior (such error) and did correlate this package with this error. When I rolled back from Identity4 to Identity4.storage everything was correct once again. But after that I did a lot of code transfer from this repository. And once I've ended, right now, everything is compiling but once again I have:
There was no runtime pack for Microsoft.AspNetCore.App available for the specified RuntimeIdentifier 'browser-wasm'.
And it is driving me crazy. Maybe you would have some idea, how to find out WHAT is causing this error (exactly the same as previously Identity4 nuget package was causing it)
[UPDATE 06.12.2020 12:30]
The question here is more: "Why is it happening and how to locate package causing the issue" I know that I can second guess what is causing this message to appear.
I did a little investigation. This error is showing in this target:
<ResolveRuntimePackAssets FrameworkReferences="#(FrameworkReference)"
ResolvedRuntimePacks="#(ResolvedRuntimePack)"
UnavailableRuntimePacks="#(UnavailableRuntimePack)"
SatelliteResourceLanguages="$(SatelliteResourceLanguages)"
DesignTimeBuild="$(DesignTimeBuild)">
<Output TaskParameter="RuntimePackAssets" ItemName="RuntimePackAsset" />
</ResolveRuntimePackAssets>
<ItemGroup>
<ReferenceCopyLocalPaths Include="#(RuntimePackAsset)"
Condition="'$(CopyLocalLockFileAssemblies)' == 'true' and ('$(SelfContained)' == 'true' or '%(RuntimePackAsset.RuntimePackAlwaysCopyLocal)' == 'true')" />
</ItemGroup>
so I did google ResolveRuntimePackAssets and found this repo and this file:
https://github.com/dotnet/sdk/blob/release/5.0.2xx/src/Tasks/Microsoft.NET.Build.Tasks/ResolveRuntimePackAssets.cs and such code:
foreach (var unavailableRuntimePack in UnavailableRuntimePacks)
{
if (frameworkReferenceNames.Contains(unavailableRuntimePack.ItemSpec))
{
// This is a runtime pack that should be used, but wasn't available for the specified RuntimeIdentifier
// NETSDK1082: There was no runtime pack for {0} available for the specified RuntimeIdentifier '{1}'.
Log.LogError(Strings.NoRuntimePackAvailable, unavailableRuntimePack.ItemSpec,
unavailableRuntimePack.GetMetadata(MetadataKeys.RuntimeIdentifier));
}
}
Looks like the code "causing" the issue (this calls for writing down information in message about: unavailableRuntimePack.ItemSpec - it would be so much easier;))
So right now the only thing is to locate what is filling this: UnavailableRuntimePacks property. I don't see any code doing that so it have to be some reflection. I've located this code which is probably doing it:
https://github.com/dotnet/sdk/blob/release/5.0.2xx/src/Tasks/Microsoft.NET.Build.Tasks/ProcessFrameworkReferences.cs
And this method:
private void ProcessRuntimeIdentifier(
string runtimeIdentifier,
KnownRuntimePack selectedRuntimePack,
string runtimePackVersion,
List<string> additionalFrameworkReferencesForRuntimePack,
HashSet<string> unrecognizedRuntimeIdentifiers,
List<ITaskItem> unavailableRuntimePacks,
List<ITaskItem> runtimePacks,
List<ITaskItem> packagesToDownload,
string isTrimmable,
bool addToPackageDownload)
{
var runtimeGraph = new RuntimeGraphCache(this).GetRuntimeGraph(RuntimeGraphPath);
var knownFrameworkReferenceRuntimePackRuntimeIdentifiers = selectedRuntimePack.RuntimePackRuntimeIdentifiers.Split(';');
string runtimePackRuntimeIdentifier = NuGetUtils.GetBestMatchingRid(
runtimeGraph,
runtimeIdentifier,
knownFrameworkReferenceRuntimePackRuntimeIdentifiers,
out bool wasInGraph);
if (runtimePackRuntimeIdentifier == null)
{
if (wasInGraph)
{
// Report this as an error later, if necessary. This is because we try to download
// all available runtime packs in case there is a transitive reference to a shared
// framework we don't directly reference. But we don't want to immediately error out
// here if a runtime pack that we might not need to reference isn't available for the
// targeted RID (e.g. Microsoft.WindowsDesktop.App for a linux RID).
var unavailableRuntimePack = new TaskItem(selectedRuntimePack.Name);
unavailableRuntimePack.SetMetadata(MetadataKeys.RuntimeIdentifier, runtimeIdentifier);
unavailableRuntimePacks.Add(unavailableRuntimePack);
}
else if (!unrecognizedRuntimeIdentifiers.Contains(runtimeIdentifier))
{
// NETSDK1083: The specified RuntimeIdentifier '{0}' is not recognized.
Log.LogError(Strings.RuntimeIdentifierNotRecognized, runtimeIdentifier);
unrecognizedRuntimeIdentifiers.Add(runtimeIdentifier);
}
}
else if (addToPackageDownload)
{
foreach (var runtimePackNamePattern in selectedRuntimePack.RuntimePackNamePatterns.Split(';'))
{
string runtimePackName = runtimePackNamePattern.Replace("**RID**", runtimePackRuntimeIdentifier);
if (runtimePacks != null)
{
TaskItem runtimePackItem = new TaskItem(runtimePackName);
runtimePackItem.SetMetadata(MetadataKeys.NuGetPackageId, runtimePackName);
runtimePackItem.SetMetadata(MetadataKeys.NuGetPackageVersion, runtimePackVersion);
runtimePackItem.SetMetadata(MetadataKeys.FrameworkName, selectedRuntimePack.Name);
runtimePackItem.SetMetadata(MetadataKeys.RuntimeIdentifier, runtimePackRuntimeIdentifier);
runtimePackItem.SetMetadata(MetadataKeys.IsTrimmable, isTrimmable);
if (selectedRuntimePack.RuntimePackAlwaysCopyLocal)
{
runtimePackItem.SetMetadata(MetadataKeys.RuntimePackAlwaysCopyLocal, "true");
}
if (additionalFrameworkReferencesForRuntimePack != null)
{
runtimePackItem.SetMetadata(MetadataKeys.AdditionalFrameworkReferences, string.Join(";", additionalFrameworkReferencesForRuntimePack));
}
runtimePacks.Add(runtimePackItem);
}
TaskItem packageToDownload = new TaskItem(runtimePackName);
packageToDownload.SetMetadata(MetadataKeys.Version, runtimePackVersion);
packagesToDownload.Add(packageToDownload);
}
}
}
After analysing it, the reason why Identity4 (for example) is causing this issue is because runtimePackRuntimeIdentifier is null and wasInGraph was true :) But what does mean, we have to dig a little bit further. The key thing here is the method GetBestMatchingRid:
public static string GetBestMatchingRid(RuntimeGraph runtimeGraph, string runtimeIdentifier,
IEnumerable<string> availableRuntimeIdentifiers, out bool wasInGraph)
{
wasInGraph = runtimeGraph.Runtimes.ContainsKey(runtimeIdentifier);
HashSet<string> availableRids = new HashSet<string>(availableRuntimeIdentifiers);
foreach (var candidateRuntimeIdentifier in runtimeGraph.ExpandRuntime(runtimeIdentifier))
{
if (availableRids.Contains(candidateRuntimeIdentifier))
{
return candidateRuntimeIdentifier;
}
}
// No compatible RID found in availableRuntimeIdentifiers
return null;
}
So here we see that runtimePackRuntimeIdentifier was null because it was not found on: availableRuntimeIdentifiers. And this variable is defined as:
var knownFrameworkReferenceRuntimePackRuntimeIdentifiers = selectedRuntimePack.RuntimePackRuntimeIdentifiers.Split(';');
So let's see how is it defined (in the code below variable runtimePackForRuntimeIDProcessing is selectedRuntimePack from the above mention)
KnownRuntimePack runtimePackForRuntimeIDProcessing;
if (knownFrameworkReference.Name.Equals(knownFrameworkReference.RuntimeFrameworkName, StringComparison.OrdinalIgnoreCase))
{
// Only add runtime packs where the framework reference name matches the RuntimeFrameworkName
// Framework references for "profiles" will use the runtime pack from the corresponding non-profile framework
runtimePackForRuntimeIDProcessing = selectedRuntimePack.Value;
includeInPackageDownload = true;
}
else if (!knownFrameworkReference.RuntimePackRuntimeIdentifiers.Equals(selectedRuntimePack?.RuntimePackRuntimeIdentifiers))
{
// If the profile has a different set of runtime identifiers than the runtime pack, use the profile.
runtimePackForRuntimeIDProcessing = knownFrameworkReference.ToKnownRuntimePack();
includeInPackageDownload = true;
}
else
{
// For the remaining profiles, don't include them in package download but add them to unavaliable if necessary.
runtimePackForRuntimeIDProcessing = knownFrameworkReference.ToKnownRuntimePack();
includeInPackageDownload = false;
}
But this is as far as I can go. I don't understand without further investigation how this selectedRuntimePack.RuntimePackRuntimeIdentifiers should be defined. This is the key thing here. Clearly for some reasons IdentityServer4 doesn't belong to selectedRuntimePack.RuntimePackRuntimeIdentifiers and this is the answer I am looking for :)
IdentityServer4 4.1.1 (latest version) is dependent upon .NETCoreApp 3.1
https://www.nuget.org/packages/IdentityServer4/
If you create a Blazor WebAssembly App using .NET Core 3.1 it will work in the Server App.
However when using .NET Core 3.1 both Client and Shared uses .NET Standard 2.1 and is therefore not compatible. These error messages are a lot clearar though:
Error Package restore failed. Rolling back package changes for
'BlazorApp.Shared'.
Error NU1202 Package IdentityServer4 4.1.1 is not compatible with
netstandard2.1 (.NETStandard,Version=v2.1). Package IdentityServer4
4.1.1 supports: netcoreapp3.1 (.NETCoreApp,Version=v3.1) BlazorApp.Shared
As I mentioned in this link, it seems there is an issue regarding to the following package. So remove the package from your project and every other project that has been referenced by your Blazor project(right-click on project, choose edit project file then remove the following):
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.0" />
In my case (and hopefully in yours too) the only use of this package was the get the constant string "Bearer" with the field member JwtBearerDefaults.AuthenticationScheme. So instead of this just simply use "Bearer" string.
I had the same issue while working on a blazor wasm/server/shared projects. In my case, I narrowed down the issue to the project sdk types:
Client.csproj:
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
Server.csproj:
<Project Sdk="Microsoft.NET.Sdk.Web">
Shared.csproj definition causing the issue:
<Project Sdk="Microsoft.NET.Sdk.Razor">
Shared.csproj definition without issue:
<Project Sdk="Microsoft.NET.Sdk">
The client wasm project did not like that the shared one was declared as Microsoft.NET.Sdk.Razor and had to change it to Microsoft.NET.Sdk in order to get rid of the error in the title.
I still need to dig further in to better understand the full differences between those two project types but I hope this can help anyone else facing this issue.

ASP.NET Opening File in Zip Folder [duplicate]

I can't use "Zipfile" class in the name space "System.IO.Compression" my code is :
using System;
using System.IO;
using System.IO.Compression;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
string startPath = #"c:\example\start";
string zipPath = #"c:\example\result.zip";
string extractPath = #"c:\example\extract";
ZipFile.CreateFromDirectory(startPath, zipPath, CompressionLevel.Fastest,true);
ZipFile.ExtractToDirectory(zipPath, extractPath);
}
}
}
the error is :
The name 'zipfile' does not exist in the current context
How I can solve it ?
You need an extra reference for this; the most convenient way to do this is via the NuGet package System.IO.Compression.ZipFile
<!-- Version here correct at time of writing, but please check for latest -->
<PackageReference Include="System.IO.Compression.ZipFile" Version="4.3.0" />
If you are working on .NET Framework without NuGet, you need to add a dll reference to the assembly, "System.IO.Compression.FileSystem.dll" - and ensure you are using at least .NET 4.5 (since it doesn't exist in earlier frameworks).
For info, you can find the assembly and .NET version(s) from MSDN
For those who are green programmers in .NET, to add the DLL reference as MarcGravell noted, you follow these steps:
To add a reference in Visual C#
In Solution Explorer, right-click the project node and click Add Reference.
In the Add Reference dialog box, select the tab indicating the type of component you want to reference.
Select the components you want to reference, and then click OK.
From the MSDN Article, How to: Add or Remove References By Using the Add Reference Dialog Box.
you can use an external package if you cant upgrade to 4.5. One such is Ionic.Zip.dll from DotNetZipLib.
using Ionic.Zip;
you can download it here, its free. http://dotnetzip.codeplex.com/
Just go to References and add "System.IO.Compression.FileSystem".
In solution explorer, right-click References, then click to expand assemblies, find System.IO.Compression.FileSystem and make sure it's checked. Then you can use it in your class - using System.IO.Compression;
Add Reference Assembly Screenshot
A solution that helped me:
Go to Tools > NuGet Package Manager > Manage NuGet Packaged for Solution... > Browse >
Search for System.IO.Compression.ZipFile and install it
System.IO.Compression is now available as a nuget package maintained by Microsoft.
To use ZipFile you need to download System.IO.Compression.ZipFile nuget package.
I know this is an old thread, but I just cannot steer away from posting some useful info on this. I see the Zip question come up a lot and this answers nearlly most of the common questions.
To get around framework issues of using 4.5+... Their is a ZipStorer class created by jaime-olivares: https://github.com/jaime-olivares/zipstorer, he also has added an example of how to use this class as well and has also added an example of how to search for a specific filename as well.
And for reference on how to use this and iterate through for a certain file extension as example you could do this:
#region
/// <summary>
/// Custom Method - Check if 'string' has '.png' or '.PNG' extension.
/// </summary>
static bool HasPNGExtension(string filename)
{
return Path.GetExtension(filename).Equals(".png", StringComparison.InvariantCultureIgnoreCase)
|| Path.GetExtension(filename).Equals(".PNG", StringComparison.InvariantCultureIgnoreCase);
}
#endregion
private void button1_Click(object sender, EventArgs e)
{
//NOTE: I recommend you add path checking first here, added the below as example ONLY.
string ZIPfileLocationHere = #"C:\Users\Name\Desktop\test.zip";
string EXTRACTIONLocationHere = #"C:\Users\Name\Desktop";
//Opens existing zip file.
ZipStorer zip = ZipStorer.Open(ZIPfileLocationHere, FileAccess.Read);
//Read all directory contents.
List<ZipStorer.ZipFileEntry> dir = zip.ReadCentralDir();
foreach (ZipStorer.ZipFileEntry entry in dir)
{
try
{
//If the files in the zip are "*.png or *.PNG" extract them.
string path = Path.Combine(EXTRACTIONLocationHere, (entry.FilenameInZip));
if (HasPNGExtension(path))
{
//Extract the file.
zip.ExtractFile(entry, path);
}
}
catch (InvalidDataException)
{
MessageBox.Show("Error: The ZIP file is invalid or corrupted");
continue;
}
catch
{
MessageBox.Show("Error: An unknown error ocurred while processing the ZIP file.");
continue;
}
}
zip.Close();
}
Add System.IO.Compression.ZipFile as nuget reference it is working
The issue here is that you just Added the reference to System.IO.Compression it is missing the reference to System.IO.Compression.Filesystem.dll
And you need to do it on .net 4.5 or later (because it doesn't exist on older versions).
I just posted a script on TechNet Maybe somebody would find it useful it requires .net 4.5 or 4.7
https://gallery.technet.microsoft.com/scriptcenter/Create-a-Zip-file-from-a-b23a7530

SLF4J Error when using CoreNLP in C#

I want to include Stanford CoreNLP in my Unity3D project. I included CoreNLP from Nuget and downloaded the NLP models from CoreNLP. Then I copied the NLP model folder into the project -> bin -> Debug folder.
The code looks like this:
var jarRoot = #"stanford-corenlp-3.9.1-models\";
const string text = "Kosgi Santosh sent an email to Stanford University. He didn't get a reply.";
var props = new Properties();
props.setProperty("annotators", "tokenize, ssplit, pos, lemma, ner, parse, dcoref");
props.setProperty("sutime.binders", "0");
var curDir = Environment.CurrentDirectory;
Directory.SetCurrentDirectory(jarRoot);
var pipeline = new StanfordCoreNLP(props);
Directory.SetCurrentDirectory(curDir);
// Annotation
var annotation = new Annotation(text);
pipeline.annotate(annotation);
var sentences = annotation.get(typeof(CoreAnnotations.SentencesAnnotation));
if (sentences == null)
{
return;
}
foreach (Annotation sentence in sentences as ArrayList)
{
System.Console.WriteLine(sentence);
}
After running, I only got some Error info
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for
further details.
I searched SLF4J site however the solution only applies to Java project. How do I supposed to solve this in my C# project?
First, go to the Visual Studio (I have VS 2017). Then go to the Tools menu and select NuGet Package Manager->Package Manager Console. The Package Manager Console will appear. Type this command: Install-Package slf4j-NetCommonLogging -Version 1.7.5.4 and press Enter key. The VS will install slf4j-NetCommonLogging dll file for your project and it will run correctly without any errors or warnings. Enjoy.

How to read the list of NuGet packages in packages.config programatically?

What's the best way to read (ideally via C#) the packages listed in packages.config files?
Within our source code repository I have a lot of solutions and projects and equally a lot of packages.config files. I'm trying to build a consolidated list of packages (and versions) in use across my source code repository.
I can see there is a NuGet.Core package available - how could I use this to achieve my goal?
Thanks
If you do not want to read the XML directly you can install the NuGet.Core NuGet package and then use the PackageReference class.
Here is some example code that uses this class to print out the package id and its version.
string fileName = #"c:\full\path\to\packages.config";
var file = new PackageReferenceFile(fileName);
foreach (PackageReference packageReference in file.GetPackageReferences())
{
Console.WriteLine("Id={0}, Version={1}", packageReference.Id, packageReference.Version);
}
You will need to find the packages.config files yourself which you can probably do with a directory search, something like:
foreach (string fileName in Directory.EnumerateFiles("d:\root\path", "packages.config", SearchOption.AllDirectories))
{
// Read the packages.config file...
}
An alternative and more up to date way of doing this is to install the NuGet.Packaging NuGet package and use code similar to:
var document = XDocument.Load (fileName);
var reader = new PackagesConfigReader (document);
foreach (PackageReference package in reader.GetPackages ())
{
Console.WriteLine (package.PackageIdentity);
}
As suggested you will need to install NuGet.Core, your solution may have several projects in it, so it's good to know how to specify the project name when installing. Let's say your Solution is MySolution and you have two projects Project01 & Project02 and you only want to install in Project02.
Install-Package NuGet.Core -ProjectName Project02
Next you will need to add a using statement in the whatever.cs page you are going to do your work to target the package and let's say you just want to get the version number so that you can print it out somewhere on your website. That is actually what I wanted to do.
using NuGet;
next I wanted to get at a specific package and read it's version number so that when we release my software I have a visual identifier at a certain place on my website that I can go to and see the version that is in production.
here is the code I wrote to populate a webforms label on my page.
protected void Page_Load(Object sender, EventArgs e)
{
var pkgRefpath = Server.MapPath("~/packages.config");
PackageReferenceFile nugetPkgConfig = new PackageReferenceFile(pkgRefpath);
IEnumerable<PackageReference> allPackages = nugetPkgConfig.GetPackageReferences();
var newtonsoftPkg = (
from pkg in allPackages
where pkg.Id == "Newtonsoft.Json"
select pkg
).FirstOrDefault();
if (newtonsoftPkg== null) return;
var newtonsoftPkg_Version = newtonsoftPkg.Version;
ltrNewtonsoftVer.Text = newtonsoftPkg_Version.ToString();
}
This is a slightly different answer to the question, but this shows the solution that I ended up with for my needs after finding this Question/Answer and modifying what I learned to suit my own needs. I hope it can help someone else out.

How do I use the Mono.CSharp interpreter in Microsoft.NET

I was under the impression Mono's compiler was usable in Microsoft.NET
edit: updated blog posting here that I originally missed that explains some of it (is consistent with Justin's answers)
I created a simple class to try to use it
[TestFixture]
class Class1
{
[Test]
public void EXPR()
{
Evaluator.Run("using System;");
int sum = (int)Evaluator.Evaluate("1+2");
}
}
And a project in Visual Studio 2010 that references C:\Program Files (x86)\Mono-2.10.1\lib\mono\4.0\Mono.CSharp.dll.
However when I try to run this task I get the following exception, thrown at the Evaluator.Run call:
System.TypeInitializationException was unhandled by user code
Message=The type initializer for 'Mono.CSharp.Evaluator' threw an exception.
Source=Mono.CSharp
TypeName=Mono.CSharp.Evaluator
StackTrace:
at Mono.CSharp.Evaluator.Run(String statement)
at Experiments.Class1.EXPR() in W:\Experiments\Class1.cs:line 16
InnerException: System.TypeLoadException
Message=Method 'Mono.CSharp.Location.ToString()' is security transparent, but is a member of a security critical type.
Source=Mono.CSharp
TypeName=Mono.CSharp.Location.ToString()
StackTrace:
at Mono.CSharp.Evaluator..cctor()
InnerException:
A google confirms one other person asking this question but no answer. I tried to start reading the microsoft article on security transparent code but got confused quite quickly. Would someone be able to suggest a quick workaround to allow me to use this? And possibly summarise the security implications, if any, to me (in the context of my situation - in the future I hope to package it with a thick client application, to be used both internally and by end-users)
It has worked under .NET since April of last year.
Small point but I notice you are missing a semi-colon in your expression for sum.
int sum = (int)Evaluator.Evaluate("1+2;");
I only have Mono 2.11 (from git) at the moment and they have changed to using a multi-instance version of the compiler instead of the static version. So, my code looks a little different:
using System;
using Mono.CSharp;
namespace REPLtest
{
class MainClass
{
public static void Main (string[] args)
{
var r = new Report (new ConsoleReportPrinter ());
var cmd = new CommandLineParser (r);
var settings = cmd.ParseArguments (args);
if (settings == null || r.Errors > 0)
Environment.Exit (1);
var evaluator = new Evaluator (settings, r);
evaluator.Run("using System;");
int sum = (int) evaluator.Evaluate("1+2;");
Console.WriteLine ("The sum of 1 + 2 is {0}", sum);
}
}
}
EDIT: I guess I should confirm that I did in fact successfully execute this on .NET 4 (using Visual C# Express 2010 on Windows XP)
EDIT AGAIN: If you have Visual Studio, you can download the latest version of Mono.CSharp and compile it yourself. There is a .sln (solution file) included with the source so you can build it on Windows without Mono. The resulting assembly would run the code above. Miguel has a post explaining the new Mono.CSharp here.
FINAL EDIT: I uploaded the compiled Mono.CSharp.dll assembly that I actually used here. Include it as a reference to compile the code above.
It looks like this is a bug in Mono.
.NET 4 abandoned Code Access Security but kept the concept of Security Transparent Code. In a nutshell, low-level code that does stuff, like call unmanaged code, must be "security critical". Application level code is marked "transparent". "Transparent" code cannot call into "security critical" code.
It sounds like Mono.CSharp.Location.ToString() needs to be marked with the [SecuritySafeCritical] attribute if you want the Mono 2.10 code to work with .NET 4. Maybe even better would be marking all of Mono.CSharp as SecuritySafeCritical.
http://msdn.microsoft.com/en-us/library/system.security.securitycriticalattribute.aspx
PS. Sorry to have multiple answers for one question. After I realized that 2.11 would work, I became more curious about what the error with 2.10 meant. I cannot really combine this answer with the others.
I decided I should have kept the code more like the question but I did not want to overwrite my previous answer:
The code below works with version 2.11 of Mono.CSharp (available here including a solution file for building with Visual Studio/.NET). It was tested with .NET 4 on Windows XP. I do not have access to Mono 2.10 at the moment.
[TestFixture]
class Class1
{
private Evaluator evaluator;
public Class1()
{
var report = new Report(new ConsoleReportPrinter());
evaluator = new Evaluator(new CompilerSettings(), report);
}
[Test]
public void EXPR()
{
evaluator.Run("using System;");
int sum = (int)evaluator.Evaluate("1+2;");
}
}
EDIT: I uploaded the Mono.CSharp.dll assembly that I actually used here. Include it as a reference to compile the code above.

Categories