Operation not supported in this platform creating an NHibernate Configuration from powershell

Viewed 514

I'm trying to add migrations to a project that uses Fluent NHibernate to call the database. The problem is that I wanted to do it in a Entity Framework style, adding the migrations, or aplying them, from the Package Manager Console.

For that, I used FluentMigrator, and FluentMigrator.NHibernate, and after porting the second one to .Net Standard, and checked that all was going well, I started to work on the Powershell script that would launch the "Add-Migration" from the package manager console.

The problem is that, when the program tries to build a NHibernate configuration, it throws a TypeInitializationException for NHibernate.NHibernateLogger. And the InnerException says:

   System.TypeInitializationException: The type initializer for 'NHibernate.Cfg.Environment' threw an exception. 
---> System.TypeInitializationException: The type initializer for 'NHibernate.NHibernateLogger' threw an exception. 
---> System.Configuration.ConfigurationErrorsException: Configuration system failed to initialize 
---> System.PlatformNotSupportedException: Operation is not supported on this platform.
   at System.Configuration.ClientConfigPaths..ctor(String exePath, Boolean includeUserConfig)
   at System.Configuration.ClientConfigPaths.GetPaths(String exePath, Boolean includeUserConfig)
   at System.Configuration.ClientConfigurationHost.get_ConfigPaths()
   at System.Configuration.ClientConfigurationHost.GetStreamName(String configPath)
   at System.Configuration.ClientConfigurationHost.get_IsAppConfigHttp()
   at System.Configuration.Internal.DelegatingConfigHost.get_IsAppConfigHttp()
   at System.Configuration.ClientConfigurationSystem..ctor()
   at System.Configuration.ConfigurationManager.EnsureConfigurationSystem()
   --- End of inner exception stack trace ---
   at System.Configuration.ConfigurationManager.EnsureConfigurationSystem()
   at System.Configuration.ConfigurationManager.PrepareConfigSystem()
   at System.Configuration.ConfigurationManager.GetSection(String sectionName)
   at System.Configuration.ConfigurationManager.get_AppSettings()
   at NHibernate.NHibernateLogger.GetNhibernateLoggerClass()
   at NHibernate.NHibernateLogger..cctor()
   --- End of inner exception stack trace ---
   at NHibernate.NHibernateLogger.For(Type type)
   at NHibernate.Cfg.Environment..cctor()
   --- End of inner exception stack trace ---
   at NHibernate.Cfg.Configuration.Reset()
   at NHibernate.Cfg.Configuration..ctor(SettingsFactory settingsFactory)
   at NHibernate.Cfg.Configuration..ctor()
   at JobsEngine.Migrations.BaseConfig.MigrationsConfiguration.GetConfiguration()

This is caused when creating a configuration using NHibernate, like this:

protected override Configuration GetConfiguration()
    {
        Configuration config = new Configuration(); //this is what throws the exception
        config = Fluently.Configure()
            .Database(MsSqlConfiguration.MsSql2012.Dialect<MsSql2012Dialect>())
            .Mappings(x => x.FluentMappings.AddFromAssembly(MigrationAssembly))
            .BuildConfiguration();
        return config;
    }

And looking for information for that "Operation is not supported in this platform" exception, it seems that what throws it is this portion of code:

exeAssembly = Assembly.GetEntryAssembly();

if (exeAssembly == null)
   throw new PlatformNotSupportedException();

in this line of the corefx.

I understand that it doesn't work because I launch it from a Powershell script, getting the assembly and so, so there is not an "entry assembly"(GetEntryAssembly returns null). If I launch from a console project, or a unit test, it works perfect. Is there a workaround or a solution to this? I would like to keep having the ability of launching it from the powershell script.

I have to say that all libraries referenced here are using(or should be using) .Net standard.

EDIT: Relevant part of the powershell script used to launch this:

$migration = [FluentMigrator.NHibernate.PSEntryPoint]::Generate($targetPath, $MigrationName)

Where $MigrationName is a string, and targetPath is the path of the assembly that has the migrations, after it has been built using EnvDTE:

$configuration = $DTE.Solution.SolutionBuild.ActiveConfiguration.Name

$DTE.Solution.SolutionBuild.BuildProject($configuration, $project.UniqueName, $true)

Where PSEntryPoint:

public static class PSEntryPoint
{
    public static object Generate(string targetPath, string migrationName)
    {
         MigrationConfigurationBase migrationConfiguration = GetMigrationConfigFromAssembly(targetPath);
        return migrationConfiguration.Generate(migrationName, targetPath);
    }

private static MigrationConfigurationBase GetMigrationConfigFromAssembly(string assemblyName)
    {
        var assembly = Assembly.LoadFrom(assemblyName);
        var migrationConfigTypes = assembly.GetTypes().Where(x => x != null && x.IsClass && !x.IsAbstract
            && typeof(MigrationConfigurationBase).IsAssignableFrom(x)).ToList();

        var migrationConfig = Activator.CreateInstance(migrationConfigTypes.First()) as MigrationConfigurationBase;

        return migrationConfig;
    }

This in general works, it creates an instance of the migrationconfiguration, it starts the "Generate", but when it gets to the new Configuration from NHibernate, it throws the Platform not supported.

1 Answers

The problem is exactly what you know. You are running the C# code a PowerShell script. I tried a couple of workarounds and unfortunately, I couldn't find any solutions for it. But, I recommend starting using C# Scripting instead of PowerShell. For e.g look at the following code:

First, install dotnet-script global tool.

dotnet tool install -g dotnet-script

Create a csx file or use dotnet script init to scaffold the script.

// filename: main.csx

#r "nuget: FluentMigrator, 3.2.1"
#r "nuget: FluentMigrator.Runner, 3.2.1"
#r "nuget: FluentMigrator.NHibernate, 1.0.0.17"
#r "nuget: NHibernate, 5.2.7"
#r "nuget: Fluent.NHibernate, 2.1.2"

using FluentMigrator;
using FluentMigrator.Runner;
using FluentMigrator.Runner.Processors;
using FluentMigrator.NHibernate;
using NHibernate;

var config = new Configuration();
config = Fluently.Configure()
    .Database(MsSqlConfiguration.MsSql2012.Dialect<MsSql2012Dialect>())
    .Mappings(x => x.FluentMappings.AddFromAssembly(MigrationAssembly))
    .BuildConfiguration();

And inside the terminal/console:

dotnet script main.csx

Of course, the code I wrote was just to prove that you can access Assembly.GetEntryAssembly inside CSharp Scripts.

Honestly, It's better to execute this kind of stuff of CSharp scripts, they are easy to debug and also it's way easier running C# assemblies in it.

If you are more curious about C# Scripting, you can read my article here: Hitchhiker’s Guide to the C# scripting

Related