it:ad:code_first:howto:seed:home

IT:AD:EF/CodeFirst:HowTo:Configure Seed

Summary

Once you've enabled Configure Migrations, you'll quickly come up to the issue of Seeding.

There are a couple of issues to get around first to make it easier to know what to do in various circumstances.

  • Seeding verus Data Updates
  • Where to put Data Updates (either Seeding, or Migrations)
  • When to use handy AddOrUpdate extension, or custom tests.

There are different forms of seeding that need to be considered:

  • Initialize/Reset Immutable Reference Data (Gender, etc. is never going to change).
  • Initialize/Reset Mutable Reference Data (Categories need to be initially setup, and only reset under rare circumstances)
  • Initialize/Reset Mutable User Data (one or two Demo Students/Classes, etc. will be needed to demonstrate the app is working, but then any changes made by users should not be erased/reseeded).

The easiest way to handle that is something along the lines of:

var seedingType = _hostSettings.Get<SeedingType>("SeedingType");
if (seedingType >= SeedingType.ResetMutableReferenceData){
  dbContext.Categories.AddOrUpdate(x=>x.Name,new Category{Name="Default"});
}

In 99% of the cases, the seeding level should always be Seeding.ResetImmutableReferenceData.

But note that at that level that ensures you always have the necessary Reference Data – but doesn't take into account any new Mutable Records needed (AppSettings stored in the db would be a good example) or Mutable Records that need changing.

That's where Patches/Updates are needed.

Another form of data change to consider is Patches/Updates that change data.

Handling data updates requires more care.

A good example might be the updating of an AppSetting value (eg: FaxServerServiceEndpoint).

The reason this is more complex is:

  • SeedingLevel.ResetMutableReferenceData would do this…but would always reset any changes made by SysAdmin. Annoying, therefore not appropriate.
  • The value might be different per environment (I usually know which environment I'm installing in (DEV,CI,ST,SIT, UAT,COMP,PROD) with _hostSettings.Get<string>("EnvironmentIdentifier");

So there are two places to consider putting it:

  • in Migrations
  • in Seeding

Each has Advantages and Disadvantages

In a Migration

* Advantages:

  • Makes sense: it's a Migration of Data, not Schema.
  • It's linear – ie, if not applied, apply, if already applied, move on and lets never discuss it again.

* Considerations:

  • Seeding happens after migrations, so if SeedingLevel is changed from SeedingLevel.ResetImmutableReferenceData to SeedingLevel.ResetMutableReferenceData:
    • Seeding data has to align perfectly with what is in the migrations (ie, be Environment aware as well) or it will crush out the updates done by the Migration.
    • Migrations has to check EnvironmentIdentifier as well – and be kept in sync with Seeding.

* Disadvantages:

  • Far away from the original Seeding…have to keep an eye on both to ensure things line up in terms of data and order…
  • Won't work in the context of a library (Migrations is an App Concept, not a Lib capable technology), so in XActLib, I'm forced to use the Seeding concept described further down.

//Do migration:
   if ([]{"UAT","PP"...}.Contains (environmentIdentifier)){
     //do an update specific to a specific environment...
   }
   //or don't check environmentIdentifier, and do an update specific to all environments...

In Seeding

* Advantages:

  • Works for XActLib, whereas Migrations wouldn't.

* Considerations:

  • You can arrange it so that edge case updates are applied after default case Seeding.
  • It's in a well known place, with the rest of the Seeding for that type.
    • Then again the Migrations folder is also a WellKnown place.

* Disadvantages:

  • Needs an XAct_DataStoreUpdateLog table to keep track of what has been applied.


if (!dbLog.Contains(x=>x.Name=="FooUpdate") && (!dbLog.Contains(x.Name=="FooUpdate")&&(x.Enabled==false)){
   if ([]{"UAT","PP"...}.Contains (environmentIdentifier)){
     //do an update specific to a specific environment...
   }
   //or don't check environmentIdentifier, and do an update specific to all environments...
}

The easiest way to do seeding is to use the AddOrUpdate method:

studentCategories.AddOrUpdate(x=>x.Name, new StudentCategory("Asleep");

never test on the Id (x⇒x.Id), but a variable within the entity. If you test against the Id, and it's autoincremented, it doesn't ever work.

The downside is…AddOrUpdate is slow. Seeding used to happen only after applying a migration, but now it happens when ever you restart the app1). And by the time you've created a dozen or so reference tables…it's not fast.

In which case, consider testing whether you need to update things or not:

if (studentCategories.Count() <10){
  ...
  ...
  ...
}

Or you can also do

if (xactDataStoreUpdateLog.Contains(x=>x.Name=="BaseStudentCategories")){
  studentCategories.AddOrUpdate(x=>x.Name,....);
}
and if you later add more:

if (xactDataStoreUpdateLog.Contains(x=>x.Name=="MoreStudentCategories")){
  studentCategories.AddOrUpdate(x=>x.Name,....);
}

Instead of x queries, it's only x the first time, and then 1 the next time.

That said, you really have to stay on top of it to ensure you don't muck up and drop updates. Make it clear that nobody can update previously created sections – only create new sections 'MoreStudentCategoriesA…B…C…'…or use Migrations (see above).

  • /home/skysigal/public_html/data/pages/it/ad/code_first/howto/seed/home.txt
  • Last modified: 2023/11/04 02:19
  • by 127.0.0.1