I am working with the the Code First style of the Entity Framework for my first time. I want to set up some default data. The first approach I came across involved creating a custom initializer. I was headed this route but noticed after setting up migrations that it came with the Configuration.cs that already overrides the seed method just like the custom initializer.
internal sealed class Configuration : DbMigrationsConfiguration<Toolkit.Model.ToolkitContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(Toolkit.Model.ToolkitContext 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" }
// );
//
}
}
So it seems there are two ways to accomplish this task. Can someone shed some light on what would be the recommended way of doing this? Or does it matter at all and I should just flip a coin?
The Configuration.cs Seed method will run every time your model changes to make sure that some specific data stays in your DB, or to even possibly to reset that data to a specified default setting.
The Custom Initializer's seed method, on the other hand, can be setup to run every single time the application loads, like in this code, which is currently in the Global.asax file of my MVC page:
Database.SetInitializer(new MyCustomInitializer<MyDbContext, Configuration>());
var db = new MyDbContext();
db.Database.Initialize(true);
The practical difference really comes into play after you deploy your application. The Custom Initializer will make sure that no user can destroy some data that's absolutely required in your program.
Related
I am creating a football manager game. I have used identity 2.0 as it will work well for my registration and login. I was able to add the extra tables that were needed but now I need to seed the data such as teams and players to these table. Any idea how to do so? The extra tables were created in the identity models using migrations. Here is a picture of the tables I am using.
In the Migrations folder, there is a file called Configuration.cs with the Seed method that you can use to create some seed data.
protected override void Seed(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.
//add roles
context.Roles.AddOrUpdate(p => p.Id,
new IdentityRole()
{
Id = EnumUtility<AspNetRoles>.GetAppRoleId(AspNetRoles.None),
Name = AspNetRoles.None.ToString()
});
}
Just run update-database and you should have data in your tables.
There are 2 Seed() methods available - one in certain initializers such as CreateDatabaseIfNotExist that is run whenever the database is created. The other is the migration Seed() which runs whenever you apply a migration via update-database.
Since it runs with every migration, you want to make sure you don't duplicate your data. You could do this by checking for existence:
if (!context.Teams.Any())
{
context.Teams.Add(new Team { Name = "Team A" });
context.Teams.Add(new Team { Name = "Team B" });
}
But there is a better way designed specifically for migrations called AddOrUpdate:
protected override void Seed(ApplicationDbContext context)
{
context.Teams.AddOrUpdate(
team => team.Id, // put the key or unique field here
new Team
{
Id = 1,
Name = "Team 1"
},
new Team
{
Id = 2,
Name = "Team 2"
});
context.SaveChanges();
}
I'm refactoring a project and want to move all the EF entities and the code-first migrations to a new project. I renamed the ContextKey in the _Migrations table to the new namespace. When running an Add-Migration, no new changes are detected (Up() and Down() are empty).
But when I remove the localdb, the db isn't re-created (it did before the move). Apparently only migrations created after the move are run (but it shouldn't).
How can I make sure all migrations (also the ones before the move) are run when creating a new db?
--edit--
Never mind :(
I dragged and dropped the existing migrations to the new project and renamed the namespaces in the migration.cs files, but forgot the code behind migration.Designer.cs
You can update all the ContextKey column values in the dbo._MigrationHistory table to match the new namespace and that's all.
For me i was moving all the code first models from ASP.NET MVC app to external Class library to share with other projects.
Below steps may help
check the dbo._MigrationHistory and you can see all records have
similar values which match the exact class of Configuration class
MyApp.Migrations.Configuration
2.(test step) run Update-Database from Package Manager Console with new class library selected and you will see for example below error
There is already an object named 'AspNetRoles' in the database.
update all the records in the ContextKey column of _MigrationHistory table to match the new namespace
MyApp.Domain.Migrations.Configuration
The reference table [__MigrationHistory] contains a ContextKey column. Unless otherwise valued, it maintains the value of the membership of DbContext namespace.
You can set a class that derives from dbMigrationsConfiguration and set the ContextKey value in the constructor.
public sealed class Configuration : DbMigrationsConfiguration<Your.Context>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
ContextKey = "PreviousValue";
}
protected override void Seed(Your.Context 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" }
// );
//
}
}
What is the best way to update an entity framework model in the database with two instances of a DbContext. For example i have a simple User model which is loaded in one DbContext but saved in another:
User model = null;
using(SampleDbContext dbContext = new SampleDbContext())
{
model = dbContext.User.Find(0);
}
model.UserName = "Test-User";
using(SampleDbContext dbContext = new SampleDbContext())
{
// Here is the place i want to save the changes of the model
// without loading it again from the database and set each property
// ...
dbCotnext.SaveChanges();
}
In my case i want to write some UserManager, which has Create, Update and Delete methods. I think creating one DbContext instance for the howle manager
is no solution, because i only want to save the changes of a specific model.
I also don't want to load the models for updating again from the database and settings each value from the source instance, like:
// Update user
using(SampleDbContext dbContext = new SampleDbContext())
{
// I don't want this:
var model = dbContect.User.Find(0);
model.UserName = sourceModel.UserName;
// ...
dbCotnext.SaveChanges();
}
Maybe my problem with manager classes is very simple, but i could not find any good solution.
P.S.: My manager classes are often singleton classes.
Thank you.
You could in your second DbContext do something like this:
using (SampleDbContext dbContext = new SampleDbContext())
{
dbContext.Entry(model).State = EntityState.Modified;
dbContext.SaveChanges();
}
This would save the changes you have made to the entity to the database. However I can't remember if dbContext.Entry queries the DB for the entity.
I try create the tables with following code:
protected override void PostProcessMappings(Configuration config)
{
base.PostProcessMappings(config);
var enversConf = new NHibernate.Envers.Configuration.Fluent.FluentConfiguration();
config.Properties.Add("nhibernate.envers.audit_table_prefix", string.Empty); // default
config.Properties.Add("nhibernate.envers.audit_table_suffix", "_REV"); // default _AUD
config.Properties.Add("nhibernate.envers.revision_field_name", "REV"); // default
config.Properties.Add("nhibernate.envers.revision_type_field_name", "REVTYPE"); // default
config.IntegrateWithEnvers(enversConf);
new SchemaExport(config).Create(false, true);
}
But in the schema, i cant see the tables.
I try to implement the code using here (Test). Next, i debug and the nhibernate object: Cfg.NHibernate.Configuration config and in my project i cant catch the mappings, and in the test, i see the mapping that will be created.
I dont know what next i need to do.
I am building in a Change History / Audit Log to my MVC app which is using the Entity Framework.
So specifically in the edit method public ActionResult Edit(ViewModel vm), we find the object we are trying to update, and then use TryUpdateModel(object) to transpose the values from the form on to the object that we are trying to update.
I want to log a change when any field of that object changes. So basically what I need is a copy of the object before it is edited and then compare it after the TryUpdateModel(object) has done its work. i.e.
[HttpPost]
public ActionResult Edit(ViewModel vm)
{
//Need to take the copy here
var object = EntityFramework.Object.Single(x=>x.ID = vm.ID);
if (ModelState.IsValid)
{
//Form the un edited view model
var uneditedVM = BuildViewModel(vm.ID); //this line seems to confuse the EntityFramework (BuildViewModel() is used to build the model when originally displaying the form)
//Compare with old view model
WriteChanges(uneditedVM, vm);
...
TryUpdateModel(object);
}
...
}
But the problem is when the code retrieves the "unedited vm", this is causing some unexpected changes in the EntityFramework - so that TryUpdateModel(object); throws an UpdateException.
So the question is - in this situation - how do I create a copy of the object outside of EntityFramework to compare for change/audit history, so that it does not affect or change the
EntityFramework at all
edit: Do not want to use triggers. Need to log the username who did it.
edit1: Using EFv4, not too sure how to go about overriding SaveChanges() but it may be an option
This route seems to be going nowhere, for such a simple requirement! I finally got it to override properly, but now I get an exception with that code:
public partial class Entities
{
public override int SaveChanges(SaveOptions options)
{
DetectChanges();
var modifiedEntities = ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
foreach (var entry in modifiedEntities)
{
var modifiedProps = ObjectStateManager.GetObjectStateEntry(entry).GetModifiedProperties(); //This line throws exception The ObjectStateManager does not contain an ObjectStateEntry with a reference to an object of type 'System.Data.Objects.EntityEntry'.
var currentValues = ObjectStateManager.GetObjectStateEntry(entry).CurrentValues;
foreach (var propName in modifiedProps)
{
var newValue = currentValues[propName];
//log changes
}
}
//return base.SaveChanges();
return base.SaveChanges(options);
}
}
IF you are using EF 4 you can subscribe to the SavingChanges event.
Since Entities is a partial class you can add additional functionality in a separate file. So create a new file named Entities and there implement the partial method OnContextCreated to hook up the event
public partial class Entities
{
partial void OnContextCreated()
{
SavingChanges += OnSavingChanges;
}
void OnSavingChanges(object sender, EventArgs e)
{
var modifiedEntities = ObjectStateManager.GetObjectStateEntries(EntityState.Modified);
foreach (var entry in modifiedEntities)
{
var modifiedProps = ObjectStateManager.GetObjectStateEntry(entry.EntityKey).GetModifiedProperties();
var currentValues = ObjectStateManager.GetObjectStateEntry(entry.EntityKey).CurrentValues;
foreach (var propName in modifiedProps)
{
var newValue = currentValues[propName];
//log changes
}
}
}
}
If you are using EF 4.1 you can go through this article to extract changes
See FrameLog, an Entity Framework logging library that I wrote for this purpose. It is open-source, including for commercial use.
I know that you would rather just see a code snippet showing how to do this, but to properly handle all the cases for logging, including relationship changes and many-to-many changes, the code gets quite large. Hopefully the library will be a good fit for your needs, but if not you can freely adapt the code.
FrameLog can log changes to all scalar and navigation properties, and also allows you to specify a subset that you are interested in logging.
There is an article with high rating here at the codeproject: Implementing Audit Trail using Entity Framework . It seems to do what you want. I have started to use this solution in a project. I first wrote triggers in T-SQL in the database but it was too hard to maintain them with changes in the object model happening all the time.