Windows: Set Environment variables for users

For some cases you might need to set environment variables for your .NET application.
This is true to .NET core too, when you are storing your application settings in these variables.

How to set up a script for the job

It’s handy to create a windows command line script for creating these variables, because when you are on a new work environment, than you don’t need to look up the keys and values.

So bring up your most-loved text editor, make your environment_variable_creator.bat, and add the following code:

@echo on
SETX VARIABLE1 VALUE
SETX PREFIX_VARIABLE2 true
pause

If you are using .NET core’s configuration builder with a prefix like this:

				ConfigurationBuilder cfgBuilder = new ConfigurationBuilder();
				cfgBuilder.AddEnvironmentVariables("PREFIX_");
				IConfigurationRoot configuration = cfgBuilder.Build();
				var value = configuration["VARIABLE2"];

Then you need to set your variables with prefix “PREFIX_” in the bat file. In this scenario, VARIABLE1 can not be read, because it does not contain the prefix.

The code sets the variables only for your user account, not for others. You can parameterize the SETX command to set the settings for a different user, but i recommend, to run the script with a command line for the targeted user, because its much easier.

To run your script under other users name, then open your command prompt with ‘Run as… different user’

.NET Core: Using AssemblyInfo shared between assemblies

There was a time when project.json has replaced the AssemblyInfo. But since .NET core uses .csproj, instead of the project.json, AssemblyInfo comes to the development again.

If you want to version your several different assemblies together, then follow this tutorial.

Add a shared AssemblyInfo.cs

Right click on your solution, and select Add > New Item..

In the following screen, select Visual C# class, and name it as ‘AssemblyInfo.cs’

Replace the file content with the example below:

using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following 
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyVersion("1.0.0")]
[assembly: AssemblyFileVersion("1.0.0")]
[assembly: AssemblyDescription("Insert your description here")]
[assembly: AssemblyCompany("YourAwesomeCompany")]
[assembly: AssemblyCopyright("Copyright © YourAwesomeCompany 2021")]

Set your projects to use the shared assembly info

Do this all of your projects where you want to use the shared assembly info file.

Edit your project file with a text editor (or right click on project > Edit project file)

Add the following tag to the PropertyGroup tag in order to diable automatic assemblyinfo.cs generation:

<PropertyGroup>
....
  <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
....
</PropertyGroup>

Add the shared assembly info to be used with inserting the following tags to between the project tags:

  <ItemGroup>
    <Compile Include="..\AssemblyInfo.cs" Link="Properties\AssemblyInfo.cs" />
  </ItemGroup>

If the project has no Properties folder, like .NET Standard projects basically do, then add the new folder also with this snippet:

  <ItemGroup>
    <Folder Include="Properties\" />
  </ItemGroup>

.NET Core: Type serialization denied

When trying to return with a complex object in .NET Core API, which has a Type property in it, the serializer gives the following exception :

System.NotSupportedException: Serialization and deserialization of 'System.Type' instances are not supported and should be avoided since they can lead to security issues.

Passing Type, DataSet, DataTable through the JSON or XML serializer gives possibility to remote code execution for attackers. More information available at https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/dataset-datatable-dataview/security-guidance

Workaround:
Declare an enumeration for your types (ex: enum { string, int, etc }) you can parse the value for the requested type explicitly.

.NET Core : Logging with Log4NET in .NET Core application

There are tons of newly created logging engines for .NET Core. Log4Net is stable, old school technology in the market. Consider using newer logging technologies, such as NLog or Serilog. But if you want to use this engine, you can make it work.

Start with the Microsoft’s tutorial, “Logging in .NET Core”. https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-5.0

Install log4Net NuGet Package, and Microsoft.Extensions.Logging.Log4Net.AspNetCore package.

Install-Package log4Net
Install-Package Microsoft.Extensions.Logging.Log4Net.AspNetCore

Make changes in your Program.cs file. In the CreateHostBuilder method, configure logging with the following code:

                .ConfigureLogging((hostingContext, logging) =>
                {
                    logging.AddConsole();
                    logging.AddLog4Net();
                })

If the “AddLog4Net” method call is unrecognized by IntelliSense, make sure you have installed the Logging extension NuGet package mentioned above.

Add a new file to your project, and name it log4Net.config. The template should be used is Web Configuration file.

Make changes in the newly generated file, here you can configure the applications logging. I’ve skipped Console logging, Microsoft’s console logger visualize logs much greater. You can learn configuring Log4Net more at https://logging.apache.org/log4net/release/manual/configuration.html
A quick start configuration example:

<?xml version="1.0" encoding="utf-8"?>
<log4net>
	<root>
		<level value="ALL" />
		<appender-ref ref="file" />
	</root>
	<appender name="file" type="log4net.Appender.RollingFileAppender">
		<file value="myapp.log" />
		<appendToFile value="true" />
		<rollingStyle value="Size" />
		<maxSizeRollBackups value="5" />
		<maximumFileSize value="10MB" />
		<staticLogFileName value="true" />
		<layout type="log4net.Layout.PatternLayout">
			<conversionPattern value="%date [%thread] %level %logger - %message%newline" />
		</layout>
	</appender>
</log4net>

.NET Core : Run Core apps as Windows service.

You may want to host your .NET Core application in a Windows computer, even a Windows server. You want to get rid of Console windows, and do not want to start the app manually after the computer is started, or restart it when the application has crashed. This tutorial helps you to make a windows service from your .NET Core application, especially a .NET Core WebAPI.

Install the “Microsoft.Extensions.Hosting.WindowsServices” NuGet package for you .NET Core application. This can be achieved from NuGet package manager console:

Install-Package Microsoft.Extensions.Hosting.WindowsServices

Make changes in your Program.cs file. Add the UseWindowsService call to the CreateHostBuilder function. The result may look something like this:

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseUrls("https://0.0.0.0:8080/", "http://0.0.0.0:8081/");
                    webBuilder.UseStartup<Startup>();
                })
                .ConfigureLogging((hostingContext, logging) =>
                {
                    logging.ClearProviders();
                    logging.AddConsole();
                })
                .UseWindowsService();

This is all of the code change you need to do.
Let’s Publish your application.

Publish your application to a folder

Make the following changes in the following dialog by pressing an edit button next to a summary label.

Set the deployment mode toself-contained

Click on Publish. Once the publish is done, copy the published files to a specific directory of the computer, or an another computer. Run the powershell script above, to create a new windows service on the hosting computer.

# New-Service documentation: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/new-service?view=powershell-7.1
# Script by banditoth

$serviceName = "typeyourservicename";
$serviceDescription = "typeyourdescription";
$displayName = "displayname";
$exeFilePath = "pathtoyourexefile.exe";
$serviceUserName = "MustBeDomain\User";

New-Service -Name $serviceName -DisplayName $displayName -BinaryPathName $exeFilePath -Credential $serviceUserName -Description $serviceDescription -StartupType Automatic

Do NOT forget to set the inbound policies for your application in Advanced Windows Firewall. Also keep in mind, if you want to access your web application outside of your local network, you need to forward ports on your router.