EF Core class library and migrations in azure pipeline - entity-framework

I know that to use EF Core in a class library a startup project is needed, as explained here: Is EF Core Add Migration Supported from .NET Standard Library?
However, I've managed to use dotnet ef migrations bundle locally using a single class library project (without using a startup project) by
Adding a ConfigurationBuilder you dbcontext
Overriding the "OnConfiguring" method to use the ConfigurationBuilder for reading the connection string where you have it. In my case from user secrets
public partial class MyContext : DbContext
{
private readonly IConfiguration configuration;
//will take the connection string from user secrets
public MyContext()
{
this.configuration = new ConfigurationBuilder()
.AddUserSecrets<ClpQUkMiContext>()
.Build();
}
public MyContext(IConfiguration configuration)
{
this.configuration = configuration;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
var connectionString = configuration["ConnectionStringInUserSecret"]
optionsBuilder.UseSqlServer(connectionString);
}
}
However, when I'm trying to use it in azure pipeline it's failing to find the connection string, which I have stored in a secret variable.
So far I've tried
Adding ".AddEnvironmentVariables()" to the configuration builder.
Mapping the secret variable in the yaml file, like
variables:
MyConnectionString: $(MySecreatConnectionString) # Setting variable to be read by the context
I would appreciate any help.

Managed to solve the issue: To map a secret variable into an environment one that can be read by the configuration, it must be done inside each task, as crlearly explained here: https://learn.microsoft.com/en-us/azure/devops/pipelines/process/variables?view=azure-devops&tabs=yaml%2Cbatch#secret-variables
dev variable inside the task

Related

Failed initial migration in Entity Framework Core on .NET 6 Isolated [duplicate]

.NET6 EFCore & Cosmos Migration issue. Need some help.
Hello folks. I am new in the world of .Net and I am facing an issue that Google has failed to help me solve. You're kind of my last regard.
So. I am trying to connect to an Azure Cosmos DB from my little HomeControlCenter Project using EFCore 6.0.3
The Error:
Unable to resolve service for type 'Microsoft.EntityFrameworkCore.Migrations.IMigrator'. This is often because no database provider has been configured for this DbContext. A provider can be configured by overriding the 'DbContext.OnConfiguring' method or by using 'AddDbContext' on the application service provider. If 'AddDbContext' is used, then also ensure that your DbContext type accepts a DbContextOptions<TContext>
object in its constructor and passes it to the base constructor for DbContext.
My Program.cs:
builder.Services.AddDbContext<ControlCenterContext>(options =>
options.UseCosmos(builder.Configuration.GetConnectionString("DefaultConnection"), "ToDoList"));
My DbContext Impl:
public class ControlCenterContext : DbContext
{
public ControlCenterContext(DbContextOptions<ControlCenterContext> options) : base(options)
{
}
}
I also tried to use an override of OnConfiguring instead of the Program.cs line.
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseCosmos(
"<AccountEndpoint>",
"<accountKey>",
databaseName: "ToDoList");
Nothing helped. When ever I run dotnet ef migrations add "initialSetup" I get the error mentioned above.
I read the error carefully and as you can see, I did apply all the necessary constructor params & other additions... I even tried to create a vanilla project and do the same all over again...
I couldn't find anything official from Microsoft, but the author of this blog states migrations using EF Core for CosmosDb are not supported: https://www.thereformedprogrammer.net/an-in-depth-study-of-cosmos-db-and-ef-core-3-0-database-provider/#1-no-migrations-can-cause-problems
This makes sense since CosmosDB is a document database, so it has no schema, it's just a bunch of JSON files. I ran into this issue when I wanted to use migrations to make seed data. The only solution I could think of was to create a separate project that uploaded the seed data with static values. But again, this was only seed data and not schema updates.

.net core not connecting to the changed database in appsettings.json connectionstrings in visual studio debug

I am working on a .net core project, I wanted to change the connectionstring of the database in appsettings.json.
I had created a duplicate database and named it originalname_fake01 and made a clone of the original database for testing.
I have also changed the database name in appsettings.developement.json. Everything seems fine but when I run the application in debug mode in visual studio. the data was being pulled from the original database rather than the changed database name in appsettings.json.
Here is my appsettings connectionstrings code:
Old connectionstring was
"connectionStrings": {
"MyConnectionString":
"Server=localhost;port=3306;database=mydb;user=root;password=rt123;"
}
changed connection string (new)
"connectionStrings": {
"FakeConnectionString":
"Server=localhost;port=3306;database=mydb_fake01;user=root;password=rt123;"
}
I am not able to understand why it is connecting to the old database rather than the new database even after changing the connectionstring.
Any help would be appreciated.
If you want the development settings to override the production settings you need to use the same names and full path. Your connection string should be named MyConnectionString, not MyConnectionString if you want the DbContext to pick it automatically.
JSON setting files have no special meaning in .NET Core, they are just files. Every provider produces key/value pairs in the form Section1:Subsection1:Attribute1, Value. Newer provider values override earlier values. Providers can be JSON or XML file readers, INI file readers, databases etc. In all cases, the settings are flattened to path/value pairs.
The file
"connectionStrings": {
"MyConnectionString": "Server=localhost;port=3306;database=mydb;user=root;password=rt123;"
}
Produces a value named connectionStrings:MyConnectionString whose value is Server=localhost;port=3306;database=mydb;user=root;password=rt123;. To override this you need to specify a setting with the same path.
The default Host builder specifies some default settings providers. From the docs, those are :
appsettings.json.
appsettings.{Environment}.json.
= Secret Manager when the app runs in the Development environment.
Environment variables.
Command-line arguments.
Settings specified lower down the list override previous ones. This means that on a development machine, the connectionStrings:MyConnectionString in appsettings.Developoment.json overrides the element with the same name in appsettings.json.
This also means that we can override the connection string with an environment variabl or a command-line argument, eg
dotnet run /connectionStrings:MyConnectionString Server=localhost;port=3306;database=mydb;user=root;password=rt123;
I have finally able to find the problem in the dbcontext.cs modelbuilder. There is a code line which has strongly typed Schema Name.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
OnModelCreatingImpl(modelBuilder);
modelBuilder.HasDefaultSchema("MyDB");
}
I changed it use the new schema as below:
modelBuilder.HasDefaultSchema("MyDB_Fake01");
I don't understand why we need to give schema name in both connectionstring and in modelbuilder. I Guess we should avoid explicit schema name targeting in ModelBuilder so that, whenever the connectionstring is changed. it will target the database correctly.
Anyways, the problem is solved as the application is connecting to the intended database.
You are almost there. In your changed connection string (new) setting, change the FakeConnectionString to MyConnectionString. If you have same keys in appsettings.json and in appsettings.development.json then latter will override the former.
"connectionStrings": {
"MyConnectionString": "Server=localhost;port=3306;database=mydb_fake01;user=root;password=rt123;"
}
P.S. This is applicable to any environment not only to development
There is also a file named "secrets.json" which overrides all other appSettings configuration. Make sure it has correct Database name.
The file named "secrets.json" worked for me as the default connection string was hardcoded there pointing to the old database. Editing the connection string (Initial Catalog=dbName) to point to the current database name fixed the problem. Thanks!
sample content of "secrets.jason":
{
"ConnectionStrings:DefaultConnection": "Data Source=TestSource;Initial Catalog=dbName;Integrated Security=True;MultipleActiveResultSets=True"
}

Stop a referenced dll from running migrations

I have a solution with a web application. This application uses Entity Framework and Code First. Then I have a second project, a console application. This console application shares the assembly that hold the migrations.
Is there a way to this console application that it should never run the Migrations? It could happen that this console application would contain a newer version which has not yet been deployed in the web application. I like to make sure there is no risk for the console to ruin the web application. Better to just have the console failing that updating the database.
Set the database initialiser to null in your DbContext constructor:
public class ConsoleContext : DbContext
{ public ConsoleContext()
: base("Name=" + Config.ConnectionStringName)
{
// Prevent attempt to initialize a database for this context
Database.SetInitializer<DtoContext>(null);
}
}
Alternatively,
in your web application:
protected void Application_Start()
{
Database.SetInitializer<MyDbContext>(
new MigrateDatabaseToLatestVersion<MyDbContext, Migrations.Configuration>());
}
and in your console application:
protected void Application_Start()
{
Database.SetInitializer<DtoContext>(null);
}
And, to be doubly certain, use two different database users. The web application will need db_datareader, db_datawriter and db_ddladmin roles if you're using Sql Server. The console application should only be in db_datareader and db_datawriter roles to prevent it changing the database schema
Easy) Yes, I have a good solution for that. in Nuget Console you have to provide the connection string and the target migration for the commands: Add-Migration/Update-Database.
Need Code) You can also do that programmatically by using DbMigrator.

Does a mvc6 service provides project.json data?

I am using asp.net 5 with mvc6, and I extended the project.json with several stuffs.
I am currently reading the project.json by parsing it myself. But I was wondering whether a service or a context exists within mvc6 already providing parsed data of project.json file.
There's no service, you have to parse it yourself
Why are you parsing manually project.json? Don't do that. If you wan't to have custom configuration settings then create new file or use one of existing ones (for example appsettings.json). Then in Startup.cs simply add that file and it will be available in the Configuration object:
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder().AddJsonFile("appsettings.json");
...
Configuration = builder.Build();
}

Is it possible to get Entity Framework to recognize previous migrations if you change your project namespace?

Let me break down the scenario:
I create my models/mappings using the code-first approach
I setup a database initializer for MigrateDatabaseToLatestVersion
I create a migration using add-migration
This creates a Configuration class like so:
namespace MyApp.Migrations
{
internal sealed class ConfigurationInfo : DbMigrationsConfiguration<MyContext>
{
}
}
I can run my code and the database will be automatically created with no issue.
Now I go in and change the namespace that my Configuration class lives under:
namespace MyApp.Data.Migrations // <-- new namespace
{
internal sealed class ConfigurationInfo : DbMigrationsConfiguration<MyContext>
{
}
}
I drop the database and rerun the code. I now get this message:
Unable to update database to match the current model because there are pending changes and automatic migration is disabled. Either write the pending model changes to a code-based migration or enable automatic migration. Set DbMigrationsConfiguration.AutomaticMigrationsEnabled to true to enable automatic migration.
When I renamed the namespace that Configuration lived under it no longer recognizes any of the migrations that were previously created.
I did a lot of experimenting and when I set MigrationsNamespace equal to the old value in the Configuration constructor like so:
namespace MyApp.Data.Migrations
{
internal sealed class ConfigurationInfo : DbMigrationsConfiguration<MyContext>
{
public ConfigurationInfo()
{
AutomaticMigrationsEnabled = false;
MigrationsNamespace = "MyApp.Migrations"; // <-- this works
}
}
}
Now everything works, except all of the previously created migrations need to live under the old namespace in order to work, as well as all future ones (which get the old namespace automatically).
This workaround didn't really do what I wanted to do, which is be able to refactor my code and still have entity framework recognize my previous migrations.
What if the name of my project changes, but I have multiple installations out there that are depending on the MigrateDatabaseToLatestVersion database initializer to receive schema changes to my code?
Am I locked into using the same namespace for my DAL as soon as I enable migrations?
This was my own fault, I did the refactor by hand and thought that I had changed the namespace for all files in my project, but I forgot to expand the migration files and update the "designer" files as well!
When I set the same namespace between the configuration file, the migration files, and the migration designer files everything works great, and I can change the namespace at any time without EF losing track of the old migrations.