Stop a referenced dll from running migrations - entity-framework

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.

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.

Run different database types in different environments in dotnet core 2

I want to run a SQLite database in development and a SQLServer Express database in production.
We are using code first with database migrations.
How do I inject a different dbcontext in each environment?
How do I run migrations against a specific database. E.g. In development I'll want to run migrations against the SQLite database.
So I guess I found a nice way for you to do that. You can use the ConfigureDevelopmentServices startup convention to add your SQLSite DbContext. So, just as some basic example you would have:
// Production "like" ConfigureServices
public void ConfigureServices(IServiceCollection services)
{
// Use Sql Server
services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("ProductionConnection")));
}
// Development ConfigureServices
public void ConfigureDevelopmentServices(IServiceCollection services)
{
// Use SQL Lite
services.AddDbContext<SchoolContext>(options =>
options.UseSqlite(Configuration.GetConnectionString("DevelopmentConnection")));
}
You can even go further and add a ConfigureStagingServices if you happen to have another different context for staging only. To avoid copy and pasting of common services, you could have a private method that register the common services and have the separate ones only with specific stuff.
Now for the migrations, I never tested this but, my best guess is if you have the correct dbContext and the correct connection string, the migrations will work fine. You just point to the EF project and run it.
For the official MS response, see Use SQLite for development, SQL Server for production

How to Only Run Migrations if Using SQL Server and not In-Memory

I'm using Entity Framework Core using SQL Server with migrations. I am also using the In-Memory and SQL-Lite In-Memory database connections in my integration tests.
How can I only run migrations when I am using a SQL Server connection? I can't see a way of detecting the type of data store based on an DbContext.
You can use the MigrationBuilder.ActiveProvider property to determine whether to execute the Up method in a migration:
protected override void Up(MigrationBuilder migrationBuilder)
{
if (migrationBuilder.ActiveProvider == "Microsoft.EntityFrameworkCore.SqlServer")
{
CreateTable(....
}
}
https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/providers#one-migration-set
EntityFramework Core has dependency injection built in. You can check the database provider by getting it as a service like so:
if (dbContext.GetService<IDatabaseProviderServices>() is SqlServerDatabaseProviderServices)
{
await dbContext.Database.MigrateAsync();
}
Here's an option...
if (db.GetService<IDatabaseProviderServices>()
.InvariantName == "Microsoft.EntityFrameworkCore.SqlServer")
{
db.Database.Migrate();
}

Need ServiceConfiguration.cscfg to populate web.config sessionstate and connection strings

I need to propagate connection string changes for entity framework, asp.net membership (which are both in the connectionstrings section of web.config) and session state (which is in sessonstate's sqlconnectionstring) in web.config when I adjust these settings in windows azure's service configuration.
During development we test our app as a standard asp.net webforms app, but once it is deployed it is running in azure. So we need to allow for the site running in both non-azure and an azure context. That's why we're just relying upon the values in web.config for now.Since these connection strings are not called directly in my code writing a utility class which grabs from azure service config if that is available or otherwise grabs from web.config is not a possibility for these values.
I realize that editing web.config would cause a disruption in service - and i only plan to do this during off hours.
I believe that the best approach is to wrap your configuration information in a service. Then, in the service, use RoleEnvironment to determine which settings to use. For example
public static class Config
{
public static string ConnStr
{
get
{
if (RoleEnvironment.IsAvailable)
return RoleEnvironment.GetConfigurationSettingValue("ConnStr");
return ConfigurationManager.AppSettings["ConnStr"];
}
}
}
If that doesn't work, and you need to change the actual web.config (for instance, using named connection strings), then you'll need to modify the config at runtime. In your role start, do something like the following:
var config = WebConfigurationManager.OpenWebConfiguration(null);
var connStrs = WebConfigurationManager.OpenWebConfiguration(null).GetSection("connectionStrings") as ConnectionStringsSection;
connStrs.ConnectionStrings["ConnStr"].ConnectionString = RoleEnvironment.GetConfigurationSettingValue("ConnStr");
config.Save();
To handle when the configuration changes after the role is running, just call the same code as above from the RoleEnvironment.Changing event.
Good luck,
Erick

Entity Framework Code First ignoring connection string, using IIS instead

I have a web app that I've created using Entity Framework Code First. In setting it up I have managed to match my DB connection string to my DBContext by specifying the full namespace and class of the DBContext as the name of the connection string.
<add name="MyClassProject.EfDbContext" connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=MyDatabase;Integrated Security=true;User Id=MyUsername;Password=MyPassword;" providerName="System.Data.SqlClient"/>
Initially when I set up the project, I just had it created in c:\inetpub\wwwroot, and just ran it through Visual Studio. Everything worked fine.
Now I'm trying to have the code build to a separate website folder, and have the website run as it's own website and app pool in IIS. I've set up the website, and my hosts file but when I went to run it I received the following error.
Cannot open database "MyDatabase" requested by the login. The login failed.
Login failed for user 'IIS APPPOOL\MyAppPool'.
I'm wondering why this is happening, as I seem to be specifying the security username and password to use for the DB in my connection string....so why is it trying to connect as the app pool that my website is running in?
Also, how can I fix this, without having to give MyAppPool (or Network Service if I changed it to that) DB permissions in SQL Server?
Update: I should've mentioned that I initialise my DBContext class using:
namespace MyClassProject
{
public class EfDbContext : DbContext
{
public EfDbContext() : base ("MyDatabase")
{
}
}
}
I found the issue.
When I initialise my DBContext class with : base("MyDatabase"), it overrides the connection string specified in the web.config.
Removing that from my DBContext class, with the database already existing, the site now works in IIS.
However, if I don't have the database created already, (or if I have my database initialiser use DropCreateDatabaseWhenModelChanges or DropCreateDatabaseAlways so that it'll needs to recreate the DB), the initialiser will fail, as it'll try to use an SQL user that doesn't have permissions to create the DB.
My way around it is to use the : base("MyDatabase") and run from Visual Studio initially so the database is created.
Then remove it from code, add the specified user to the DB security in SQL Server, and it'll allow my site to run in IIS thereafter.
Remove Integrated Security=true;. That is the setting that passes the current user off.
When using Integrated Security, the DB is given a token from the user who is currently running the process. In all likelihood, you run Visual Studio from your user account, which likely has Admin permissions on your SQL Server instance.
When IIS runs your app, it uses something called an Application Pool (or App pool). You can have multiple apps in a single pool to be managed together. The app pool also runs under a special user account named for the pool. App pool users exist under a container called "IIS AppPool", so the local user for the DefaultAppPool is IIS AppPool\DefaultAppPool. If you want to grant access to a resource on your local system (including file permissions), you can also grant it to the app pool user or local group IIS_IUSRS to grant it to all app pools.
Remember that these are local accounts, so they will not cross network boundaries. To grant permissions on a different server, you'll need to either use a domain user (or even better, a domain Managed Service Account) or you can set the app pool user to NETWORK SERVICE and then you can grant permissions to MyDomain\MyWebServer$ (the dollar sign is important).
You can use Web.config Transform to have Local connection stirng different from Remote (say in Release mode). To start using it you need to publish your Web App using One-Click Publish from Visual Studio. That's really very handy way to publish web apps!
Looks like that's what you're looking for.
Or set connection string name base on a condition:
public EfDbContext() : base (GetConnectionStringName())
{
}
private static GetConnectionStringName()
{
return RunLocally() : "LocalDatabase" : "RemoteDatabase";
}
private static bool RunLocally()
{
// implement some how
}