Purpose and semantics of the IMigrationMetadata interface in the Entity Framework

I am trying to figure out what the semantics of the System.Data.Entity.Migrations.Infrastructure.IMigrationMetadata interface are in EF. I know what he used to manage and apply database migration. But I can not find detailed information about this. To be specific, I would like to know:

  • Which Source property is used? Why is it always null when creating a migration using tools?
  • Which Target property is used? I see that the tools generate something Base64-look and are put into resources. What is it? Why is it generated in such a dishonest format?
  • Is it possible to develop manual migration without using tools? I suppose this is not easy due to the fact that a similar Target Base Base64 property must be somehow generated. I'm right?
  • When is this interface really used? At the moment, I have found that migrations that do not implement this interface cannot be found automatically using migrator. I'm right? Is this the only purpose of the interface?
+7
source share
3 answers

The IMigrationMetadata interface has the following responsibilities that I know of.

  • Define the migration using the ID property so that it can be recognized and enabled using commands such as Update-Database .
  • Providing a snapshot of the model, as after migration, through the Target property. This is used to identify the changes that should be included in the new migration.

I assume that the Source property is often not implemented by tools, since this is not required when implementing Add-Migration . This code probably just compares the model, as it was at the end of the last, existing migration with the model created from the code, to determine the changes that need to be included in the new migration.

The Target property returns an EDMX model that has been compressed using GZipStream and encoded using Convert.ToBase64String. I wrote the following code to decode and encode these values. You will probably find this useful if you intend to manually transfer the hyphenation.

 using System; using System.IO; using System.IO.Compression; using System.Text; namespace ConsoleApplication6 { class Program { static void Main() { var minimalModel = File.ReadAllText("Model1.edmx"); var encodedMinimalModel = Encode(minimalModel); var decodedMinimalModel = Decode(encodedMinimalModel); } private static string Decode(string encodedText) { var compressedBytes = Convert.FromBase64String(encodedText); var decompressedBytes = Decompress(compressedBytes); return Encoding.UTF8.GetString(decompressedBytes); } private static string Encode(string plainText) { var bytes = Encoding.UTF8.GetBytes(plainText); var compressedBytes = Compress(bytes); return Convert.ToBase64String(compressedBytes); } public static byte[] Decompress(byte[] bytes) { using (var memorySteam = new MemoryStream(bytes)) { using (var gzipStream = new GZipStream(memorySteam, CompressionMode.Decompress)) { return ToByteArray(gzipStream); } } } private static byte[] ToByteArray(Stream stream) { using (var resultMemoryStream = new MemoryStream()) { stream.CopyTo(resultMemoryStream); return resultMemoryStream.ToArray(); } } public static byte[] Compress(byte[] bytes) { using (var memoryStream = new MemoryStream()) { using (var gzipStream = new GZipStream(memoryStream, CompressionMode.Compress)) { gzipStream.Write(bytes,0, bytes.Length); } return memoryStream.ToArray(); } } } } 

Compression probably explains your request as to why a format that was not readable by humans was chosen. This content is repeated at least once (in the Target property) for each migration and may be large depending on the size of the model. Compression is maintained in space.

In this article, as far as I can see, this is really only the last transition that is required to return the true representation of the model after its application. Only this migration is used by Add-Migration to calculate the changes needed for the new migration. If you are dealing with a very large model and / or with a large number of migrations, deleting this content can be beneficial. The rest of this post covers my output of the minimum value for the Target property, which can be used in all but the most recent migration.

The Target property should return a string object - an ArgumentNullException is raised when System.Convert.FromBase64String is called in System.Data.Entity.Migrations.DbMigrator.ApplyMigration when the update database is called if Target returns null.

In addition, it must be a valid XML document. When I returned the empty string from Target, I received an XmlException with the message "Root element is missing.".

From now on, I used my code above to encode values.

I didn’t start building the model very quickly, starting with <root /> , for example, so I switched to dropping elements from an empty EDMX file that I generated by adding a new “ADO.Net entity data model” to my project, and then select "The empty model." That was the result.

 <?xml version="1.0" encoding="utf-8"?> <edmx:Edmx Version="3.0" xmlns:edmx="http://schemas.microsoft.com/ado/2009/11/edmx"> <edmx:Runtime> <edmx:StorageModels> <Schema xmlns="http://schemas.microsoft.com/ado/2009/11/edm/ssdl" Namespace="Model1.Store" Alias="Self" Provider="System.Data.SqlClient" ProviderManifestToken="2005"> </Schema> </edmx:StorageModels> </edmx:Runtime> </edmx:Edmx> 

When I encoded this using my code above, this was the result.

 H4sIAAAAAAAEAJVQy07DMBC8I/EP1t6xExASRA1VVTgWIYK4W/amtfCjeN2q/D12HsqJAxdLOzOe2Z3V+uIsO2MkE3wLNa+AoVdBG79v4ZT6mwdYP11frVC7S/OSH/Y5i++KOH/31BS2hUNKx0YIUgd0krgzKgYKfeIqOCF1ELdV9SjqWhQ5ZFfGRt/3k0/G4YDMWJdClHvcBY2WJiZz3WA+xv4vURBpC+xVOqSjVNjC4F3zkoTANtbIbNmh7YG9xXA2GmOefyih488ySd5926016NMi2ElveqT0Eb4wd5Lz7mHZVozrzoeJPy6biKWGCSh95+kXfT3Qv6UBAAA= 

Use caution to maintain real target values ​​for each of your migrations in the original control if you need to revert to an earlier version. You can try applying the migration to the database, and then use Visual Studio to create the EDMX file. Another alternative would be to roll back the classes that form your model, and then do Add-Migration . Take the Target value from the newly created migration.

+12
source

I just looked at it because I wanted to use the Source property to ensure a strict migration order.

The answer to question 1 is hidden in DbMigrator.Scaffold

 var scaffoldedMigration = _configuration.CodeGenerator.Generate( migrationId, migrationOperations, (sourceModel == _emptyModel.Value) || (sourceModel == _currentModel) || !sourceMigrationId.IsAutomaticMigration() ? null : Convert.ToBase64String(modelCompressor.Compress(sourceModel)), Convert.ToBase64String(modelCompressor.Compress(_currentModel)), @namespace, migrationName); 

In other words, the Source property is only populated if the previous migration was “Auto Migration”. Just checked this, and the subsequent migration after the automatic migration gives something like this:

 [GeneratedCode("EntityFramework.Migrations", "6.2.0-61023")] public sealed partial class Fourth : IMigrationMetadata { private readonly ResourceManager Resources = new ResourceManager(typeof(Fourth)); string IMigrationMetadata.Id { get { return "201905250916038_Fourth"; } } string IMigrationMetadata.Source { get { return Resources.GetString("Source"); } } string IMigrationMetadata.Target { get { return Resources.GetString("Target"); } } } 
0
source

You go to: EF6 repositories on codeplex , and you see:

 public interface IMigrationMetadata { /// <summary> /// Gets the unique identifier for the migration. /// </summary> string Id { get; } /// <summary> /// Gets the state of the model before this migration is run. /// </summary> string Source { get; } /// <summary> /// Gets the state of the model after this migration is run. /// </summary> string Target { get; } } 

You can get the project and check the links to find out how this interface is used. The basic thing is your model. Again with the code, you will be able to track how this is done.

-2
source

All Articles