Reload/Restart ASP.NET Core 2.1 web application - c#

I have a .NET Core 2.1 web app where users can select the database provider of their choice. It's a choice between SQL Server, SQLite and MySQL (for now, more providers could be added later). I am saving user's choices to a json file along with the connection strings for each database provider:
"ConnectionStrings": {
"MSSQL": "Server=(localdb)\\MSSQLLocalDB;Database=ABC_db;Trusted_Connection=True;MultipleActiveResultSets=true",
"SQLite": "Data Source=ABC.db"
},
"UserSettings": {
"DatabaseProvider": "MSSQL", //this changes as per user's selection
"GenerateDb": false //this will be false for the first time, after that it will be true
}
And in my ConfigureServices method in Startup.cs I have placed some checks to register/inject the database context and identity:
GenerateDb = Configuration.GetValue<bool>("GenerateDb");
DatabaseProvider = Configuration.GetValue<string>("SystemSettings:SystemProfile:DatabaseProvider");
if(GenerateDb)
{
if (DatabaseProvider == "MSSQL")
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString(DatabaseProvider)));
else if (DatabaseProvider == "SQLite")
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlite(Configuration.GetConnectionString(DatabaseProvider)));
services.AddDefaultIdentity<IdentityUser>()
.AddEntityFrameworkStores<ApplicationDbContext>();
}
and this code works as expected, it sets the database context with whatever provider the user has selected. The only problem is that to activate the database context, I have to stop and start the app again so next when it reads the json file, GenerateDb is true. I am looking for something that can help me restart the app without manually doing it. Is this functionality available? I couldn't find anything in the docs.

An option would be to register 2 different implementations of ApplicationDbContext.
First, create the new classes (they can be empty implementations, it doesn't matter)
public class SQliteApplicationDbContext : ApplicationDbContext {}
public class SqlServerApplicationDbContext : ApplicationDbContext {}
Then register them as such:
services.AddDbContext<SqlServerApplicationDbContext >(options =>
options.UseSqlServer(Configuration.GetConnectionString(DatabaseProvider)));
services.AddDbContext<SQliteApplicationDbContext>(options =>
options.UseSqlite(Configuration.GetConnectionString(DatabaseProvider)));
services.AddScoped<ApplicationDbContext>((ctx) =>
{
// fyi: would be better to implement the options pattern here
DatabaseProvider = Configuration.GetValue<string>("SystemSettings:SystemProfile:DatabaseProvider");
if (DatabaseProvider == "MSSQL")
ctx.GetService<SqlServerApplicationDbContext >();
else if (DatabaseProvider == "SQLite")
ctx.GetService<SQliteApplicationDbContext>();
else
throw new Exception("Bad configuration");
});
Note that this makes the assumptions that asp.net core is configured to watch the changes in the json file.

Related

Entity Framework Stub DB in Unit Testing

I have a running NET6 app that uses EF6 to connect with Postgres.
I don't want to have DB access in the tests just logic, so im trying to replace the actual Postgres db with an in-memory db.
public class TestApp : WebApplicationFactory<Program>
{
protected override IHost CreateHost(IHostBuilder builder)
{
builder.ConfigureServices(services =>
{
services.RemoveAll(typeof(IAuthService));
services.AddScoped<IAuthService, StubAuthService>();
var myDatabaseName = "test_"+DateTime.Now.ToFileTimeUtc();
var options = new DbContextOptionsBuilder<PlayersContext>()
.UseInMemoryDatabase(databaseName: myDatabaseName )
.Options;
services.AddDbContextFactory<PlayersContext>(f => new DbContext(options));
});
return base.CreateHost(builder);
}
}
This is wrong and not working, im likely trying doing it wrong. My questions here would be:
How to stub the database in tests so the app uses an in memory db instead of the actual database ?
How can I read that stubbed DB context inside a test to validate something was inserted for instance ?
Thanks !

Force DBUP to rerun new scripts during development

We're using DBUP to handle db migrations. Each release, we would like to run the dbup console app with a command line switch so that during dev we can re-run our scripts while we're working on them, however we don't want it to re-run all the previous releases scripts which already appear in the database. How can this be achieved?
We added a '-debug' command line switch to our DbUp console application. If this is present we switch which Journal class is used when talking to the database.
The Journal class (https://dbup.readthedocs.io/en/latest/more-info/journaling/) in DbUp is the class that interacts with the database to check and record which scripts have already been run (stored by default in the Schema Versions table). For Dev, we force this to use a read-only version of this, which can check which scripts are already present (to prevent you re-running everything each time) but prevents new records being recorded, so that next time it will attempt to re-run your new scripts again.
The read only journal looks like this;
public class ReadOnlyJournal : IJournal
{
private readonly IJournal _innerJournal;
public ReadOnlyJournal(IJournal innerJournal)
{
_innerJournal = innerJournal;
}
public void EnsureTableExistsAndIsLatestVersion(Func<IDbCommand> dbCommandFactory)
{
_innerJournal.EnsureTableExistsAndIsLatestVersion(dbCommandFactory);
}
public string[] GetExecutedScripts()
{
return _innerJournal.GetExecutedScripts().ToArray();
}
public void StoreExecutedScript(SqlScript script, Func<IDbCommand> dbCommandFactory)
{
// don't store anything
}
}
Then an extension method to allow the use of this new journal to be easier specified;
public static class DbUpHelper
{
public static UpgradeEngineBuilder WithReadOnlyJournal(this UpgradeEngineBuilder builder, string schema, string table)
{
builder.Configure(c => c.Journal = new ReadOnlyJournal(new SqlTableJournal(() => c.ConnectionManager, () => c.Log, schema, table)));
return builder;
}
}
And then finally the change to your DbUp console app;
var upgrader = debug
? DeployChanges.To
.SqlDatabase(connectionString)
.WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly())
.WithReadOnlyJournal("dbo", "SchemaVersions")
.LogToConsole()
.Build()
: DeployChanges.To
.SqlDatabase(connectionString)
.WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly())
.LogToConsole()
.Build();
var result = upgrader.PerformUpgrade();
if (!result.Successful)
....

Why are values from db loaded differently on Console Apps and WebAPIs?

I've been using a DbContext (MySql.Data.EntityFrameworkCore 6.10.6) on a WebAPI project (.NET Core 2.0) for quite a while without any problems but now I need to use the same context on a Console Application and special characters aren't being loaded correctly.
WebAPI:
public class ProductsController : Controller
{
private readonly MyDbContext _context;
public ProductsController(MyDbContext context)
{
_context = context;
}
[HttpGet]
public string Get()
{
var productName = _context.Products
.Single(x => x.Id == 1)
.Name;
return productName; = // "PÃO FRANCÊS"
}
}
Console App:
private static void Main(string[] args)
{
// setup DI
Initialize();
var context = ServiceProvider.GetService<MyDbContext>();
var productName = context.Products
.Single(x => x.Id == 1)
.Name;
Console.WriteLine(productName); // "P�O FRANC�S"
}
Why are these values different even though the same context is used?
Update 1:
Changing the Console.OutputEncoding to UTF8 or changing the console font to Lucidas Console or Console didn't change the result. The real purpose of the Console App is to call an API and the result has the � character as soon as I retrieve the value from the DB when debugging. I don't need to print the result on the console.
Update 2:
When debugging past the productName variable, the value doesn't change when calling productName or ?productName in the Immediate Window from Visual Studio.
Update 3:
I've configured a test database using MS SQL Server and the characters are loaded correctly. It looks like it's a problem with MySql.Data.EntityFrameworkCore
Update 4:
Tested it with a new Console App using .NET Framework 4.7.1 and the characters are displayed correctly.
The solution was to add this line to the console app.
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Reference: https://github.com/dotnet/corefx/issues/10054
Try adding this to get the special characters to load correctly.
Console.OutputEncoding = System.Text.Encoding.Unicode;

code first migration not working when deploying to Azure

I am building an MVC 5 website with code first migration approach. Also, mention that I'm using Visual Studio Online linking to my Azure Web site. So, when I use Team Explorer in VS 2013 to do a check in, it automatically is deployed to Azure web sites.
Locally all works OK. However, when deploying it to Azure (check in) the following points are NOT performed:
If a new table is created using the code first approach, it is not created when deploying to Azure
The seed method (in the configuration.cs file) is not executed.
Due to the above, I have to manually either create the new tables or adding the test data. Google it for a solution I have read that this is a problem in the .Net framework due to the kind of index (nonclustered) to is set by default.
So, I have trying to use different alternatives. As for example:
public Configuration()
{
AutomaticMigrationsEnabled = true;
ContextKey = "MySolution.Models.ApplicationDbContext";
SetSqlGenerator("System.Data.SqlClient", new AzureSqlGenerator());
}
Where I created the AzureSqlGenerator class:
public class AzureSqlGenerator : SqlServerMigrationSqlGenerator
{
protected override void Generate(CreateTableOperation createTableOperation)
{
if ((createTableOperation.PrimaryKey != null)
&& !createTableOperation.PrimaryKey.IsClustered)
{
createTableOperation.PrimaryKey.IsClustered = true;
}
base.Generate(createTableOperation);
}
}
But, that doesn't work.
Long story short, I have the following two questions:
Is it possible to migrate the tables and its data when deploying to Azure?
If so, what would be the correct steps to do that?
Thanks in advance.
You can migrate tables. To migrate data, you should use Seed method of entity framework.
public class SchoolDBInitializer : DropCreateDatabaseAlways<SchoolDBContext>
{
protected override void Seed(SchoolDBContext context)
{
IList<Standard> defaultStandards = new List<Standard>();
defaultStandards.Add(new Standard() { StandardName = "Standard 1", Description = "First Standard" });
defaultStandards.Add(new Standard() { StandardName = "Standard 2", Description = "Second Standard" });
defaultStandards.Add(new Standard() { StandardName = "Standard 3", Description = "Third Standard" });
foreach (Standard std in defaultStandards)
context.Standards.Add(std);
base.Seed(context);
}
}
http://www.entityframeworktutorial.net/code-first/seed-database-in-code-first.aspx
In my opinion the best you can do, use Sql Server Data Tools. It will help you with tables and also data, because it uses a declarative mode. You 'declare' how should be your database, and it handles de updates.
https://www.youtube.com/watch?v=uPll3dMxTXU

Entity Framework Code First: Configuration.cs seed or custom initializer

I am working with the the Code First style of the Entity Framework for my first time. I want to set up some default data. The first approach I came across involved creating a custom initializer. I was headed this route but noticed after setting up migrations that it came with the Configuration.cs that already overrides the seed method just like the custom initializer.
internal sealed class Configuration : DbMigrationsConfiguration<Toolkit.Model.ToolkitContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(Toolkit.Model.ToolkitContext context)
{
// This method will be called after migrating to the latest version.
// You can use the DbSet<T>.AddOrUpdate() helper extension method
// to avoid creating duplicate seed data. E.g.
//
// context.People.AddOrUpdate(
// p => p.FullName,
// new Person { FullName = "Andrew Peters" },
// new Person { FullName = "Brice Lambson" },
// new Person { FullName = "Rowan Miller" }
// );
//
}
}
So it seems there are two ways to accomplish this task. Can someone shed some light on what would be the recommended way of doing this? Or does it matter at all and I should just flip a coin?
The Configuration.cs Seed method will run every time your model changes to make sure that some specific data stays in your DB, or to even possibly to reset that data to a specified default setting.
The Custom Initializer's seed method, on the other hand, can be setup to run every single time the application loads, like in this code, which is currently in the Global.asax file of my MVC page:
Database.SetInitializer(new MyCustomInitializer<MyDbContext, Configuration>());
var db = new MyDbContext();
db.Database.Initialize(true);
The practical difference really comes into play after you deploy your application. The Custom Initializer will make sure that no user can destroy some data that's absolutely required in your program.

Categories

Resources