I've been building a basic MVC application to begin to learn the ASP.NET MVC framework.
After doing some more reading last night, I learned about "dropcreatedatabaseifmodelchanges" and that sounded like what I want to use while I am in the early development phase.
So to do this in Global.asax I put the following code:
public class MyDbInitializer : DropCreateDatabaseAlways<SwaggersDB> {
}
Where "SwaggersDB" is my database context as follows:
namespace SwaggersMVC.Models {
public class SwaggersDB : DbContext{
public DbSet<Hostel> Hostels { get; set;}
public DbSet<Room> Rooms { get; set; }
public DbSet<Booking> Bookings { get; set; }
public DbSet<RoomBooking> RoomBookings { get; set; }
}
}
I was under the impression that now, when I add a new property to one of my model classes and re-run the application that the corresponding database table would be updated - however when I query the table it has not been added.
I have also enabled migrations via the package manager, and I was thinking that maybe this has been messing with my Database Initializer.
I have tried using "update-database" with migrations but it says it would result in a loss of data. I really don't care about losing data, I just want to recreate the database each time the application runs.
If you use the DropCreateDatabaseAlways initializer, it will do just that and drop and recreate your database each time. You can use the Seed() method in the initializer to repopulate needed tables. This is good up to the point you deploy at which time you would probably want to disable the initializer or switch to something like CreateDatabaseIfNotExists and then use migrations.
In the constructor of the Configuration() class in the migration you can add:
internal sealed class Configuration : DbMigrationsConfiguration<COPERSRMS.MVC.DataContexts.ApplicationDbContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(COPERSRMS.MVC.DataContexts.ApplicationDbContext 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" }
// );
//
}
}
and your database will be updated with each change. Then you can generate a script to update the previously deployed database that will do a compare from the initial schema.
Related
I am trying to insert data from my ASP.NET Core MVC application with Entity Framework to my SQL Server database on localhost.
My model class looks like this:
public class Auto
{
public string Motorleistung { get; set; }
public string Lackierung { get; set; }
public string Felgen { get; set; }
public string Sonderleistungen { get; set; }
}
I already added the DbContext in a new folder (Services/AutoContext class):
public class AutoContext : DbContext
{
DbSet<Auto> Autos { get; set; }
public AutoContext(DbContextOptions<AutoContext> options)
: base(options)
{
Database.EnsureCreated();
}
}
I added this part to the Startup.cs file in the ConfigurateServices method:
public void ConfigureServices(IServiceCollection services)
{
var connectionString = "Server=localhost;Database=Auto;User Id=sa;Password=YourPassword123";
services.AddControllersWithViews();
services.AddDbContext<AutoContext>(o => o.UseSqlServer(connectionString));
}
I am trying to use a class extension with a method to insert properties from auto into the DB:
public static class AutoContextExtensions
{
public static void CreateSeedData(this AutoContext context)
{
var auto = new List<Auto>()
{
new Auto()
{
Motorleistung = "500 PS",
Lackierung = "Gelb",
Felgen = "Chrome",
Sonderleistungen = "Sitzheizung"
}
};
context.AddRange(auto);
context.SaveChanges();
}
}
I am now trying to call the CreateSeedData function from the Startup.cs to pass the data into my database like:
Projectname.AutoContextExtensions.CreateSeedData();
It expects me to give a parameter. What parameter do I have to pass?
(I extracted the code from a sample)
You need an instance of the context to be able to call the extension method.
There are more recommended ways to seed the database in EFCore
1- Use migrations to seed the data
Then EF Core migrations can automatically compute what insert, update
or delete operations need to be applied when upgrading the database to
a new version of the model.
in OnModelCreating in the DBContext
modelBuilder.Entity<Auto>().HasData( new Auto() {
Motorleistung = "500 PS",
Lackierung = "Gelb",
Felgen = "Chrome",
Sonderleistungen = "Sitzheizung"
});
Then Add a new migration
2- Use seeding context
using (var context = new DataSeedingContext())
{
context.Database.EnsureCreated();
var testBlog = context.Blogs.FirstOrDefault(b => b.Url == "http://test.com");
if (testBlog == null)
{
context.Blogs.Add(new Blog { Url = "http://test.com" });
}
context.SaveChanges();
}
a Warning from the docs
The seeding code should not be part of the normal app execution as
this can cause concurrency issues when multiple instances are running
and would also require the app having permission to modify the
database schema.
For more details check the documentation along with full sample project
CreateSeedData is an extension method for AutoContext type.But you are calling method like Projectname.AutoContextExtensions.CreateSeedData(); so you need to pass the parameter value of type autocontext. You should create a variable with AutoContext type ,Then variablename.CreateSeedData() for extension method to work like you expect
I'm having an issue with the current project I'm working on. It works fine and runs without error with it's current classes and functionality. My issue is that I can't seem to add another model, it's corresponding controller and views and get it to actually work.
Usually I would simply add a new model class to the folder, update my dbcontext class with a dbset with the new model as datatype. Then write "update-database -force" in the package manager console and it would apply the automatic migrations. But for some strange reason I can't seem to comprehend, it simply won't do that anymore.
Instead, after I create the model and add the dbset and then trying to update database it runs the update fine, but it doesn't add any new migrations. The funny thing is when I run the project I get the usual error you always get when you have forgotten to update the database where it's recommending code first migrations etc.
I tried checking my config file and it seems the context key is set to applicationuser instead of the proper dbcontext class, which I'm sensing is why it doesn't detect any changes(usually it figures this out itself?). But when I try changing it to the proper one and updating the database again, it gives me an error saying something about asproles is already in the database?
I'm completely lost here and would appreciate any input an experienced vs13 user can give me.
EDIT:
I should mention I have been working on the identity framework recently, which is probably why it has automatically changed the contextkey? But I haven't had any issues during that with any of my existing classes.
NEW EDIT (29-01-2015)
Relevant part of configuration file:
internal sealed class Configuration : DbMigrationsConfiguration<MEV3.Models.ApplicationDbContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
ContextKey = "MEV3.Models.QuestionContext";
}
protected override void Seed(MEV3.Models.ApplicationDbContext 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" }
// );
this.AddUserAndRoles();
}
My QuestionContext file:
namespace MEV3.Models
{
public class QuestionContext : DbContext
{
public DbSet<ExamSet> ExamSets { get; set; }
public DbSet<BlanketSet> BlanketSets { get; set; }
public DbSet<LooseQuestionCase> LooseQuestionCases { get; set; }
public DbSet<Medcase> Medcases { get; set; }
public DbSet<Question> Questions { get; set; }
public DbSet<QuestionChoice> QuestionChoices { get; set; }
public DbSet<QuestionBeforeAfter> QuestionBeforeAfters { get; set; }
public DbSet<Answer> Answers { get; set; }
public DbSet<Tag> Tags { get; set; }
public DbSet<Category> Categories { get; set; }
public DbSet<Institution> Institutions { get; set; }
public DbSet<ExamType> ExamTypes { get; set; }
public DbSet<NewsArticle> NewsArticles { get; set; }
//public DbSet<TaskRecord> TaskRecords { get; set; }
//protected override void OnModelCreating(DbModelBuilder modelBuilder)
//{
// modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
//}
}
My ApplicationDbContext file:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
public DbSet<TaskRecord> TaskRecords { get; set; }
}
When I have the contextkey set to my QuestionContext and try to make changes to any models in that dbset, it gives me an error about the taskrecords (which is strange cuz they are inside the applicationdbcontext class).
This means I can't make any succesful updates of the database if I try to change a model in the QuestionContext.
If I set the contextkey to ApplicationDbContext I can make changes to anything in the applicationdbcontext succesfully, but it wont register any changes done in any of the models in the QuestionContext.
I'm at a loss, the way it works now it seems I can only add new models to the applicationdbcontext or change models in it, can't go back and alter anything in the questioncontext. Any thoughts?
Automatic migrations are succesfully enabled on both contexts btw. I've used this setup succesfully before I started fiddling with the identityframework.
Your application has more than one context, therefore you should tell Update-Migration which configuration to chose from. If the two contexts are in different projects, you can use the -ProjectName <string> command. Otherwise, use the -ConfigurationTypeName <string> command, making sure your configuration classes have different names. Also, make sure your QuestionContext's Configuration is public. More information here.
Also, make sure the connection string is pointing to the right Database when you run the project (under your current build configuration).
Finally, I would recommend you Enable-Migrations as forcing it like you do can lead to data loss.
I am trying to run the code first migration in entity framework 6.0. I have added 4 new entities in my entities modal. However when i run the "add-migration" command in VS 2013, the generated migration file contains the script of all entitles (just like the initial migration) in my modal, though they are already in linked database. Obviously when I rum "Update-Database" commends, it generates entity already exists error. My DBContext class looks like following:
public class BidstructDbContext : DbContext
{
public BidstructDbContext() : base(nameOrConnectionString: "Bidstruct")
{
this.Configuration.LazyLoadingEnabled = false;
}
public DbSet<User> Users { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<Permission> Permissions { get; set; }
public DbSet<Company> Company { get; set; }
// New Added Table
public DbSet<Gadgets> Gadgets { get; set; }
public DbSet<Language> Language { get; set; }
public DbSet<LanguageKeys> TranslationKeys { get; set; }
public DbSet<Translations> Translations { get; set; }
static BidstructDbContext()
{
Database.SetInitializer(new DatabaseInitializer());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
}
}
and DatabaseInitializer class looks like as following:
public class DatabaseInitializer :
// CreateDatabaseIfNotExists<BidstructDbContext> // when model is stable
DropCreateDatabaseIfModelChanges<BidstructDbContext> // when iterating
{
private const int AttendeeCount = 1000;
// EF is NOT a good way to add a lot of new records.
// Never has been really. Not built for that.
// People should (and do) switch to ADO and bulk insert for that kind of thing
// It's really for interactive apps with humans driving data creation, not machines
private const int AttendeesWithFavoritesCount = 4;
protected override void Seed(BidstructDbContext context)
{
}
}
Any idea, how to resolve this problem. Its was working fine for me few days back but now I am facing this problem :(
Check to see if your context keys have changed, in your migration history.
I'm working on a project that has been using automatic migrations, but the automatic migration was not occurring due to a lot of class changes. In trying to switch to non-automatic migration, Add-Migration was regenerating the entire schema.
So I tried putting the manual table changes into the Up() of the DbMigration, and this applied a migration and an entry into the __MigrationHistory table, but with a different context key (the namespace and class name of my configuration file.)
A quick test of renaming the previous (older) migration record's context key to be the same as the current one caused the migration up/down to generate correctly.
Even then...it may not be 100%. Most of my changes were correct, but it started out adding a table which already existed, then turned around and removed it.
I have a test application where I would like to drop and recreate database every time I run the application. This is my context class:
public class MySolutionContext : DbContext
{
public MySolutionContext()
: base("MySolution")
{
Database.SetInitializer<MySolutionContext>(new DropCreateDatabaseAlways());
}
public DbSet<Order> Orders { get; set; }
public DbSet<OrderItem> OrderITems { get; set; }
public void Seed(MySolutionContext context)
{
var order1 = new Order
{
Archive = false,
CompletionDate = DateTime.Now,
Person = "Bartosz"
};
var order2 = new Order
{
Archive = false,
CompletionDate = DateTime.Now,
Person = "Anna"
};
context.Orders.Add(order1);
context.Orders.Add(order2);
context.SaveChanges();
}
public class DropCreateDatabaseAlways : DropCreateDatabaseAlways<MySolutionContext>
{
protected override void Seed(MySolutionContext context)
{
context.Seed(context);
base.Seed(context);
}
}
}
When I run the application for the first time, Seed method is executed and database gets created. However, when I stop and rerun the application, Seed method is not firing at all and previously created database is being used. Why does it happen? What am I missing in my code?
The problem here is that migration is activated in your current project. Related to this article (http://entityframework.codeplex.com/workitem/1689), it's not possible to use migration AND "DropCreateDatabaseAlways" as the initializer at the same time.
If you want to use migration and "DropCreateDatabaseAlways" (which is completely useless, except from testing maybe), you'll have to write a own Init() method, which deletes and creates your database at every application start.
Database.Delete();
Database.Create();
But if you deactivate migration, you can use the initalizer "DropCreateDatabaseAlways".
If the problem is still there without migration here are some hints how to solve this kind of problem:
The Initializer is only executed if you are using the instance of the database or if the database doesn't exist. The seed method is part of your Initializer, so it doesn't get executed as well. To fix this problem you can "work" with the database by accessing part of the data like:
context.Set<Entity>.Count();
or any other method which works with the database.
Another solution is to force the database to call the Initializer:
Database.Initialize(true/false);
Hope this helps.
In a test project, I also needed to drop the database and recreate everything each time I start the tests.
Simply adding the following line was not enough:
Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>());
I tweaked it with this :
Database.SetInitializer(new DropCreateDatabaseAlways<ApsContext>());
using (MyContext context = new MyContext())
{
context.Database.Delete();
}
It's important to know that, if migration is enabled, you need to actually call the database to create the schema. I simply call an entity trying to find something with Id = 0 (so it returns nothing).
I am working on a MVC project with entity framework in automatic migrations and trying to figure out how to update the database after new properties have been added.
I have this scenario.
Before:
public class X {
public int Id {get;set;}
public string Name {get;set;}
public bool IsChecked() {
... // some heavy duty routine
}
}
The code has been released and is working on the production environment but slow so I want to cache the method IsChecked. I alter my class:
public class X {
public int Id {get;set;}
public string Name {get;set;}
public bool Checked {get;set;}
public bool SetChecked() {
bool result = ... // some heavy duty routine
Checked = result;
}
}
Now I want to make an update script which executes the SetChecked on all items in the database.
What approach should be used?
If migration is enabled and set to auto, all you need to do is to add a new migration, in vs just open package-manager console and select the project from the drop down list and type this command:
add-migration "name-of-migration"
this will create a new class under migrations folder then you have to compile and upload new code to the server. After the first query the database will be updated.