Create SSO Application with MSBuild .Net 4.6.2 - msbuild

With .Net 3.5, I could able to using MSBuild to create a SSO Apllication (Type: Config Store) and read values with SSOSettingsFileReader.ReadString(appName, keyName). The msbuild script is:
<Sso.CreateApp ApplicationName="$(SsoAppName)" SettingsFile="#(SettingsGroup)"
AdminGroup="$(SsoAppAdminGroup)" UserGroup="$(SsoAppUserGroup)" ContactInfo="$(MsiManufacturer)"
SettingNameWhiteList="$(SsoSettingNameWhiteList)" SettingNameBlackList="$(SsoSettingNameBlackList)"
Condition="%(Identity) == %(Identity) and Exists(%(fullpath))"/>
After upgrading to .Net 4.6.2, I am using the same, the app is created with all keys/values, but I cannot read the Value by Key anymore, by SSOSettingsFileReader or by SSOConfigStore.GetConfigStore(...).
var ssoStore = new SSOConfigStore();
var appMgmtBag = new ConfigurationPropertyBag();
((ISSOConfigStore) ssoStore).GetConfigInfo(appName, idenifierGUID, SSOFlag.SSO_FLAG_RUNTIME, appMgmtBag);
object propertyValue;
appMgmtBag.Read(propName, out propertyValue, 0);
return (string)propertyValue;
The error message is:
The mapping does not exist. For Config Store applications, the config info has not been set.
If I use SSO Application Configuration, manually create an app with test key/value. I can retrieve the value.
Please tell me how can I create a SSO app automatically with MSBuild.

Related

Set Environment=Development when xunit testing an ASP.NET Core app

I xUnit test my ASP.NET Core web app, and my test class includes:
this.host = Program.CreateHostBuilder(Array.Empty<string>()).Build();
in order to access host.Services.
I discover that the host has Environment=Production. So the configuration seen in my startup file ignores appsettings.Development.json.
How do I inject or force host to have Environment=Development?
Preferably without any code in the web app itself.
(Context: I'm using JetBrains Rider. I find nothing in Rider setup or configuration that lets me choose an Environment for a UnitTest session. But if there is a solution on that line the question still stands)
I had assumed—wrongly—that I might fix it with this:
this.host = Program.CreateHostBuilder(
"ASPNETCORE_ENVIRONMENT=Development"
).Build();
because the docs say
The default configuration loads environment variables and command line arguments prefixed with DOTNET_ and ASPNETCORE_
but what fixed it was:
this.host = Program.CreateHostBuilder(
"ENVIRONMENT=Development"
).Build();
With this, the Configuration element then picked up the appsettings.Development.json file instead of ignoring it.
( So now I wonder whether part of the env variable processing is done by the dotnet executable before reaching Program.Main() )

Configure NLog in ASP.NET Core 2.0 without nlog.config file

I want to create a NuGet for common things used at any ASP.NET Core application, and I want to add a Log manager inside taking advantage of NLog. Regarding this, following the tutorial on NLog it seems like I am forced to add a nlog.config at any application that is using my own NuGet.
Is there any option to avoid this config file, maybe creating a configuration in code, or at least to embed it on my own NuGet?
Thanks a lot in advance
NOTE: followed NLog getting started tutorial
If you are following the tutorial then you can replace this call:
var logger = NLog.Web.NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
With your own custom NLog LoggingConfiguration:
var nlogConfig = new NLog.Config.LoggingConfiguration();
var nlogFileTarget = new NLog.Targets.FileTarget("logfile") { FileName = "file.txt" };
nlogConfig.AddRuleForAllLevels(nlogFileTarget);
var logger = NLog.Web.NLogBuilder.ConfigureNLog(nlogConfig).GetCurrentClassLogger();

Adding Claims to an ASP.Net Identity Core user fails with EF insert error

I am using ASP.Net Core on .Net Core, version:
> dotnet --info
.NET Command Line Tools (2.1.4)
Product Information:
Version: 2.1.4
Commit SHA-1 hash: 5e8add2190
Runtime Environment:
OS Name: Windows
OS Version: 10.0.16299
OS Platform: Windows
RID: win10-x64
Base Path: C:\Program Files\dotnet\sdk\2.1.4\
Microsoft .NET Core Shared Framework Host
Version : 2.0.5
Build : 17373eb129b3b05aa18ece963f8795d65ef8ea54
I am attempting to add a new user with claims using:
var r = await _userManager.CreateAsync(new ApplicationUser { UserName = "example", Email = "example#example.com" }, "password");
var u = await userManager.FindByEmailAsync("example#example.com");
if(u == null) {
throw new Exception("User was not found");
}
await _userManager.AddClaimAsync(u, new Claim("FullName", "Example McExample"));
I am using MS SQL as the backing store, with the schema created by Entity Framework using the command:
dotnet ef database update
The user is created in the database, but the claim creation fails with the error:
Microsoft.EntityFrameworkCore.DbUpdateException: An error occurred
while updating the entries. See the inner exception for details. --->
System.Data.SqlClient.SqlException: Cannot insert the value NULL into
column 'Id', table 'IdentityDemoDatabase.dbo.AspNetUserClaims'; column does
not allow nulls. INSERT fails.
The statement has been terminated.
When stepping through the code, the user is successfully created, then the user is successfully retrieved (user returned matches user just added) and then the AddClaimAsync() method fails with the above error.
What am I doing wrong?
As it turns out, I was using the wrong EF migrations.
EF uses the database context you configure in your Startup class, and the migrations it generates are very specific to the provider configured there.
I was using the migrations copied from the dotnet new --auth individual template, but these are built for Sqlite, not MS SQL Server as I was using.
To generate your own, delete the existing migrations, then configure your Startup to use the provider you want:
// Add a store context for ASP.Net Identity 2 to use
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"]));
Then add the EF tooling (if it isnt already in your project) to your csproj:
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.1" />
Then run:
dotnet restore
dotnet ef migrations add InitialCreate
dotnet ef database update
EF will run your Startup, get the provider and context and then create the tables in your database.
Delete the existing migrations
Use
dotnet ef database update 0
and
dotnet ef migrations remove

Self-update / shadow-copy with Asp.Net Core

I'm writing a Asp.Net Core application which should be able to update itself (replace its own binaries while running).
This MSDN article describes shadow copying with the classical .Net framework, which would be exactly what I need. But the whole AppDomain thing is missing in .Net Core.
So my questions are:
Is there an alternative way in .Net Core to enable shadow copying the assemblies?
Are there other mechanisms in .Net Core that allow to build a self-updating application?
Since there is no built in mechanism in .NET Core for doing this, I ended up implementing my own custom solution. It works roughly like this:
The running application downloads and extracts new binaries to a new folder.
The running application starts a small updater process. The following parameters are passed to the updater process via command line:
Process id of the running application
Binary path of the running application
Path of the downloaded binaries
The running application exits itself.
The updater process waits until the running application has exited (using the process id) or forcefully kills the running application if it doesn't exit by itself within a given timeout.
The updater process deletes the existing binaries and copies the new downloaded binaries over.
The updater process starts the new version of the main application.
Make sure you do as much as possible in the main application (downloading, unpacking, validation, ...) and keep the updater process as simple as possible (minimize risk of failing).
This approach has proven to be quite stable.
There's no build in shadow copying facilities in .NET Core
.Net API Browser indicates that the property required to set this up in .Net Core is but AppDomainSetup is not.
To be clear, AppDomain was added in .Net Standard 2.0 but creating a domain is not currently Supported
To save someone having to do what I just did and make this - this only copies files with a different date modified time. I checked and rebuilding your app only changes this on a few files. This makes for a very fast self-loader that then starts the exe in the new location, and exits the exe doing the loading that was running from the old location. This may rely on a few things like your DLL running the code must be named the same as the EXE that starts it.
Works in .Net 5:
using System;
using System.Diagnostics;
using System.IO;
namespace NetworkHelper
{
public static class LocalCopier
{
public static void EnsureRunningLocally(string callingAssemblyDotLocation)
{
var assemblyFileFriendlyName = Path.GetFileName(callingAssemblyDotLocation.Replace(".", "-"));
var assemblyDirToCheck = Path.GetDirectoryName(callingAssemblyDotLocation);
var localLocation = Configuration.Tools.AppsLocation + assemblyFileFriendlyName + "\\";
var assemblyFinalExePath = localLocation + assemblyFileFriendlyName.Replace("-dll", ".exe");
// Check what assembly passed in path starts with
var runningFromNetwork = callingAssemblyDotLocation.ToLower().StartsWith(#"\\w2k3nas1\");
if (callingAssemblyDotLocation.ToLower().StartsWith(#"i:\")) runningFromNetwork = true;
if (!runningFromNetwork) return;
// Check if copied to local already
Directory.CreateDirectory(localLocation);
// Foreach file in source dir, recursively
CopyOnlyDifferentFiles(assemblyDirToCheck, localLocation);
Process.Start(assemblyFinalExePath);
Environment.Exit(0);
}
private static void CopyOnlyDifferentFiles(string sourceFolderPath, string destinationFolderPath)
{
string[] originalFiles = Directory.GetFiles(sourceFolderPath, "*", SearchOption.AllDirectories);
Array.ForEach(originalFiles, (originalFileLocation) =>
{
FileInfo originalFile = new FileInfo(originalFileLocation);
FileInfo destFile = new FileInfo(originalFileLocation.Replace(sourceFolderPath, destinationFolderPath));
if (destFile.Exists)
{
if (originalFile.LastWriteTime != destFile.LastWriteTime)
{
originalFile.CopyTo(destFile.FullName, true);
}
}
else
{
Directory.CreateDirectory(destFile.DirectoryName);
originalFile.CopyTo(destFile.FullName, false);
}
});
}
}
}
Note that "\w2k3nas1" and "i:" are examples of network locations where if it is running from those, it should copy itself to a local directory, I use application data/roaming/localApps and then restart itself from the new directory.
This can all be put into a reference library and be called from any client apps with:
NetworkHelpers.LocalCopier.EnsureRunningLocally(Assembly.GetExecutingAssembly().Location);
(Here, Assembly.GetExecutingAssembly().Location is passed in from the calling app, because if you were to run that from in the reference project, you'd get that library's dll instead.)
I made my own solution with PowerShell Core (available on Windows/Linux/Mac).
You can use the following script to create a powershell script to update the app. IMHO: PowerShell solution is better than an external update app: script is transparent and no additional overhead for background services that lives outside of your app.
Don't forget to inject your variables:
# We don't need progress bars to consume CPU
$ProgressPreference = 'SilentlyContinue'
# Stopping the current app
$appDll = '{assemblyName}.dll'
Stop-Process -Id {processId} -Force
$appFolder = '{folderPath}'
Set-Location -Path $appFolder
# Source file location
$source = '{updateLink}'
# Destination to save the file (folder of the script)
$updateName = Get-Date -Format 'up_dd_MM_yyyy_HH_mm'
$updateNameFile = $updateName + '_update.zip'
$updateZipPath = Join-Path -Path $appFolder -ChildPath $updateNameFile
# Download the update
Invoke-WebRequest -Uri $source -OutFile $updateZipPath
# Classic Unzip
Expand-Archive -Path $updateZipPath -DestinationPath $appFolder -Force
# Cleaning
Remove-Item -Path $updateZipPath
Start-Process -FilePath 'dotnet' -ArgumentList $appDll

WCF service not working after program update

I have recently added a WCF service reference to my program. When I perform a clean install of this program, everything seems to work as expected. But, when I install the program on a client which already has a previous version (without the new service reference) installed, I get a exception telling me the default endpoint for this particular service could not be found.
It seems that the appname.exe.config is not being updated with the new endpoint settings. Is there any reason for this and how can I force the installer to overwrite the config file? I'm using the default Visual Studio 2008 installer project with RemovePreviousVersions set to True.
Update:
My program encrypts the settings section after the first run with the following code
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
ConfigurationSection section = config.GetSection(sectionKey);
if (section != null)
{
if (!section.SectionInformation.IsProtected)
{
if (!section.ElementInformation.IsLocked)
{
section.SectionInformation.ProtectSection("DataProtectionConfigurationProvider");
section.SectionInformation.ForceSave = true;
config.Save(ConfigurationSaveMode.Full);
}
}
}
When I do not run the program before installing the new version the app.config gets updated.
You are right that it is the config file that is not updated.
There are several possibilities:
The installer has the old version of the config file
The installer does not have a config file and the program is using the old one on the machine
Try uninstalling the project first, then install and check that the config file has been copied in.