Migrations in separate assembly how to avoid hardcoded connection string? - asp.net-core

I'm writing ASP.NET Core and Entity Framework Core application and I want to store my data access layer in separate assembly, so I followed this tutorial: http://www.michael-whelan.net/ef-core-101-migrations-in-separate-assembly/
But I would also like to avoid hardcoding connection string. I tried to store it in JSON config file or as environment variable and get it using ConfigurationBuilder but when using command line migration tool dotnet ef migrations none of these are available.
Is there any way to solve this problem? I'm using 1.0.1 versions of both .NET Core and EF Core.

To solve this issue I create a class library only for migration with a DbContext deriving from my DbContext but with hard connected connection string.
using Microsoft.EntityFrameworkCore;
namespace ChatLe.Repository.Identity.SqlServer
{
public class ChatLeIdentityDbContext: ChatLe.Models.ChatLeIdentityDbContext
{
public ChatLeIdentityDbContext()
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=chatle;Trusted_Connection=True;MultipleActiveResultSets=true");
base.OnConfiguring(optionsBuilder);
}
}
}
Then I launch ef tool command like this :
To add a migration run dotnet ef --startup-project {path to my startup project} migrations add login-activity --context ChatLe.Repository.Identity.SqlServer.ChatLeIdentityDbContext
To upgrade the database run dotnet ef --startup-project {path to my startup project} database update --context ChatLe.Repository.Identity.SqlServer.ChatLeIdentityDbContext
Read the full sample on my git hub project : https://github.com/aguacongas/chatle/tree/develop/src/ChatLe.Repository.Identity.SqlServer

Related

Migration targets 2.1.0-rtm-30799 even though Target Framework is .Net Core 2.1

I am using VS2017 version 15.7.3
I am curious as to why a new solution will create a database with migrations listed as being version 2.1.0 but if the identity tables for the very same database are added via the PM Console then the migration will be listed as rtm.
Steps to demonstrate what I mean.
I build a new solution using the ASP.NET Core Web Application template
Choosing Individual User Accounts for Authentication
Then the generated code shows that the data migration will have ProductVersion 2.1.0
namespace WebApplication14.Data.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.1.0")
//etc
I run the application, register a user and click Migrations Applied to create the database.
Next
I use SQL Server Object Explorer to delete all the tables.
I delete the Migrations folder from the solution
In Package Manager Console I type
Add-migration one
Now the ApplicationDbContextModelSnapshot refers to the rtm ProductVersion
namespace WebApplication14.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.1.0-rtm-30799")
If I then run
update-database
the tables get created in the database with the rtm product version listed in __EFMigrationsHistory
I am wondering if this difference is by design or not and if so why it is designed that way.

Migrations are not being applied

I am trying to apply migrations programmatically using Entity Framework Core 2.0, in a Code-First ASP.Net Core 2.0 project. If I run the migrations manually through a terminal, they're applied without issue. Applying the migrations in my Startup class though results in the database model never changing.
Am I doing this wrong?
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.AddMvc();
services.AddEntityFrameworkSqlite();
services.AddDbContext<ApplicationContext>(options => options.UseSqlite("Data Source=blogging.db"));
services.AddDbContext<UserContext>(options => options.UseSqlite("Data Source=blogging.db"));
}
// 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.UseMvc();
var services = app.ApplicationServices.GetService<IServiceScopeFactory>();
var context = services.CreateScope().ServiceProvider.GetRequiredService<ApplicationContext>();
context.Database.Migrate();
}
I can run this from the terminal and it works fine:
dotnet ef migrations add FourthMigration --context EFCore_Test.DataAccess.ApplicationContext
I have multiple DataContext Types; only one represents the entire data model and the rest are used just to access the database in a more domain specific manor. The ApplicationContext represents my "everything + kitchen sink" data context. It's this context that I perform my migrations and updates with.
In preparation for deploying to Azure, I want to have the web-app migrate itself with each deployment, instead of having to wire up powershell scripts to run the dotnet core tooling commands.
The new Migration files aren't picked up and added to the solution explorer in Visual Studio for macOS. I originally didn't think anything of this because I assumed the IDE was using dotnet build under the hood, which would pick up the migration files. I'm not sure what the IDE does, but when I build with those migration files missing, they're not included in the compiled assembly. This would cause the app to never migrate on startup.
I manually added the migration classes in the Solution Explorer, then ran the app and the migration was applied. I repeated this several times with multiple migrations and never had another issue.

update Migration Command Fails for ConfigurationDbContext and PersistentGrantDbContext

I have been trying to get IdentityServer4 version 1.5.2 to work for a few days now without success. I am using VS2017
My Entity classes,DataContexts, repositories and migrations are resident in a .Net Standard Library (1.6). So far so good except when I run update-migration command for "PersistenGrantDbContext" and "ConfigurationDbCOntext". I get the error message
Could not load file or assembly 'System.Data.SqlClient, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified.
The datacontext classes that i created myself dont seem to have this problem after implementing "IDbContextFactory" interface
Here I have the implementation for the two culprits
public class TemporaryDbContextFactoryScopes : IDbContextFactory<PersistedGrantDbContext>
{
public PersistedGrantDbContext Create(DbContextFactoryOptions options)
{
var builder = new DbContextOptionsBuilder<PersistedGrantDbContext>();
builder.UseSqlServer("Server=-------;Database=-----------;Trusted_Connection=True;MultipleActiveResultSets=true",
optionsBuilder => optionsBuilder.MigrationsAssembly(typeof(UserDbContext).GetTypeInfo().Assembly.GetName().Name));
return new PersistedGrantDbContext(builder.Options, new OperationalStoreOptions());
}
}
public class TemporaryDbContextFactoryOperational : IDbContextFactory<ConfigurationDbContext>
{
public ConfigurationDbContext Create(DbContextFactoryOptions options)
{
var builder = new DbContextOptionsBuilder<ConfigurationDbContext>();
builder.UseSqlServer("Server=---------;Database=--------;Trusted_Connection=True;MultipleActiveResultSets=true",
optionsBuilder => optionsBuilder.MigrationsAssembly(typeof(UserDbContext).GetTypeInfo().Assembly.GetName().Name));
return new ConfigurationDbContext(builder.Options, new ConfigurationStoreOptions());
}
}
I have installed the latest version of System.Data.SqlClient still not working
Just wanted to share what I did to get things rolling. Not sure if that is the right approach though
First I did this in the class library .csproj
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.1" />
<DotNetCliToolReference Include="Microsoft.Extensions.SecretManager.Tools" Version="1.0.1" />
which turned out to be a bad idea because that was throwing out that error now without it running my migrations with my web app as my start up project return the error "Not context blah blah with name blah blah found"
So I realised contexts that I created myself worked without glitches so I did this for PersistentGrantDBCOntext and COnfigurationGrantDbContext
public class PGrantDbContext: PersistedGrantDbContext
{
public PGrantDbContext(DbContextOptions<PersistedGrantDbContext> options, OperationalStoreOptions storeOptions) : base(options, storeOptions)
{
}
}
and
public ConfigDbContext(DbContextOptions<ConfigurationDbContext> options, ConfigurationStoreOptions storeOptions):base(options,storeOptions)
{
}
and everything went smoothly.
Just not so sure if it is the right approach though
I was also in a similar situation few weeks ago and here's how I solved it.
Very similar to yours, I have two projects called Company.Identity (.NETCoreApp) as my Identity Project and Company.Identity.Data (.NETStandard 1.6) for migrations. I Use Company.Identity Project as the startup project for migration purposes as it is in the same solution as my data project and I didn't want to clutter the solution with yet another project just as a startup project for migrations.
I followed the tutorial in here.
The cli tool reference <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.1" /> is in my Company.Identity.csproj file.
I followed all the steps in the above tutorial except for the following difference
In ConfigureServices method in the Company.Identity Startup class, I have set the migrationsAssembly to Company.Identity.Data
var migrationsAssembly = "Company.Identity.Data";
Note that Company.Identity project has already got IdentityServer4.EntityFramework nuget package installed. This enabled me to add the migration for PersistedGrantDbContext. But when I tried to run migration for ConfigurationDbContext it gave me a build error. This was because the migrations generated for PersistedGrantDbContext requried IdentityServer4.EntityFramework nuget package. So I had to install that in the Company.Identity.Data project
I was able to add migrations using the following commands after above changes through command prompt in my Company.Identity project.
dotnet ef migrations add InitialIdentityServerPersistedGrantDbMigration -c PersistedGrantDbContext -o Migrations/IdentityServer/PersistedGrantDb -p ../Company.Identity.Data
dotnet ef migrations add InitialIdentityServerConfigurationDbMigration -c ConfigurationDbContext -o Migrations/IdentityServer/ConfigurationDb -p ../Company.Identity.Data
Hope that helps
this may help you specially if you have docker-compose in your project. i was able to resolve this issue by removing the docker-compose project and then creating the migrations then adding it again.
https://stackoverflow.com/a/60320410/4977086

Automatically execute migrations when publishing ASP.NET Core app

Question
Is there any ways that I can automatically execute the migration code (EF 7) when publishing my ASP 5 application to IIS using Web Deploy?
I Tried
in the project.json, I added this code in the scripts:
"scripts" : {
"prepublish": ["dnx ef database update", "other commands..."],
"postpublish": ["dnx ef database update"]
}
none worked for me.
Additional Info
I followed the instructions on this link to deploy my ASP 5 RC-1 web application to IIS using web deploy.
After doing so in the publish settings I have:
Using web deploy in ASP 4 applications I have additional database options:
Use context.Database.Migrate()
You can call this from your Startup class:
using (var context = new MyContext(...))
{
context.Database.Migrate();
}
It will migrate your database to the latest version on application startup. But be careful doing it, maybe comment out this code and uncommend only when you want to run your migrations.
Apparently this process does not work now. https://github.com/aspnet/Home/issues/622 After you publish you should find the power shell script with the name of "profile name"-publish.ps1. Then add your commands below these three lines close to the end of this file. You might want to use powershell to make it easier to debug.
'Calling Publish-AspNet' | Write-Verbose
# call Publish-AspNet to perform the publish operation
Publish-AspNet -publishProperties $publishProperties -packOutput $packOutput
So I added the option -environment to my ef database command. Now it works:
"postpublish": ["dnx ef database update -e Staging"]
I have four different appsettings.json which different connection string for each environment. Just needed to indicate the environment for the command to work.
In you Startup.cs class add this code
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var context = serviceScope.ServiceProvider.GetService<AppDBContext>();
context.Database.Migrate();
}
}

EF6 localdb for integration testing

I am developing a WPF application which requires some database access. For development purposes I am using localdb with database migrations enabled and EF6.
What I cannot figure out is how do I configure a separate localdb database for integration tests inside a VS2013 test project and run the database migrations to bring it up to date and then seed it with data.
Here's what I've ended up doing based off information from several sources. First, in the App.Config of my test project I've added the following
<connectionStrings>
<add name="YourContextName" connectionString="Data Source=(localdb)\mssqllocaldb;Integrated Security=true;AttachDBFilename=|DataDirectory|\databasename.mdf"
providerName="System.Data.SqlClient" />
</connectionStrings>
Then I created the following class:
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Data.Entity;
using MyProject.Model.Entities;
using MyProject.Migrations;
namespace IntegrationTests
{
public class DatabaseInitializer : DropCreateDatabaseAlways<MyProjectDataContext>
{
}
[TestClass]
public class Initalize
{
[AssemblyInitialize]
public static void AssemblyInit(TestContext context)
{
AppDomain.CurrentDomain.SetData("DataDirectory", context.TestDeploymentDir);
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyProjectDataContext, Configuration>());
//Database.SetInitializer(new DatabaseInitializer());
}
}
}
My understanding is that this runs before each test run. The SetData call updates the |DataDirectory| used in the App.Config so that it is unique and doesn't try to use the same instance as the normal project.
From there I have the choice of either running the migrations and then supplying some test data as part of the test or to run an initializer that sets up the test data. Note that to run the migrations the configuration class generated by the migration must be changed from internal to public.
This seems to work so far. Not sure if this is the best way or if I can combine the migrations and then run a different seed for the tests.
Within your test project, you could set Database.SetInitializer() (or databaseInitializer within the project config file) with MigrateDatabaseToLatestVersion or with your own IDatabaseInitializer that calls DbMigrator.Update, and use a custom DbMigrationsConfiguration.
The test project could use a separate connection string for using LocalDB, and if the file referenced by your connection string's AttachDBFilename, then the initializer will try to create it.
Might be helpful: Recreate and Reseed LocalDb Before Each Unit Test
...But DropCreateDatabaseAlways won't work with EF6 migrations