Mono / MonoDevelop: Get solution version at runtime - c#

I am looking to retrieve the application version (essentially the <ReleaseVersion> property in the solution file) at runtime. How does one access this via code?

The standard way of setting the application version in .NET (and therefore presumably MONO) is to use the AssemblyVersion attribute. This is normally specified in the AssemblyInfo.cs file, but can be specified in other files, as is shown below.
To get the version at runtime, you can use the AssemblyName.Version property. The following is a slightly modified version of the code from the given link:
using System;
using System.Reflection;
[assembly:AssemblyVersion("1.1.0.0")]
class Example
{
static void Main()
{
Console.WriteLine("The version of the currently executing assembly is: {0}",
Assembly.GetExecutingAssembly().GetName().Version);
}
}
When compiled and run, it produces this:
The version of the currently executing assembly is: 1.1.0.0
The above was tested on both .NET (Visual Studio 2010), and Mono 3.0 (compiled using mcs from the command line, not Mono Develop).

have you tried using the suggestions in this forum --> get current version

It's an ugly hack, but you can utilise the BeforeBuild target to write that property to a file, which you then promptly include as an embedded resource. In your *.csproj file:
<ItemGroup>
<EmbeddedResource Include="release-version.txt" />
</ItemGroup>
<!-- .... -->
<Target Name="BeforeBuild">
<Exec WorkingDirectory="$(ProjectDir)" Command="echo $(ReleaseVersion) >release-version.txt" />
</Target>
....then in your C&sharp; code, you could create a property like this:
public static string Version {
get {
using (StreamReader resourceReader = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream("SBRL.GlidingSquirrel.release-version.txt")))
{
return resourceReader.ReadToEnd().Trim();
}
}
}
This should work on all major operating systems. Don't forget:
To add using System.Reflection; to the top of your file if you haven't already
To add the release-version.txt file to your version control's ignore list (e.g. .gitignore, etc.)

Related

global usings and .NET Standard 2.0

I recently recognized that I can use the C# 10 feature file-scoped namespaces in .NET Standard 2.0 projects as well by setting <LangVersion>10</LangVersion> in the csproj file.
However, global usings don't work that way, I'm getting compiler errors due to missing using statements.
Are there any tweaks so that I can use global usings in a .NET Standard 2.0 library as well?
I'm not sure why it doesn't work with a separated .cs file. However, a workaround that works is using the MSBuild syntax. In your .csproj you can add the following:
<ItemGroup>
<Using Include="System.Linq" />
</ItemGroup>
There are some keywords you can use - like Alias or Static -, as you would do in a normal .cs file.
<ItemGroup>
<Using Include="Test.Namespace" Alias="Domain" />
</ItemGroup>
And then in your code, you can do the following:
namespace Test.Namespace
{
public class TestClass {}
}
namespace Another.Namespace
{
new Domain.TestClass();
}
If it helps, I found this information in the following blog post.

How to auto increment the version (eg. “1.0.*”) of a .NET Core project?

In the old .NET framework, you could set the [assembly: AssemblyVersion("1.0.*")] and the compiler would auto-increment the version.
With .NET core, I've tried all sorts of things, but I can't get it to auto-increment.
I've added <Deterministic>False</Deterministic> and <AssemblyVersion>1.0.*</AssemblyVersion> to the .csproj per similar question. The code compiles but the version stays the same
I've tried the same thing but with <Version>1.0.*</Version> tag as described here. This actually set the product version to 1.0.* (with the asterisk).
I've set the Assembly Version in the Property Properties/Package page. Doesn't seem to do anything either.
None of it seems to work. Am I missing something simple? This is just a standard .NET Core Web Project.
As indicated if you follow some of the links from the comments/answers, the most straightforward way to get .NET Framework-like version auto-incrementing in .NET Core 3/5/6 is to add to your .csproj:
<PropertyGroup>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<Deterministic>false</Deterministic>
</PropertyGroup>
And add to your Program.cs:
[assembly: System.Reflection.AssemblyVersion("1.0.*")]
You may still want to read the links if there are other AssemblyInfo properties you want to set.
One simple way I did it previously is by reading the current version and increasing it by one, so you get the current version and increment by one using the command line.
With that said, it is possible to do the following for the .net core project:
In your .csproj file, you add the following:
<PropertyGroup>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<Deterministic>false</Deterministic>
</PropertyGroup>
In your code, for instance, in your entry point class, add the following:
using System.Reflection;
[assembly: AssemblyVersion("1.0.*")]
When you build the code, it will get a version like 1.0.8419.40347.
To make more customization, check this article: https://sachabarbs.wordpress.com/2020/02/23/net-core-standard-auto-incrementing-versioning/
In addition, I added this link:
Equivalent to AssemblyInfo in dotnet core/csproj
And I use this exertion as well for Visual Studio:
https://marketplace.visualstudio.com/items?itemName=PrecisionInfinity.AutomaticVersions

Loading an external .NET Standard 2.0 assembly with blazor

In a Blazor app, I want to load an external assembly and execute a method. For this, I have created a new ASP.Net Core webapplication using the Blazor template.
Then, in a Razor Page (which will be compiled and executed by browser/wasm) I use reflection to load the assembly and run the method (based on code found here)
// download external assembly from server
HttpClient client = new HttpClient();
var bytes = await client.GetByteArrayAsync("http://localhost:62633/_framework/MyCustomLib.dll");
//load assembly
var assembly = System.Reflection.Assembly.Load(bytes);
// get type/method info
var type = assembly.GetType("MyCustomLib.MyCustomClass");
var method = type.GetMethod("WriteSomething");
// instantiate object and run method
object classInstance = Activator.CreateInstance(type, null);
method.Invoke(classInstance, null);
The method WriteSomething contains a single Console.WriteLine() which prints something in the browser's console, thanks to blazor/mono.wasm goodness. The complete code in this library is:
namespace MyCustomLib
{
public class MyCustomClass
{
public void WriteSomething()
{
System.Console.WriteLine("This is printed from a loaded dll 3 !");
}
}
}
Result:
As you can see, this works great when MyCustomLib.dll is built as a .NET Framework Class Library. However, I want to use a .NET Standard Class Library.
When I build a MyCustomLib.dll as a .NET Standard 2.0 library, and execute the same blazor app, I get the following error in the browser's console:
Could not load file or assembly 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' or one of its dependencies.
I would expect mono.wasm would have loaded the necessary dependencies to support .NET Standard assemblies.
Loading the assembly into the AppDomain yields the same result.
var assembly = AppDomain.CurrentDomain.Load(bytes);
Switching down to netstandard 1.6 gives me a similar error, this time about System.Runtime (because mono.wasm expects Mono.Runtime I assume).
Maybe there's a way to perform LoadAssembly on the assemblies referenced by the netstandard2.0 package, but I wouldn't know how.
How can I load a .NET Standard 2.0 into the browser environment using Blazor?
After doing some further investigation, I've concluded that my problem is that my external library is not properly linked to the mono.net dependencies. This is why, when you build a Blazor app, it is compiled a second time to /dist/_framework/_bin.
I've found three possible solutions to this problem:
1. Turn the external class library into a Blazor Web app
This way, your app will automatically be converted to a mono-compatible assembly when built. A simple peek in to a Blazor .csproj shows the dependencies needed to achieve this. For it to work, I had to change the .csproj of my external assembly:
from a default netstandard library:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
</Project>
into a web app:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RunCommand>dotnet</RunCommand>
<LangVersion>7.3</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Blazor.Build" Version="0.7.0" PrivateAssets="all" />
</ItemGroup>
</Project>
These are the only dependencies needed. On build, the compatible assembly will be found in the /dist/_framework/_bin folder. It can then be loaded using the methods described in the question.
This works, but feels a bit hacky because the only reason we're turning the library into a web app is so that it can compile itself into a properly linked assembly.
2. Load the netstandard2.0 mono facade
Another solution is to unzip the Nuget Package from Microsoft.AspNetCore.Blazor.Build and grab the netstandard.dll. It's found in the tools\mono\bcl\Facades folder. Now, when doing the following in the main Blazor app:
var netstandard = await client.GetByteArrayAsync("http://localhost:62633/_framework/netstandard.dll");
var externallib = await client.GetByteArrayAsync("http://localhost:62633/_framework/MyCustomLib.dll");
AppDomain.CurrentDomain.Load(netstandard);
var assembly = AppDomain.CurrentDomain.Load(externallib);
then the unmodified netstandard 2.0 library MyCustomLib will be loaded without errors.
No need to change it to a web app
This works, but it feels even hackier than the first solution, unsure whether this will fail later along the way...
3. Use the Blazor Build tools
The Blazor Build tools, currently found here, they have a ResolveRuntimeDependenciesCommand command for the CLI which seems to do exactly what a blazor web app is doing when it spits output to /_framework/_bin.
I'm still looking at how this could be used to convert a "non blazor-webapp" assembly into a mono-compatible one.
Feel free to comment or answer with additional information. I'm leaving this question open until a "cleaner" solution is found.
This is caused by the blazor linker stripping out the dependency to netstandard since it's not explicitly used in the main WebAssemblyHost project.
When dynamically loading a DLL that uses netstandard, it will assume that it was already loaded and crash.
If you plan to load assemblies dynamically, I recommend using
<BlazorWebAssemblyEnableLinking>false</BlazorWebAssemblyEnableLinking>
to ensure non of the base dependencies is stripped out.

Can I define constants based on the runtime identifier in .NET Core?

I have a .NET Core Console application. My goal here is to be able to conditionally DLLImport a function and call it, but only on Windows runtimes.
I thought maybe if I could access the runtime identifier in the csproj file, I could conditionally define a constant for that runtime, then in my c# I could surround the DLLImport and calls in #if/#endif blocks.
Is it possible to set compilation constants within a csproj based on the runtime the project is being built for? This is specifically for an SDK-style Project format (that starts with <Project Sdk="Microsoft.NET.Sdk">) that is targeting .NET Core.
Note: this question gets close, but is for project.json style projects.
Alternately, is there a better approach to accomplish my goal?
If you are building and publishing for different runtimes by passing different --runtime options (MSBuild property RuntimeIdentifier), you can condition on that property in the csproj file (allowing you to use #if BUILT_FOR_WINDOWS in your C# code):
<PropertyGroup>
<DefineConstants Condition="'$(RuntimeIdentifier)' == 'win-x64'">$(DefineConstants);BUILT_FOR_WINDOWS</DefineConstants>
</PropertyGroup>
However you can also test the current OS at run time using:
using System.Runtime.InteropServices;
…
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
// call windows function here
}
else
{
// do something else here
}
As long as a function marked with [DllImport(…)] is not called on an OS where the library / method cannot be found, there shouldn't be any problems. Do note that DllImport() can also probe for different libraries depending on the os - so DllImport("foo") would check for foo.dll, foo.dylib, libfoo.so etc.
Adding to a Martin Ullrich's answer: if you want to define constants based on RuntimeIdentifier in a referenced library project as opposed to a project with application entry point make sure that you include the list of identifiers which you use in a RuntimeIdentifiers property in the project's .csproj file, for example:
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RuntimeIdentifiers>linux-x64;linux-arm</RuntimeIdentifiers>
</PropertyGroup>
If you don't do it then the constants will not be defined as RuntimeIdentifier property will not be passed to the csproj, as was in my case.
Source: https://github.com/dotnet/core/issues/2678#issuecomment-498967871

C#: Including assemblies without visual studio

How do I include an external assembly .dll in a C# .exe? I'm specifically working with the MySql connectors for .NET 4.0.
Please note, I'm not using Visual Studio at this time and do not plan to in order to accomplish this. Here's the error I get when I compile: CS0246: The type or namespace MySql could not be found (are you missing a using directive or an assembly reference?)
I have tried:
- Copying the .dlls into the source folder for the project.
- Copying the .dlls into the .NET folder under Windows
The first three lines of code are:
using System;
using MySql;
using MySql.Data;
Sample Program (Program.cs)
using System;
using MySql.Data.MySqlClient;
namespace TestApp
{
internal class Program
{
private static void Main(string [] args)
{
Console.WriteLine("Hello world!");
var command = new MySqlCommand();
Console.WriteLine(command.ToString());
}
}
}
Project file (TestApp.csproj)
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Compile Include="Program.cs" />
</ItemGroup>
<Target Name="Build">
<Csc Sources="#(Compile)" AdditionalLibPaths="lib" References="MySql.Data.dll">
</Csc>
</Target>
</Project>
MySql.Data.dll is in a folder named lib
Run msbuild from the command line
msbuild TestApp.csproj /t:Build
Microsoft's guide to writing your own msbuild files.
https://msdn.microsoft.com/en-us/library/dd576348.aspx
csc task details
https://msdn.microsoft.com/en-us/library/s5c8athz.aspx
You can use the /reference (or /r) command line option:
csc yourcode.cs /r:MySql.Data.dll
Assuming that, as you said, you have copied the MySql.Data.dll into the source folder for the project.
If you haven't copied the dynamic link libray to your source folder, then you can use the /lib: command line option to specify additional directories to search in for your references.

Categories

Resources