EF Code First DropCreateDatabaseAlways not executed - c#

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).

Related

What is the behavior of entity framework when a table does not exist yet for an entity?

We are working with multiple people on a software landscape. And therefore we find it convenient to work in parallel. So the C# code for the entity is being added, while the table definition is being created (db first approach).
My question is, can this MyEntity, and DbSet be added already to the C# code in the context, without EF throwing exceptions, because the DB table is not in the database yet. This would allow the C# code development to continue (creating repository, provider, validations, etc) in the meanwhile. Of course under the condition that the DbSet is not being used in the C# code.
So is EF fine with the DbSet being part of the context, while the table for MyEntity does not exist in the database yet?
Yes you can add the entity to the context, without having the table. I verified this with a test project. See the code below. It connects to an existing DB on the local machine. The context is created without any issues. You can even use the Entities property, add an Entity, and SaveChanges(). It will create the table for you the first time. The second time the table is not there (because it got removed manually after creation for instance), it will throw an exception.
It will throw an exception because it keeps records of the state of the database in __MigrationHistory.
using System.Data.Entity;
namespace EntityFrameWorkMissingTableTest
{
class Program
{
static void Main(string[] args)
{
using (var context = new MyContext("Data Source=localhost;Initial Catalog=MyContext;Integrated Security=True"))
{
context.Entities.Add(new Entity());
context.SaveChanges();
}
}
public class MyContext : DbContext
{
public MyContext(string connectionString)
: base(connectionString)
{
}
public DbSet<Entity> Entities { get; set; }
}
public class Entity
{
public int Id { get; set; }
}
}
}

Entify Framework: Is it legal to reuse an entity added to a disposed context

Suppose I have created an Entity Framework entity1 and added it to dbContext1.
I then, for any reason, Dispose of dbContext1 and create a fresh DbContext2.
Is it OK to now reuse entity1 by adding it to dbContext2?
EF Version is 6.1.3.
public class DbSaver
{
MyDbContext _context;
public DbSaver()
{
MyDbContext _context = new MyDbEntities();
}
public void MustDoSave(/*whatever*/)
{
bool saved = false;
MyEntity entity1 = new MyEntity() { /*setup entity field values here*/ };
_context.Add(entity1);
while (!saved)
{
try
{
_context.SaveChanges();
saved = true;
}
catch
{
_context.Dispose(); // dispose the original context ("contextA)
_context = new MyDbEntities(); // create a fresh one
// the questionable part:
_context.Add(entity1); // is it legal to now reuse an entity previously added to the old context ("contextB")??
Thread.Sleep(1000); // wait before fresh retry
}
}
}
}
It is a tricky question. If the changes were persisted, then the entity will have identity field populated. Don't assume that it will not be populated just because the transaction has been rolled back - that is the implementation detail of the DbContext class.
Consequently, if you add the same entity to the new DbContext assuming that it is still new, i.e. without its identity field populated, you might get into troubles when you try to persist changes for the second time.
My advice is not to do that, since correctness of your code then depends on implementation details of Entity Framework. Needless to say, implementation may change in the future and then your code which seems to be working fine today will stop working tomorrow.
Yes, it is, as the entity does not keep a reference to the context (or contexts) that it is currently associated with.

How to switch from migrations to dropcreatedatabaseifmodel changes

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.

Entity framework attach: object with same key exists

I am building a windows form application, and I use multiple DBContext instances (mostly one per Business layer call).
After literally days of dealing with an issue (while inserting new entities, the ones they referred to were added as new ones, instead of using the existing entities), I found out that the problem was I had to attach the existing entities to the context.
All was good for about 2 hours, when I then got errors while attaching: the entity with the same key exists in the context.
I tried testing before attaching (similar method for every entity type):
private void attachIfNeeded(POCO.Program myObject, myContext context)
{
if (!context.Set<POCO.Program>().Local.Any(e => e.ID == myObject.ID))
{
context.programs.Attach(myObject);
return true;
}
else
{
myObject = context.Set<POCO.Program>().Local.Single(e => e.ID == myObject.ID);
return false;
}
}
But the tests return false, but it still fails when attaching.
So basically, if I don't attach, it will add a new entity instead of using the existing (and intended) one. If I do attach, there's an error I can't figure out.
I have looked around (doing this the whole day now) and I actually (think I) know what the problem is:
The entity I am trying to add has multiple relationships, and other entities can be reached by multiple paths. Could that cause the problem?
Please help with this, solutions out there really make no sense to me and haven't worked.
I am really close to the point where I will try-catch around the attach statement and be done with it. But I will hate doing it.
Here are my entities (not all of them, but this should be enough):
public class Word
{
[Key]
public int ID {get;set;}
[Required]
public string word { get; set; }
public WordCategories category { get; set; }
public Word parent {get;set;}
public List<Unit> units { get; set; }
public Program program { get; set; }
public List<Lesson> lessons { get; set; }
public Word()
{
units = new List<Unit>();
lessons = new List<Lesson>();
}
}
public class Unit
{
[Key ]
public int ID { get; set; }
[Required]
public string name { get; set; }
public string description { get; set; }
public List<Lesson> lessons { get; set; }
public Program program {get;set;}
public List<Word> words { get; set; }
public Unit()
{
lessons=new List<Lesson>();
words = new List<Word>();
}
}
And here is where I am calling the attach method. The error is thrown on the first attach:
public int addWords(List<POCO.Word > words,int programID, int unitID,int lessonID)
{
CourseHelperDBContext context = getcontext();
int result;
foreach(POCO.Word a in words)
{
foreach (POCO.Unit b in a.units)
attachIfNeeded(b, context);
foreach(POCO.Lesson c in a.lessons )
attachIfNeeded(c,context);
attachIfNeeded(a.program,context);
if (a.parent != null)
attachIfNeeded(a.parent,context);
}
context.words.AddRange(words);
result = context.SaveChanges();
return result;
}
I cannot believe I'm having so many issues with this. I just want to store those entities, add some (I haven't gotten to the point where I would change them) and save it.
So far I've figured:
Some words are new, some exist and some are changed (mostly parent property);
All units exist, as do programs and lessons (so I need to attach them);
The object graph contains multiple paths to entities, some of which exist, some of which are new;
I am using a new context for every request. I run into other issues when I was using the same all the time. I found solutions that pointed to this pattern, and I think it's OK since that's what you'd do on an ASP MVC project.
All these could be causing problems, but I don't know which and how to work around them.
I think I can make this work by adding one word at a time, and pulling programs, lessons and units every time... but that means many many round trips to the DB. This can't be the way.
Back to this after quite some time, the problem in this case was that I needed to retrieve the entities that were present on my relationships.
The solution was neither attach (because it would fail if the entity is already attached) nor add (because it already existed on the DB).
What I should have done was to retrieve every entity related to the one I was adding.
This helped:
Entity Framework creating new entity with relationship to existing entity, results in attempt to create new copy of the existing entity
After attaching the entity, try setting the entity state to modified.
context.programs.Attach(myObject);
context.Entry(myObject).State = EntityState.Modified;
I think there's a mistake in your test logic.
If entity does not exist in database, you should be adding instead of attaching. Your code is attaching if it can't find an entity when it should really be adding.
Code to add a new entity (Create/Insert)
context.Set<T>.Add(entity);
Code to attach an entity (Update)
context.Set<T>.Attach(entity);
context.Entry(entity).State = EntityState.Modified;
If your code is failing on the first attach, that would be attachIfNeeded(b,context); ? I don't think you have shown us the code for this.
I share my experience with the same exception.
First, here is my code:
public void UpdateBulk(IEnumerable<Position> pDokumentPosition, DbDal pCtx)
{
foreach (Position vPos in pDokumentPosition)
{
vPos.LastDateChanged = DateTime.Now;
pCtx.Entry(vPos).State = EntityState.Modified;
}
pCtx.SaveChanges();
}
I got the same exception on the EntityState.Modified line.
In my case the problem was that, when set the vPos state to modified, then all the related objects (vPos.Document and vPos.Produkt) loaded in the context with unchanged state.
In the foreach first step it not makes any exception, just on the second step, because eg. the related Dokument entity has already been loaded in the memory/context (so the key property of the Dokument too).
And how i solve it? (maybe not the best solution):
I detach the related entites in every step with this lines:
if (vPos.Dokument != null)
{
pCtx.Entry(vPos.Dokument).State = EntityState.Detached;
}
if (vPos.Produkt!=null)
{
pCtx.Entry(vPos.Produkt).State = EntityState.Detached;
}
If you have better solution, I'm looking forward to it...
You can try this
context.words.Add(words);
result=context.SaveChanges();

Entity Framework - Migrations - Code First - Seeding per Migration

I am looking into Migrations in an effort to clean up our deployment processes. The less manual intervention required when pushing a change to production the better.
I have run into 3 major snags with the migrations system. They are show stoppers if I can not figure out a clean way around them.
1. How do I add Seed data per migration:
I execute the command "add-migration" which scaffolds a new migration file with Up and Down functions. Now, I want to automatically make changes to the data with both Up and Down changes. I don't want to add the Seed data to the Configuration.Seed method as this runs for all migrations which ends in all sorts of duplication problems.
2. If the above is not possible, how do I avoid duplications?
I have an enum that I loop through to add the values to the database.
foreach(var enumValue in Enum.GetValues(typeof(Access.Level)))
{
context.Access.AddOrUpdate(
new Access { AccessId = ((int)enumValue), Name = enumValue.ToString() }
);
}
context.SaveChanges();
Even though I am using AddOrUpdate, I still get duplicates in the database. The above code brings me to my 3rd and final problem:
3. How can I seed Primary Keys?
My enumerable with the above code is:
public class Access
{
public enum Level
{
None = 10,
Read = 20,
ReadWrite = 30
}
public int AccessId { get; set; }
public string Name { get; set; }
}
I am specifying the values that I want as my primary key, but Entity Framework seems to ignore it. They still end up being 1,2,3. How do I get it to be 10,20,30?
Are these limitations of EF at the moment or are they intentional constraints to prevent some other kind of catastrophe I am not seeing?
When I have fixed data that I want to insert with a migration, I put the inserts directly in the Up() migration using calls to Sql("Insert ..."). See the note halfway down this page: how to insert fixed data
You prevent duplicates in the Seed method by calling the AddOrUpdate overload that takes an identifier expression specifying the natural key - see this answer and this blog entry.
Primary keys that are integers are created as identity fields by default. To specify otherwise use the [DatabaseGenerated(DatabaseGeneratedOption.None)] attribute
I think this is a good explanation of Initializer and Seed methods
Here is an example of how to use the AddOrUpdate method:
foreach(var enumValue in Enum.GetValues(typeof(Access.Level)))
{
context.Access.AddOrUpdate(
x => x.Name, //the natural key is "Name"
new Access { AccessId = ((int)enumValue), Name = enumValue.ToString() }
);
}
As a possible solution to item 1, I made an implementation of the IDatabaseInitializer strategy which will run the Seed method of each pending migration only, you will need to implement a custom IMigrationSeed interface in each of your DbMigration classes, the Seed method will then be implemented right after Up and Down methods of every migration class.
This helps to solve two problems for me:
Group Database Model Migration with Database Data Migration (or Seeding)
Check what part of the Seed migration code should really be running, not checking data in the database but using already known data which is the database model that was just created.
The interface looks like this
public interface IMigrationSeed<TContext>
{
void Seed(TContext context);
}
Below is the new implementation that will call this Seed method
public class CheckAndMigrateDatabaseToLatestVersion<TContext, TMigrationsConfiguration>
: IDatabaseInitializer<TContext>
where TContext : DbContext
where TMigrationsConfiguration : DbMigrationsConfiguration<TContext>, new()
{
public virtual void InitializeDatabase(TContext context)
{
var migratorBase = ((MigratorBase)new DbMigrator(Activator.CreateInstance<TMigrationsConfiguration>()));
var pendingMigrations = migratorBase.GetPendingMigrations().ToArray();
if (pendingMigrations.Any()) // Is there anything to migrate?
{
// Applying all migrations
migratorBase.Update();
// Here all migrations are applied
foreach (var pendingMigration in pendingMigrations)
{
var migrationName = pendingMigration.Substring(pendingMigration.IndexOf('_') + 1);
var t = typeof(TMigrationsConfiguration).Assembly.GetType(
typeof(TMigrationsConfiguration).Namespace + "." + migrationName);
if (t != null
&& t.GetInterfaces().Any(x => x.IsGenericType
&& x.GetGenericTypeDefinition() == typeof(IMigrationSeed<>)))
{
// Apply migration seed
var seedMigration = (IMigrationSeed<TContext>)Activator.CreateInstance(t);
seedMigration.Seed(context);
context.SaveChanges();
}
}
}
}
}
The good thing here is you have a real EF context to manipulate Seed Data, just like standard EF Seed implementation. However this can get strange if for example you decide to delete a table that was Seeded in a previous migration, you will have to refactor your existing Seed code accordingly.
EDIT:
As an alternative to implement the seed method after the Up and Down, you can create a partial class of the same Migration class, I found this useful as it allows me to safely delete the migration class when I want to re-seed the same migration.
Hi I have found a very useful information for your problem in this link:
Safari Books Online
"1. How do I add Seed data per migration:"
As you see in the example you need to create a new confiugration for seeding.
This seed Configuration must be called after migration.
public sealed class Configuration : DbMigrationsConfiguration
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(SafariCodeFirst.SeminarContext 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" }
// );
//
}
}
"2. If the above is not possible, how do I avoid duplications?"
AddOrUpdate Must help you exactly to avoding the duplicates if you get an error here you might have a configuration error post the call stack please. See the example!
"3. How can I seed Primary Keys?"
Here it is also on your key definition. If your key DatabaseGenerated(DatabaseGeneratedOption.Identity) than you do not have to provide it. In some other senarios you need to create a new one it is depending on the key type.
"Are these limitations of EF at the moment or are they intentional constraints to prevent some other kind of catastrophe I am not seeing?"
Not that I know!
OK, so with a bit of bashing I have managed to bash EF into submission.
Here is what I did:
1. There is no way that I found to see data for a specific migration. It all must go into the common Configuration.Seed method.
2. To avoid duplicates I had to do 2 things.
For my enums, I wrote the following seed code:
foreach (var enumValue in Enum.GetValues(typeof(Access.Level)))
{
var id = (int)enumValue;
var val = enumValue.ToString();
if(!context.Access.Any(e => e.AccessId == id))
context.Access.Add(
new Access { AccessId = id, Name = val }
);
}
context.SaveChanges();
So basically, just checking if it exists and adding if not
3. In order for the above to work, you need to be able to insert Primary Key Values. Luckily for me this table will always have the same static data so I could deactivate the auto increment. To do that, the code looks like:
public class Access
{
public enum Level
{
None = 10,
Read = 20,
ReadWrite = 30
}
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int AccessId { get; set; }
public string Name { get; set; }
}

Categories

Resources