Triggering EF migration at application startup by code - migration

Using Entity Framework Migrations (Beta1), using Update-Database command is all good during development.
But when the application is running on some customer's server somewhere, I really want my application to automatically update it's database schema to the latest version when it's started.
Is this possible? Documentation is scarce.

They aren't providing a way to do this until RTM, at which point they have promised a command line app and a msdeploy provider.
Source: http://blogs.msdn.com/b/adonet/archive/2011/11/29/code-first-migrations-beta-1-released.aspx
Of course not being satisfied with that, the powershell command is stored in the packages directory and is plain text, it appears to just load up an assembly called EntityFramework.Migrations.Commands stored in the same directory.
Tracing through that assembly I came up with the following
public class MyContext : DbContext
{
static MyContext()
{
DbMigrationsConfiguration configuration = new DbMigrationsConfiguration() {
MigrationsAssembly = typeof(MyContext).Assembly,
ContextType = typeof(MyContext),
AutomaticMigrationsEnabled = true,
};
DbMigrator dbMigrator = new DbMigrator(configuration);
dbMigrator.Update(null);
}
}
UPDATE: after a bit of experimentation I figured out a few more things
Performing an update in the static constructor for your context is bad as it breaks the powershell commands, much better off adding the code to application startup another way (Global.asax, WebActivator or Main method)
The above code only works when using AutomaticMigrations, you need to set the MigrationsNamespace for it to pickup on manually created migrations
The configuration class I was creating should already exist in your project (added when you install the migration nuget package), so just instantiate that instead.
Which means the code is simplified to
DbMigrator dbMigrator = new DbMigrator(new NAMESPACE.TO.MIGRATIONS.Configuration());
dbMigrator.Update(null);

Another options for this issue is to add
Database.SetInitializer<MyContext>(new MigrateDatabaseToLatestVersion<MyContext, NAMESPACE.TO.MIGRATIONS.Configuration>());
line to your Global.asax Application_Start method.

Related

Can I determine `IsDevelopment` from `IWebJobsBuilder`

Very much an XY problem, but I'm interested in the underlying answer too.
See bottom for XY context.
I'm in a .NET Core 3 AzureFunctions (v3) App project.
This code makes my question fairly clear, I think:
namespace MyProj.Functions
{
internal class CustomStartup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
var isDevelopment = true; //Can I correctly populate this, such that it's true only for local Dev?
if(isDevelopment)
{
// Do stuff I wouldn't want to do in Prod, or on CI...
}
}
}
}
XY Context:
I have set up Swagger/Swashbuckle for my Function, and ideally I want it to auto-open the swagger page when I start the Function, locally.
On an API project this is trivial to do in Project Properties, but a Functions csproj doesn't have the option to start a web page "onDebug"; that whole page of project Properties is greyed out.
The above is the context in which I'm calling builder.AddSwashBuckle(Assembly.GetExecutingAssembly()); and I've added a call to Diagnostics.Process to start a webpage during Startup. This works just fine for me.
I've currently got that behind a [Conditional("DEBUG")] flag, but I'd like it to be more constrained if possible. Definitely open to other solutions, but I haven't been able to find any so ...
While I am not completely sure that it is possible in azure functions I think that setting the ASPNETCORE_ENVIRONMENT application setting as described in https://learn.microsoft.com/en-us/azure/azure-functions/functions-how-to-use-azure-function-app-settings should allow you to get whether the environment is set as production or development by injecting a IHostEnvironment dependency and checking
.IsDevelopment()
on the injected dependency.

EF Core Migration error: "Unable to create an object of type 'ApplicationContext'"

I'm trying to do the migration with EF Core but I get an error - how can I fix this error?
PM> add-migration ini
Unable to create an object of type 'ApplicationContext'. Add an
implementation of 'IDesignTimeDbContextFactory' to
the project, or see https://go.microsoft.com/fwlink/?linkid=851728 for
additional patterns supported at design time.
This will also happen if you have multiple start-up projects - when migrating, just select one (in VS right click Solution and Set StartUp Projects...)
I had the same issue with asp.net core 3.1.
For me, it was pretty straight forward, adding an implementation of IDesignTimeDbContextFactory to the main web project fixed the issue.
A basic implementation looks something like this:
public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<YourDbContext>
{
public YourDbContext CreateDbContext(string[] args)
{
IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var builder = new DbContextOptionsBuilder<YourDbContext >();
var connectionString = configuration.GetConnectionString("DefaultConnection");
builder.UseSqlServer(connectionString);
return new YourDbContext(builder.Options);
}
}
Please refer to this blog post on the subject.
It is highly probably because of your default startup project:
Open Package manager console and write this command:
PM> Add-Migration -StartupProject[YourProjectPath(just press a tab all the.csproj paths will come up, and then choose your DataBaseLayer project to execute a migration for)] migrationName
In this way you don't need to again go to solution and set startup
project with your webProject whenever you finished adding a new migration.
1.Modify your code in ConfigureService with :
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
x => x.MigrationsAssembly("WebApplication")));
In command line which includes WebApplication.csproj:
dotnet ef migrations add Init
I got this error when creating a migration. In my code in the ApplicationContext constructor, I used the Database.EnsureCreated() method. Typically this method is used for small applications where you need to check for the existence of a database and create it once. When creating an application with a database that will change with new versions and using migrations, you should not use this method.
In my case, this error existed because I was applying migrations at runtime. After removing Database.Migrate(); in my code, I was able to add the new migration without errors.
By the way, I used EF Core .NET command-line interface (CLI) tools, not the Package Manager Console (I run into problems I couldn't solve when I was using the Package Manager Console, so I went for the CLI alternative, and it was worth the change).
Also, make sure you use the appropriate options to select your startup project and your target project.
I got this error when the Primary Key for the table was not set manually (with attributes or with FluentAPI) and was not determined automatically (not "Id" kayword and not "TableName" + "Id")
Stack: ASP.NET Core EF + Postgres (Npgsql)

MVC4 Getting Migrations to Run when publishing app through visual studio 2012

I created a visual studio 2012 MVC4 App. I am testing the "publish" functionality by right clicking the project and choosing publish. I followed the instructions here. I can connect to the remote web server and the folders get published to the correct folder, except the content folder for some reason.
When I run browse to the remote web server it prompts me for login so the app is working. However, the migrations never happened. The only tables created are the simplemembership tables, so I know the web server is connecting to the remote db server. No other tables are created and the seed method doesn't run. I seed the roles and a default user.
I checked the box in publish settings that says "Execute Code First Migrations (runs on application start)"
Everything works fine on my localdb connection string for local testing. Just can't figure out how to create db from existing migrations and seed when I publish to live site, note I will only seed once. Is there a way to specify which migrations to run again? I can copy the migrations and run on the database server but why the extra step?
EDIT:
When adding the database.setinilizer to my context I now get an error saying some of my fields in my userprofile table are not there, I use simple membership. This error occurs on the first page load after web publish, then on proceeding page loads I get an error The "WebSecurity.InitializeDatabaseConnection" method can be called only once.
HOwever, it does create my simplemembership tables now but my migration for all other tables never runs, that is why I am missing the additional user profile fields.
EDIT:
Basically I am not checking if websecurity is initialized prior to calling WebSecurity.InitializeDatabaseConnection so that resolved that issue. Now I have it partially working. The app creates the simplemembership tables fine but since I added tables to the UserProfile table I can't seed until I change them. So instead I manually create the userprofile table and have the app create the rest of the tables. Then I comment out the userprofile table in my initial migration. After this when I sign in it will then create the rest of my tables.
Open issue is how to get my database migration to run prior to the simplemembership initialization?
To get migration work on remote server, you need to add use SetInitializer in you Context class first :
static MyDatabaseContext()
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyProjectContext, Migrations.Configuration>());
}
And in you Migration Configuration you need to add this code :
public Configuration()
{
AutomaticMigrationsEnabled = false;
AutomaticMigrationDataLossAllowed = false;
}
I don't select the "Execute Code First Migrations (runs on application start)", and just after setting initialization in MyProjectContext, it does the migration for me.
If you have done by here, for seed your data, you can do same as below in your Migration configuration class:
protected override void Seed(MyProject.Models.MyProjectContextcontext)
{
DoSeed(context);
}
private void DoSeed(MyProjectContext context)
{
var users = new List<User>
{
new Site { UserId = 1, Name = "TestUser", IsDeleted = false },
new Site { UserId = 2, Name = "TestUser2", IsDeleted = false }
};
users.ForEach(s => context.Users.AddOrUpdate(s));
context.SaveChanges();
}
I have not selected the "Execute Code First Migrations (runs on application start)" on deploy Profile.
For some more details on this see this:
This link
and
This link
Hope this helps(as it worked for me), otherwise please add any error if there is, when you deploy you app.
I think the issue is ,Because of the fact that as long as you have not tried to access data
or create any data from/in site, which needs to connect to database, the migration and seeding
will not run"
And the reason for running migration on your site after logging into the site,
would be because your site need to be authorised in all pages, or the page that you want
to see data in.
If you have a page example home page, that does not authorization to access to the page,
you will see the page and if in this page there is some data that needs to be fetched from
data base, you may see the migration runs.
I found this from this Deploy it to IIS, but not sure if it is the reason.
Please let me know if your migration still has not ran if you browse your home page that
has data and no authentication needed for this data access.

Can MSBuild (via Microsoft.Build namespace) load a project from memory?

I'm working on a service that will download a .NET solution from a repository and build it (not unlike a continuous-integration build service). What I want to know is, using MSBuild programmatically via the Microsoft.Build namespace classes, can I can load the solution and project(s) into memory and build it without first saving them to disk in a temporary folder?
I'm still learning MSBuild and trying things, but I figure someone on Stack Overflow has tried this and has some insight.
I can't speak to whether this is a good idea or not, but it's possible to do.
ProjectInstance has a constructor that accepts a ProjectRootElement, which can be constructed via the Create(XmlReader) method. And as you may know, XmlReader can be attached to various Streams including a MemoryStream.
Here's how something like this may look:
var xmlReader = XmlTextReader.Create([your existing memory stream]);
var project = ProjectRootElement.Create(xmlReader);
var buildParams = new BuildParameters();
var buildData = new BuildRequestData(new ProjectInstance(project),
new string[] { "Build", "Your Other Target Here" });
var buildResult = BuildManager.DefaultBuildManager.Build(buildParams, buildData);
After researching MSBuild and all the involved components, it appears as though it's necessary to have the entire project set up on the file system before being able to build it. Unfortunately, what I'm trying to do just can't be done with the provided toolset.

Conversion of V2 Ninject Binding to V3

I've been banging my head at this for about 8 hours now, and I just can't seem to find a simple explanation on how to change my custom bootstrapper for ninject (Last worked on the code back in v2.x.x.x) to the new v3.0.0.0 syntax.
I currently have the following:
public class NinjectCustomBootStrapper : NinjectNancyBootstrapper
{
protected override Ninject.IKernel GetApplicationContainer()
{
return Program.MyContainer;
}
}
in a septate class, and :
public static IKernel MyContainer
{
get { return _myContainer ?? (_myContainer = CreateKernel()); }
set { _myContainer = value; }
}
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<CardMonitorService>().ToSelf().InSingletonScope();
return kernel;
}
in my main program 'Program.c' in a command line app.
Iv'e since updated ninject to V3.0.0.0 only to find that there's been some breaking changes. I'll admit I don't use ninject very often (I usually use structuremap), and the only reason this project does is I didn't write it originally.
Since I've upgraded Ninject, now when the app is started up I get the following exception:
Method not found: 'Ninject.Syntax.IBindingWhenInNamedWithOrOnSyntax`1<!0>
Ninject.Syntax.IBindingToSyntax`1.ToConstant(!0)'.
After a ton of searching and researching, the closest I've been able to find is this:
http://sharpfellows.com/post/Ninject-Auto-registration-is-changing-in-version-3.aspx
Which while it points me in the right direction, still isn't quite a solution as I'm not using a custom binding generator.
So my question is this.
How do I rewrite the above so that my project once again works and the WCF service when called gets the correct singleton binding handed to it when a request comes in. Going back to ninject 2 is not an option, as other dependencies in the project that have been added have forced the v3 upgrade and these add new functionality that's been requested hence why I'm working on it.
For reference this is a .NET4 build, running on NancyFX with a self hosting WCF setup as a windows service using Topshelf to provide the SCM interface.
Cheers
Shawty
Addendum to clear things up a little
This is an existing project that was originally written sometime back, I've been asked to add some new features to the project.
As part of adding these new features I have been required to upgrade the version of Ninject being used from an earlier version to V3.0.0.0 as newer dependencies added to the project require the newer version of Ninject.
Under the previous V2 of Ninject the code I have given above worked fine, with no issues, since the project has had Ninject V3 added I now get the exception as described above.
I cannot go back to the earlier version of Ninject as that will mean not being able to add the new functionality that I'm adding.
From the research I've done so far the sharpfellows link above is the closest explanation of my issue that I've managed to find so far on the internet.
I don't use Ninject very often, so I've not got the background to know what's changed between V2 & V3 that (based on my research) is the cause of my issue.
I need to know how to change my code written under V2 (and shown above) so that it works under V3.
MissingMethodException is usually a deployment problem. You compile against a different assembly than you deploy. Check that you deployed the same version and same build.
So after a week or so it turns out that the problem was that the Nancy dev team broke binary comparability with the latest version of ninject (or vice versa) :-)
There is a GitHub pull request to fix this available at :
https://github.com/NancyFx/Nancy.Bootstrappers.Ninject/pull/6
However the next version 'Nancy.Bootstrapper.Ninject' 0.12 will be out on NuGet soon which will have the fix implemented.