18

Apparently IMigrationMetadata.Target encodes the state of the EF model. Can I use this to reconstruct the model for a particular migration?

Tim Lovell-Smith
  • 15,310
  • 14
  • 76
  • 93
  • +1, we want to avoid running migrations automatically and instead run them when an admin invokes them, so we need to be able to reconstruct a model from whatever the current migration is. – Andrew Stanton-Nurse Mar 29 '13 at 19:14
  • 1
    Could you elaborate a bit? Like where and when would you like to reconstruct the model? What problem would you like to solve? – Gert Arnold Mar 29 '13 at 21:26

4 Answers4

31

Yes, it is possible. I was myself curious what exactly those magic resource strings were storing. By digging into the Entity Framework source (see the DbMigrator.GetLastModel() method), I found out that the IMigrationMetadata.Target just stores a base-64 string containing gzipped XML data. To test this, I created a new console application containing a simple code-first model defined as follows:

public class ContactContext : DbContext
{
    public virtual IDbSet<Contact> Contacts { get; set; }
}

public class Contact 
{
    public int Id {get; set;}
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

Then I created a migration using the NuGet Package Manager Console:

PM> Enable-Migrations
PM> Add-Migration MyMigration

Next I added the following code to my application's Main() method to decode the value in that string and dump it to the console:

var migration = new MyMigration();
var metadata = (IMigrationMetadata)migration;
var compressedBytes = Convert.FromBase64String(metadata.Target);
var memoryStream = new MemoryStream(compressedBytes);
var gzip = new GZipStream(memoryStream, CompressionMode.Decompress);
var reader = new StreamReader(gzip);
Console.WriteLine(reader.ReadToEnd());

This outputs an EDMX file representing the Entity Data Model associated with my DbContext that created the migration. If I write this output to a file with the .edmx extension, I'm able to open it with Visual Studio and view it in the Entity Designer.

Then if for some reason I wanted to regenerate the DbContext and entity classes that produced the model, I would need only do the following:

  1. Add the .edmx file to a Visual Studio project.
  2. Install the EF 5.x DbContext Generator for C# if I don't already have it.
  3. Add the related T4 templates by selecting Add -> New Item from project node context menu.
  4. Modify the newly added .tt files, replacing $edmxInputFile$ with the name of my .edmx file.
  5. Watch as the two templates magically regenerate my code-first types to their respective .cs files.

Hope that answers your question! :-D

luksan
  • 7,661
  • 3
  • 36
  • 38
  • 1
    Cool. Now i just need to think of an actual application for this :D – Tim Lovell-Smith Apr 02 '13 at 00:45
  • 1
    Great stuff @luksan, thanks! For anyone interested I've created a little Gist that can extract the EDMX from the target hash and compress it back again: https://gist.github.com/gligoran/87fe3e8eadf5db97ad03. I use this when I need to change a migration without disturbing the rest of the chain. I extract the EDMX from my change migration, edit the XML and compress it back to get the new target. I then have to do this for every migration that follows the changed one. – gligoran Jun 01 '15 at 16:27
5

I created a small console app to export EDMX from the Model column of the __MigrationHistory table https://github.com/andreydil/EfMigrationModelDecoder
You can choose specific migration using /migration parameter, i.e:

EfMigrationModelDecoder.Cli.exe "<connectionString here>" /migration:Init
Andrey
  • 196
  • 1
  • 8
4

I created a PowerShell script to extract the latest migration from a DB to a edmx-file.

https://gist.github.com/otto-gebb/93d021c8fd300646dba0073a77585a94

Gebb
  • 6,371
  • 3
  • 44
  • 56
1

You can also use SQL...

SELECT CONVERT(xml, DECOMPRESS(Model)) FROM [dbo].[__MigrationHistory] WHERE MigrationId = 'NameOfMigration'
Prisoner ZERO
  • 13,848
  • 21
  • 92
  • 137