实体框架中IMigrationMetadata接口的目的和语义 [英] Purpose and semantic of IMigrationMetadata interface in Entity Framework

查看:83
本文介绍了实体框架中IMigrationMetadata接口的目的和语义的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图找出EF中System.Data.Entity.Migrations.Infrastructure.IMigrationMetadata接口的语义是什么。我知道它用于管理和应用数据库迁移。但是我找不到有关它的详细信息。具体我想知道:


  1. 什么Source属性用于?为什么在使用工具生成迁移时总是为空?

  2. 使用什么Target属性?我看到这些工具正在生成一些基于Base64的东西,放在资源中。它是什么?为什么它以非友好格式生成?

  3. 是否有可能手动开发迁移而无需工具使用?我认为这是不容易的,因为Target属性的Base64类似的值应该以某种方式生成。我是对吗?

  4. 当这个界面实际使用时?目前,我发现迁移者无法自动找到不实现此接口的迁移。我对吗?这是界面的唯一目的吗?


解决方案

IMigrationMetadata Interface 具有以下我所知道的职责。


  1. 通过ID属性识别迁移,以便可以识别并通过诸如 Update-Database

  2. 在通过Target属性应用迁移后,提供模型的快照。这是用来确定应该包含在新的迁移中的更改。

我猜测Source属性通常没有被实现由于在实施 Add-Migration 中不需要该工具。该代码可能只是将模型与最新现有迁移结束时的代码进行比较,并使用代码生成的模型来确定需要在新迁移中包含的更改。



Target属性返回一个已经使用GZipStream压缩并使用Convert.ToBase64String编码的EDMX格式的模型。我写了以下代码来对这些值进行解码和编码。如果您要手动编码迁移,您可能会发现这有用。

  using System; 
使用System.IO;
使用System.IO.Compression;
使用System.Text;

命名空间ConsoleApplication6
{
类程序
{
static void Main()
{
var minimalModel = File.ReadAllText ( Model1.edmx);

var encodedMinimalModel = Encode(minimalModel);

var decodedMinimalModel = Decode(encodedMinimalModel);
}

private static string解码(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))
{
使用(var gzipStream = new GZipStream(memorySteam,CompressionMode.Decompress))
{
返回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();
}
}
}
}

压缩可能解释了您的查询,为什么选择了非人类可读的格式。对于每次迁移,此内容至少重复一次(在Target属性中),并且可以根据模型的大小而较大。压缩节省空间。



就此而言,就我所见,真的只是返回模型的真实表示所需的最后一次迁移之后应用。 Add-Migration 仅使用该迁移来计算新迁移中所需的更改。如果您正在处理非常大的模型和/或大量迁移,那么删除该内容可能是有利的。这篇文章的其余部分涵盖了我对Target属性的最小值的推导,可以在除最近的迁移之外的所有内容中使用。



Target属性必须返回一个字符串对象 - 当调用update-database时,如果Target返回null,则在System.Data.Entity.Migrations.DbMigrator.ApplyMigration的调用中抛出ArgumentNullException。



此外,它必须是一个有效的XML文档。当我从目标我返回一个空字符串时,我得到一个XmlException,并显示消息Root元素丢失。



从这一点上,我使用了我上面的代码



< root /> 例如,所以我交换,通过在我的项目中添加一个新的ADO.Net实体数据模型,然后选择空模型选项,从我生成的一个空的EDMX文件中丢弃元素。这是结果。

 <?xml version =1.0encoding =utf-8?> 
< edmx:Edmx Version =3.0xmlns:edmx =http://schemas.microsoft.com/ado/2009/11/edmx>
< edmx:运行时>
< edmx:StorageModels>
< Schema xmlns =http://schemas.microsoft.com/ado/2009/11/edm/ssdlNamespace =Model1.StoreAlias =SelfProvider =System.Data.SqlClient ProviderManifestToken =2005>
< / Schema>
< / edmx:StorageModels>
< / edmx:Runtime>
< / edmx:Edmx>

当我使用上面的代码编码时,这是结果。

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

小心确保在源代码管理中保留每个迁移的真实目标值,以防您需要回滚到早期版本。您可以尝试将迁移应用于数据库,然后使用Visual Studio生成EDMX文件。另一个选择是回滚形成模型的类,然后执行 Add-Migration 。从新创建的迁移中获取目标值。


I'm trying to find out what is the semantic of System.Data.Entity.Migrations.Infrastructure.IMigrationMetadata interface in the EF. I know that it's used to manage and apply DB migrations. But I can't find detailed information about it. To be specific I would like to know:

  1. What Source property is used for? Why it's always null when I generate migrations using tools?
  2. What Target property is used for? I see that tools is generating something Base64-looking and placed into resources. What is it? Why it's generated in such non-friendly format?
  3. Is it possible to develop migration manually without tools usage? I suppose it is not easy because of that Target property Base64-like value which should be generated somehow. Am I right?
  4. When this interface is actually used? At the moment I found out that migrations not implementing this interface can't be found automatically by migrator. Am I right? Is it the only purpose of the interface?

解决方案

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

  1. Identify the migration via the ID property so that is can be recognized and included by commands such as Update-Database.
  2. Supply a snapshot of the model as it is after the migration is applied via the Target property. This is used to determine the changes that should be included in a new migration.

I am guessing that the Source property is often not implemented by the tooling as it is not required in the implementation of Add-Migration. That code probably just compares the model as it was at the end of the most recent, existing migration with a model generated from the code to determine the changes that need to be included in the new migration.

The Target property returns a model in EDMX format that has been both compressed using the GZipStream and encoded using Convert.ToBase64String. I wrote the following code to both decode and encode these values. You would probaly find this useful if you are going to be coding migrations manually.

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();
            }
        }
    }
}

The compression probably explains your query as to why a non-human readable format was chosen. This content is repeated at least once (in the Target property) for each migration and can be large depending on the size of the model. The compression saves on space.

On that note, as far as I can see, it is really only the last migration that is required to return a true representation of the model after it has been applied. Only that migration is used by Add-Migration to calculate the changes required in the new migration. If you are dealing with a very large model and/or a very large number of migrations, removing that content could be advantageous. The remainder of this post covers my derivation of a minimal value for the Target property which can be used in all but the most recent migration.

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

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

From this point on, I used my code from above to encode the values.

I did not get very far with gradually building up the model starting with <root /> for example so I swapped over to discarding elements from an empty EDMX file that I generated by adding a new 'ADO.Net Entity Data Model' to my project and then choosing the 'Empty Model' option. This 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 from above, this was the result.

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

Be careful to ensure that you retain the real Target values for each of your migrations in source control in case you need to roll back to an earlier version. You could try applying the migration to a database and then using Visual Studio to generate an EDMX file. Another alternative would be to roll back the classes that form your model and then execute Add-Migration. Take the Target value from the newly created migration.

这篇关于实体框架中IMigrationMetadata接口的目的和语义的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆