EF Core CLI Migration Database Update Flow - entity-framework-core

May I know how does the actual flow look like when performing migration using the command below?
dotnet-ef database update --project MyModel --startup-project MyProject --context DBContext --configuration UAT -v
It is always out of my expectations. When I run the command above, it will keep performing migration on my dev database. I have a static class that will set my DB connection string on the MyProject.Program.Main using the preprocessor directives. Like the code below.
#if DEV
Setting.Env = "DEV";
#elseif UAT
Setting.Env = "UAT";
#else
Setting.Env = "DEV";
#endif
In my static Setting class have the function like below:
public static string GetDBConn()
{
switch (Env)
{
case "UAT":
return UATDBConn;
default:
return DEVDBConn;
}
}
FYI DEVDBConn and UATDBConn are read-only strings in the Setting static class.
I also set the DBContext on the Startup.ConfigureServices as below:
services.AddDbContext<DBContext>(
options =>
{
options.UseSqlServer(Setting.GetDBConn());
});
Do I miss something to make my migration command work correctly?
Any help is greatly appreciated.

You have define the elseif like that but it will be like that elif.
you can check more about Preprocessor directives
#if DEV
Setting.Env = "DEV";
#elif UAT
Setting.Env = "UAT";
#else
Setting.Env = "DEV";
#endif

Related

How can I make a symfony 4 command to be registered only in dev environment and disabled in prod?

In my App I have a helper class App\Command\GenerateFixturesCommand that provides a command named my-nice-project:generate-fixtures.
This command consumes a service of my own project named App\Services\CatalogFixtureGenerator that generates 1000 random PDF documents for testing while developing the app.
To do so, this service uses the joshtronic\LoremIpsum class which is required in composer only in dev. LoremIpsum is a third-party library. I require it under composer's require-dev.
So the injection is:
I run my GenerateFixturesCommand.
Before that, the system transparently locates my CatalogFixtureGenerator and to inject it into the command.
Before that, the system transparently locates the LoremIpsum third party service to inject it into my fixture generator service.
All is autowired.
When I deploy to prod and do composer install --no-dev --optimize-autoloader of course the LoremIpsum class is not installed.
But when I clear the cache with APP_ENV=prod php bin/console cache:clear the framework finds the command and cannot inject the autowired dependencies.
[WARNING] Some commands could not be registered:
In CatalogsFixtureGenerator.php line 26:
Class 'joshtronic\LoremIpsum' not found
This my-nice-project:generate-fixtures command is never going to be used in the production server.
Question
How can I "disable" the command in prod?
I mean: How can I tell the framework that the class GenerateFixturesCommand should not be loaded nor its autowired dependencies, and neither of them should be autowired in prod?
Use the isEnabled() method in Command.
For example
public function isEnabled(): bool
{
// disable on prod
if ($this->appKernel->getEnvironment() === 'prod') {
return false;
}
return true;
}
In my last project, I need some commands to work only in dev environment. You use getenv function to achieve this:
# src/Command/SomeCommand.php
...
public function __construct()
{
parent::__construct();
if (getenv("APP_ENV") !== "dev") {
exit('This command should work only "dev" environment.');
}
}
This will do the trick.
Code fun :)
The solution #gusDeCooL suggests doesn't work with lazy-loaded commands (at least not for me).
I ended up implementing the isEnabled() method anyway, but then I added a guard in execute():
<?php
namespace App\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
#[AsCommand(
name: 'command:name',
description: 'Some description',
)]
class CommandName extends Command
{
public function isEnabled(): bool
{
return 'dev' === getenv('APP_ENV');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
if (!$this->isEnabled()) {
$io->error('This command is only available in `dev` environment.');
exit(1);
}
// the rest
}
}

IdentityServer4 Error: No DbContext named 'ConfigurationDbContext' was found

I am trying to hook up persistence to my identity server by adding entity framework. Currently, when trying to add a migration I am receiving the error
No DbContext named 'ConfigurationDbContext' was found.
Before running the migration, I have cd'd into the directory my .csproj file sits in and am running dotnet ef migrations add InitConfigration -c ConfigurationDbContext -o Data/Migrations/IdentityServer/Configuration to attempt to add the migration.
My Startup.cs class looks as follows:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
var migrationsAssembly = typeof(ApplicationDbContext).GetTypeInfo().Assembly.GetName().Name;
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddAspNetIdentity<ApplicationUser>()
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
db => db.MigrationsAssembly(migrationsAssembly));
})
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
db => db.MigrationsAssembly(migrationsAssembly));
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseIdentityServer();
app.UseMvc();
}
}
How can I resolve this issue?
EDIT: After further investigation, when i use the --project flag when generating the migration as follows: dotnet ef migrations add InitConfigration -c ConfigurationDbContext -o Data/Migrations/IdentityServer/Configuration --project BleacherBuddy.IdentityServerService, I receive the error:
MSBUILD : error MSB1009: Project file does not exist. Unable to
retrieve project metadata. Ensure it's an MSBuild-based .NET Core
project
My current guess is that because this is a service fabric project (stateless .net core), the build process is failing here and not allowing me to generate the migration. After some research, I'm still unsure how to overcome this or if this is the actual issue. I recreated the simple identity server project as a web api (not using service fabric) and I was able to generate the classes as expected. All help is appreciated.
dotnet ef migrations add InitConfigration -c ConfigurationDbContext -o Data/Migrations/IdentityServer/Configuration
In this command you explicitly ask dotnet ef to use ConfigurationDbContext class as a database context. You don't have it in your Startup.cs, so I assume you have it elsewhere. In that case you'll need to give a fully qualified class name to the tool, including the namespace, so your command should look like this:
dotnet ef migrations add InitConfigration -c Fully.Qualified.Namespaces.ConfigurationDbContext -o Data/Migrations/IdentityServer/Configuration
- replace Fully.Qualified.Namespaces. with the actual path to your ConfigurationDbContext class.
UPD:
Alternatively, since you actually setup your identity service with ApplicationDbContext as EF store, you might need to use the same context in your command:
dotnet ef migrations add InitConfigration -c ApplicationDbContext -o Data/Migrations/IdentityServer/Configuration
In this case you also might need to specify fully qualified namespace before the context class name.
I am using Identity Server 4. After making all kind of configurations in code, I visited this headline.
The point to note is that trying from Visual Studio will not help. So, it is better to open your IdentityServer project directory in command prompt and try following commands first
dotnet tool install --global dotnet-ef
dotnet add package Microsoft.EntityFrameworkCore.Design
and then try following
dotnet ef migrations add InitialIdentityServerPersistedGrantDbMigration -c PersistedGrantDbContext -o Migrations/IdentityServer/PersistedGrantDb
dotnet ef migrations add InitialIdentityServerConfigurationDbMigration -c ConfigurationDbContext -o Migrations/IdentityServer/ConfigurationDb
You will get your issue resolved out.
Here's what I did. Installed the following packages:
IdentityServer4
IdentityServer4.AspNetIdentity
IdentityServer4.EntityFramework
Microsoft.AspNetCore.Identity.EntityFrameworkCore
Microsoft.EntityFrameworkCore.Design
Microsoft.EntityFrameworkCore.SqlServer
And then, instead of just building the web project with these references, I ran the project and stopped it and then tried the
dotnet ef database update --context ConfigurationDbContext command and it worked.
I am guessing the issue could be with generating the object files or a complete NuGet package restore.
The issue will happen in three cases:
Even you are putting your migrations in separate class library so in this case you have to implement the design time interface and follow the Microsoft instructions.
Or you are installing this Nuget package to a class library by mistake IdentityServer4.EntityFrameWork.
You may have the docker-compose enable via visual studio which may cause the same issue as i described here: Add-Migration while using database in separate Docker container throws an error
In all cases make sure to clean then build the solution before run the command again.
I had the exact same problem, AFTER adding
services.AddDbContext<IdentityDbContext>(o => { o.UseSqlServer(Configuration.GetConnectionString("IdentityConnection")); });
When I commented that out, it could find the context again.
You Can Try This and it's working for me
dotnet add package IdentityServer4.EntityFramework
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
Add this line of code into Startup.cs
const string connectionString = #"Data Source=(LocalDb)\MSSQLLocalDB;database=IdentityServer4.Quickstart.EntityFramework-3.0.0;trusted_connection=yes;";
var migrationAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetAllApiResources())
.AddInMemoryClients(Config.GetClients())
.AddTestUsers(Config.GetUsers())
//Configuration Store: clients and resources
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = b =>
b.UseSqlServer(connectionString ,
sql => sql.MigrationsAssembly(migrationAssembly ));
})
//Operational Store: tokens, consents, codes, etc
.AddOperationalStore(options =>
{
options.ConfigureDbContext = b =>
b.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationAssembly ));
});
Now, Finally try to add migrations
Add-Migration Initial_Configuration -c ConfigurationDbContext -o Migrations/IdentityServer4/ConfigurationDb
Add-Migration Initial_Persisted -c PersistedGrantDbContext -o Migrations/IdentityServer4/PersistedDb
This issue is a Bear.
None of the other answers here worked for me. There is a hint in one of the answers that you need to implement a Design Time DbContext, but there is no info on how to do that.
Anyway, here is the answer that helped me. I've done this type of stuff before, but nothing like this and I can't claim to have come up with this code. I found this answer highly rated on github:
https://github.com/IdentityServer/IdentityServer4/issues/2235#issuecomment-402467103
It is a lot of code, but here it is:
using System;
using System.IO;
using System.Reflection;
using Duende.IdentityServer.EntityFramework.DbContexts;
using Duende.IdentityServer.EntityFramework.Options;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.Extensions.Configuration;
namespace Awesome.IdentityServer.Infrastructure
{
public class ConfigurationContextDesignTimeFactory : DesignTimeDbContextFactoryBase<ConfigurationDbContext>
{
public ConfigurationContextDesignTimeFactory()
: base("AwesomeIdentity", typeof(Startup).GetTypeInfo().Assembly.GetName().Name)
{
}
protected override ConfigurationDbContext CreateNewInstance(DbContextOptions<ConfigurationDbContext> options)
{
return new ConfigurationDbContext(options, new ConfigurationStoreOptions());
}
}
public class PersistedGrantContextDesignTimeFactory : DesignTimeDbContextFactoryBase<PersistedGrantDbContext>
{
public PersistedGrantContextDesignTimeFactory()
: base("AwesomeIdentity", typeof(Startup).GetTypeInfo().Assembly.GetName().Name)
{
}
protected override PersistedGrantDbContext CreateNewInstance(DbContextOptions<PersistedGrantDbContext> options)
{
return new PersistedGrantDbContext(options, new OperationalStoreOptions());
}
}
public abstract class DesignTimeDbContextFactoryBase<TContext> : IDesignTimeDbContextFactory<TContext> where TContext : DbContext
{
protected string ConnectionStringName { get; }
protected String MigrationsAssemblyName { get; }
public DesignTimeDbContextFactoryBase(string connectionStringName, string migrationsAssemblyName)
{
ConnectionStringName = connectionStringName;
MigrationsAssemblyName = migrationsAssemblyName;
}
public TContext CreateDbContext(string[] args)
{
return Create(
Directory.GetCurrentDirectory(),
Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"),
ConnectionStringName, MigrationsAssemblyName);
}
protected abstract TContext CreateNewInstance(
DbContextOptions<TContext> options);
public TContext CreateWithConnectionStringName(string connectionStringName, string migrationsAssemblyName)
{
var environmentName =
Environment.GetEnvironmentVariable(
"ASPNETCORE_ENVIRONMENT");
var basePath = AppContext.BaseDirectory;
return Create(basePath, environmentName, connectionStringName, migrationsAssemblyName);
}
private TContext Create(string basePath, string environmentName, string connectionStringName, string migrationsAssemblyName)
{
var builder = new ConfigurationBuilder()
.SetBasePath(basePath)
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{environmentName}.json", true)
.AddEnvironmentVariables();
var config = builder.Build();
var connstr = config.GetConnectionString(connectionStringName);
if (String.IsNullOrWhiteSpace(connstr) == true)
{
throw new InvalidOperationException(
"Could not find a connection string named 'default'.");
}
else
{
return CreateWithConnectionString(connstr, migrationsAssemblyName);
}
}
private TContext CreateWithConnectionString(string connectionString, string migrationsAssemblyName)
{
if (string.IsNullOrEmpty(connectionString))
throw new ArgumentException(
$"{nameof(connectionString)} is null or empty.",
nameof(connectionString));
var optionsBuilder =
new DbContextOptionsBuilder<TContext>();
Console.WriteLine(
"MyDesignTimeDbContextFactory.Create(string): Connection string: {0}",
connectionString);
optionsBuilder.UseSqlServer(connectionString, sqlServerOptions => sqlServerOptions.MigrationsAssembly(migrationsAssemblyName));
DbContextOptions<TContext> options = optionsBuilder.Options;
return CreateNewInstance(options);
}
}
}
Add this code to your Web project.
Make sure to have a good connection string name in your appsettings.json (Not appsetting.Development.json).
BTW, I was using this to upgrade IdentityServer4 to Duende Identity Server 5.
For me, I had to delete my obj and bin folders, then rebuild the project. Then the command worked.
this may help. this solved my problem as i have docker-compose in my project. i had to remove docker-compose and add it again.
https://stackoverflow.com/a/60320410/4977086

Nunit3 how to change testcase name based on parameters passed from TestFixtureSource

I'm using NUnit 3.0 and TestFixtureSource to run test cases inside a fixture multiple times with different parameters/configurations (I do want to do this at TestFixture level). Simple example:
[TestFixtureSource(typeof (ConfigurationProvider))]
public class Fixture
{
public Fixture(Configuration configuration)
{
_configuration = configuration;
}
private Configuration _configuration;
[Test]
public void Test()
{
//do something with _configuration
Assert.Fail();
}
}
Let's say Test() fails for one of the configurations and succeeds for another. In the run report file and in Visual Studio's Test Explorer the name for both the failed and the succeeded runs will be displayed as just Test(), which doesn't tell me anything about which setup caused issues.
Is there a way to affect the test cases names in this situation (i.e. prefix its name per fixture run/configuration)? As a workaround I'm currently printing to the results output before each test case fires but I would rather avoid doing that.
Since NUnit 3.0 is in beta and this feature is fairly new I wasn't able to find anything in the docs. I found TestCaseData but I don't think it's tailored to be used with fixtures just yet (it's designed for test cases).
I can't find a way to change the testname, but it should not be neccessary, because NUnit3 constructs the testname by including a description of the testfixture.
The example class Fixture from the question can be used unchanged if the Configuration and ConfigurationProvider has an implementation like this:
public class Configuration
{
public string Description { get; }
public Configuration(string description)
{
Description = description;
}
public override string ToString()
{
return Description;
}
}
public class ConfigurationProvider : IEnumerable
{
public IEnumerator GetEnumerator()
{
yield return new Configuration("Foo");
yield return new Configuration("Bar");
yield return new Configuration("Baz");
}
}
The 'trick' is to make sure the constructor-parameter to the fixture is a string or has a ToString-method that gives a sensible description of the fixture.
If you are using NUnit 3 Test Adapter in Visual Studio, then the testfixtures will be displayed as Fixture(Foo), Fixture(Bar) and Fixture(Baz) so you can easily distinguish between their tests. The xml-output from nunit3-console.exe also uses descriptive names, fx: fullname=MyTests.Fixture(Bar).Test
<test-case id="0-1003" name="Test" fullname="MyTests.Fixture(Bar).Test" methodname="Test" classname="MyTests.Fixture" runstate="Runnable" result="Failed" ... >
<failure>
<message><![CDATA[]]></message>
<stack-trace><![CDATA[at MyTests.Fixture.Test() in ... ]]></stack-trace>
</failure>
...
</test-case>
One way to perform such actions is to have find and replace tokens in source code and dynamically build test libraries before execution using command line msbuild. High level steps are
Define test case names as sometest_TOKEN in source then using command line tools like fnr.exe replce _TOKEN with whatever you like. For example sometest_build2145.
Compile the dll with using msbuild for example msbuild /t:REbuild mytestproj.sln. Thereafter execute all test cases in mytestproj.dll.

specific config by environment in Scala

What is a good way to set up a project in Scala which uses different configuration depending on environments.
I need to specifically have different databases for development, test and production environment (similar to what is done in Rails)
Another strategy I'm using consists of using includes.
I usually store my DEV settings in the default application.conf file then I create a new conf file for other environments and include the default one.
Let's say my DEV conf application.conf looks like this:
myapp {
server-address = "localhost"
server-port = 9000
some-other-setting = "cool !"
}
Then for the PROD, I could have another file called prod.conf:
include "application"
# override default (DEV) settings
myapp {
server-address = ${PROD_SERVER_HOSTNAME}
server-port = ${PROD_SERVER_PORT}
}
Note that I override only the settings that change in the PROD environment (some-other-setting is thus the same as in DEV).
The config bootstrap code doesn't test anything
...
val conf = ConfigFactory.load()
...
To switch from the DEV to the PROD conf, simply pass a system property with the name of the config file to load:
java -Dconfig.resource=prod.conf ...
In DEV, no need to pass it since application.conf will be loaded by default.
So here we're using Typesafe Config's default loading mechanism to achieve this.
I've created a simple project to demonstrate this technique. Feel free to clone and experiment.
Use typesafe Config. Create a Config object like this:
import com.typesafe.config._
object Config {
val env = if (System.getenv("SCALA_ENV") == null) "development" else System.getenv("SCALA_ENV")
val conf = ConfigFactory.load()
def apply() = conf.getConfig(env)
}
Then create the application.conf file in src/main/resources folder:
development {
your_app {
databaseUrl = "jdbc:mysql://localhost:3306/dev_db"
databaseUser = "xxxx"
databasePassword = "xxxx"
}
}
test {
your_app {
databaseUrl = "jdbc:mysql://localhost:3306/test_db"
databaseUser = "xxxxx"
databasePassword = "xxxx"
}
}
Now from anywhere in your application, you can access configuration:
Config().getString("your_app.databaseUrl")
If you have your environment set up (e.g. export SCALA_ENV=test) when you run your application, it will consider the right configuration section. The default is development
I wasn't happy with how Daniel Cukiers solution did not allow defaults and overrides, so I changed it around to make full use of those.
The only configuration you have to do is set a ENVIRONMENT variable on the system (defaults to 'dev' if none is set)
(Java solution, compatible with Scala):
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
public class MyCompanyConfig {
public final static Config base = ConfigFactory.load().getConfig("mycompany");
public final static String environment = System.getenv("ENVIRONMENT") == null ? "dev" : System.getenv("ENVIRONMENT");
/**
* Returns a subtree of the base configuration with environment settings applied.
*
* #param setting The subtree to return config for.
* #return A config with base in given setting, with environment modifications applied.
*/
public static Config load(String setting) {
Config config = base.getConfig(setting);
if (config.hasPath(environment)) {
return config.getConfig(environment).withFallback(config);
}
return config;
}
}
This allows a single reference.conf in a library looking like this:
mycompany.module1 {
setting1 : "adefaultvalue"
url : "localhost"
test {
// will be used where ENVIRONMENT="test"
url : "test.mycompany.com"
}
prod {
// will be used where ENVIRONMENT="prod"
setting1 : "changethedefault"
url : "www.mycompany.com"
}
}
Usage:
Config conf = MyCompanyConfig.load("module1")

NUnit my extension doesn't load

I wrote an extension for NUnit 2.6.2.
I install a listener like
namespace NUnit.EventExtension
{
[NUnitAddin(Type = ExtensionType.Core)]
public class NUnitExtension : IAddin
{
public bool Install(IExtensionHost host)
{
IExtensionPoint listeners = host.GetExtensionPoint("EventListeners");
listeners.Install(new AttrHooksEventListener());
return true;
}
}
}
The AttrHooksEventListener is a simple class when I output some text to the console.
The project is builded success. I copy my dll file to bin\addins path. But my extension isn't showed in list of extensions and doesn't work.
PS I tried to use some other sample extensions, anyone from them doesn't work.
Whiy?
I found a solution.
I olny changed the target .NET framework version from v4.0 to v3.5.