EF4.3 基于代码的迁移演练
原文地址:http://blogs.msdn.com/b/adonet/archive/2012/02/09/ef-4-3-code-based-migrations-walkthrough.aspx
原文名称:EF 4.3 Code-Based Migrations Walkthrough
准备工作
在开始之前,我们需要一个项目,以及一个 Code First 的模型,对于这次演示,我们使用典型的博客 Blog 和回复 Post 模型。
1. 创建新的 MigrationsCodeDemo 控制台应用程序
2. 为项目添加最新版本的 EntityFramework NuGet 包。
找到包管理器控制台。
运行 Install-Package EntityFramework 命令。
成功之后的项目文件如下:
创建初始的模型和数据库
1. 增加如下的 Model.cs 文件,代码定义了一个单个的 Blog 类表示我们的模型对象,BlogContext 类就是 EF Code First 的上下文。
using System.Data.Entity;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity.Infrastructure;
namespace MigrationsCodeDemo
{
public class BlogContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
}
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
}
}
2. 现在我们已经有了一个模型,是时候进行一次数据访问了。使用如下的代码更新 Program.cs 文件。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MigrationsCodeDemo
{
class Program
{
static void Main(string[] args)
{
using (var db = new BlogContext())
{
db.Blogs.Add(new Blog { Name = "Another Blog " });
db.SaveChanges();
foreach (var blog in db.Blogs)
{
Console.WriteLine(blog.Name);
}
}
}
}
}
3. 运行程序,现在就可以在你本地的 SQLExpress 数据库中看到数据库了。
启用迁移
1. 在 Blog 类中增加一个 Url 属性。
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
}
2. 现在运行程序,会看到如下的异常。
未经处理的异常: System.InvalidOperationException: The model backing the 'BlogContext' context has changed since the database was created. Consider using Code First Migrations to update the database (http://go.microsoft.com/fwlink/?LinkId=238269).
3. 正如异常建议,现在是开始使用 Code First 迁移的时候了,第一步是为我们的上下文启用迁移支持。
在包管理器的控制台中执行命令:Enable-Migrations
4. 这个命令在项目中增加名为 Migrations 文件夹,文件夹中包含两个文件。
Configuration 类,这个类允许你配置你的上下文的迁移行为,在这个演示中,我们仅仅使用默认配置。
因为在这个项目中只有一个上下文,Enable-Migrations 自动填充应用的这个上下文。
201202171353373_InitialCreate.cs 中的 InitialCreate ,生成这个迁移是因为我们已经通过 Code First 创建了一个数据库。这个迁移中的代码表示我们已经创建的数据库,
namespace MigrationsCodeDemo.Migrations
{
using System.Data.Entity.Migrations;
public partial class InitialCreate : DbMigration
{
public override void Up()
{
CreateTable(
"Blogs",
c => new
{
BlogId = c.Int(nullable: false, identity: true),
Name = c.String(),
})
.PrimaryKey(t => t.BlogId);
}
public override void Down()
{
DropTable("Blogs");
}
}
}
第一个迁移
你会使用到两个命令
Add-Migration,将会基于你对模型的修改创建一个迁移的脚手架。
Update-Database,将没有提交的修改提交到数据库。
1. 我们首先为我们新增加的 Url 属性创建一个迁移,Add-Migration 会帮我们完成这个工作,我们为这个迁移命名为:AddBlogUrl。
执行:Add-Migration AddBlogUrl
项目中增加了一个文件 201202171413369_AddBlogUrl.cs
内容如下:
namespace MigrationsCodeDemo.Migrations
{
using System.Data.Entity.Migrations;
public partial class AddBlogUrl : DbMigration
{
public override void Up()
{
AddColumn("Blogs", "Url", c => c.String());
}
public override void Down()
{
DropColumn("Blogs", "Url");
}
}
}
3. 我们可以在这个迁移中编辑或者增加内容,但是现在看起来就很不错,让我们将这个迁移提交到数据库中。
执行: Update-Database
4. 现在的数据库中已经包含了 Url 列。
定制迁移
下面我们看看如何定制迁移。
1. 在代码中增加一个回复的类 Post,同时在 Blog 中增加一个评分属性。
using System.Data.Entity;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity.Infrastructure;
namespace MigrationsCodeDemo
{
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public int Rating { get; set; }
public List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
[MaxLength(200)]
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
}
2. 使用 Add-Migration 命令创建一个迁移,命名为 AddPostClass。
3. Code First Migrations 帮我们创建好了一个迁移,但是我们希望能增加如下的修改:
首先,为 Posts.Title 增加一个唯一索引。
增加一个非空的 Blogs.Rating 列,如果表中已经存在了数据,那么就将 CLR 默认的数据类型值赋予新增加的列 (Rating 是整数类型,应该就是 0), 但是,我们希望默认为 3, 这样已经存在的博客会有一个不错的评分。
namespace MigrationsCodeDemo.Migrations
{
using System.Data.Entity.Migrations;
public partial class AddPostClass : DbMigration
{
public override void Up()
{
CreateTable(
"Posts",
c => new
{
PostId = c.Int(nullable: false, identity: true),
Title = c.String(maxLength: 200),
Content = c.String(),
BlogId = c.Int(nullable: false),
})
.PrimaryKey(t => t.PostId)
.ForeignKey("Blogs", t => t.BlogId, cascadeDelete: true)
.Index(t => t.BlogId)
.Index(p => p.Title, unique: true);
AddColumn("Blogs", "Rating", c => c.Int(nullable: false, defaultValue: 3));
}
public override void Down()
{
DropIndex("Posts", new[] { "BlogId" });
DropForeignKey("Posts", "BlogId", "Blogs");
DropColumn("Blogs", "Rating");
DropTable("Posts");
}
}
}
4. 运行 Update-Database 提交到数据库,这次可以使用一个参数 -Verbose,以便可以看到生成的 SQL 脚本。
数据操作/定制 SQL
我们已经完成的迁移并没有修改或者移动任何数据,我们可以在迁移中的任何地点运行任意脚本。
1. 在模型上增加一个摘要 PostAbstrack 属性,然后,希望使用已经发布的内容来填充这个摘要属性。
public class Post
{
public int PostId { get; set; }
[MaxLength(200)]
public string Title { get; set; }
public string Content { get; set; }
public string Abstract { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
2. 创建一个迁移。
3. 修改生成的迁移文件,
namespace MigrationsCodeDemo.Migrations
{
using System.Data.Entity.Migrations;
public partial class AddPostAbstract : DbMigration
{
public override void Up()
{
AddColumn("Posts", "Abstract", c => c.String());
Sql("UPDATE Posts SET Abstract = LEFT(Content, 100) WHERE Abstract IS NULL");
}
public override void Down()
{
DropColumn("Posts", "Abstract");
}
}
}
4. 更新数据库之后,就会得到最新的数据了。使用 Verbose 参数可以看到提交的脚本。
迁移到特定版本(包含降级)
你可以升级或者降级到特定的版本。
假设我们希望数据库迁移到执行 AddBlogUrl 迁移之后的状态,通过 -TargetMigration 参数来降级。
如果你希望回到最初的空数据库,可以使用参数 TargetMigration:$InitialDatabase.
获取迁移的脚本
在运行 Update-Database 的时候,使用参数 -Script 。
-SourceMigration:$InitialDatabase 用来指定起始的版本, -TargetMigration:"AddPostAbstract" 用来指定目标版本。
我们希望取得从初始的数据库,直到迁移到 AddPostAbstract 版本使用的脚本。
执行之后,得到一个脚本文件。
CREATE TABLE [Blogs] (
[BlogId] [int] NOT NULL IDENTITY,
[Name] [nvarchar](max),
CONSTRAINT [PK_Blogs] PRIMARY KEY ([BlogId])
)
CREATE TABLE [__MigrationHistory] (
[MigrationId] [nvarchar](255) NOT NULL,
[CreatedOn] [datetime] NOT NULL,
[Model] [varbinary](max) NOT NULL,
[ProductVersion] [nvarchar](32) NOT NULL,
CONSTRAINT [PK___MigrationHistory] PRIMARY KEY ([MigrationId])
)
BEGIN TRY
EXEC sp_MS_marksystemobject '__MigrationHistory'
END TRY
BEGIN CATCH
END CATCH
INSERT INTO [__MigrationHistory] ([MigrationId], [CreatedOn], [Model], [ProductVersion]) VALUES ('201202171353373_InitialCreate', '2012-02-17T14:51:25.032Z', 0x1F8B0800000000000400ECBD07601C499625262F6DCA7B7F4AF54AD7E074A10880601324D8904010ECC188CDE692EC1D69472329AB2A81CA6556655D661640CCED9DBCF7DE7BEFBDF7DE7BEFBDF7BA3B9D4E27F7DFFF3F5C6664016CF6CE4ADAC99E2180AAC81F3F7E7C1F3F22FEC7BFF71F7CFC7BBC5B94E9655E3745B5FCECA3DDF1CE4769BE9C56B36279F1D947EBF67CFBE0A3DFE3E8374E1E9FCE16EFD29F34EDF6D08EDE5C369F7D346FDBD5A3BB779BE93C5F64CD78514CEBAAA9CEDBF1B45ADCCD66D5DDBD9D9D83BBBB3B777302F111C14AD3C7AFD6CBB658E4FC07FD79522DA7F9AA5D67E517D52C2F1BFD9CBE79CD50D317D9226F56D934FFECA32F8A8B3A6B098BE684DA3ECD17D547E971596484C9EBBC3C7F4FB4761E02AD8F6C87D4E529A1D65EBFB95EE5DCED671F3D29AB0BBF05B5F9BDF2EBE003FAE8655DADF2BABD7E959F7BEF9DCD3E4AEF86EFDEEDBE6C5FEDBC07143EFBE86CD9DEDBFB287DB12ECB6C52D207E759D9E41FA5AB4F1FBD6EAB3AFF3C5FE644907CF6326BDBBCA6C9399BE53C0425C5A3D5A7B7A3C6C3BB3B7BA0C6DD6CB9AC5AA6710FF90EAAF8D720FABAAD89693E4A9F15EFF2D9F37C79D1CE2DB25F64EFCC27F4EB47E957CB82788C5E6AEB75EE0F4EFEDEDCE95775F9B3DCE7E3BB8E09FAAC41DCDA660551DD9B2F7C96BF6B3B6C22ED5FE7ADD7B2F92875C08567C7CC61510C6C5F4E24EE8A4C18D9B93B203C8FBFC8562B228E274CFA49FA5A24E964FBF5FB4BCB4260DC9D3611A1B1D8DA9E8843B38BBCF32D754D983E2BEAA67D9AB5D924C3749DCC16BD66B7A0ACE9C9277057341DBD4D6BFC2E6FF4D5C9B82FEF0C45DF7D46035A9080F1D8746484474C4DE88BAFA75999D503427E5295EBC57293C2D80445E4CF87219FDC1E020B930F803FE8BFFFF86E67FC5D227B22A32D3B6AAF3B659BB8BDDBC4F66EB9BEC3DD8F95D36EB61F3DD693261FA54498CB6206B67B7DDDB4F9628C06E3D7BFA83C290B1AAF6BF045B62CCEF3A67D53BDCD610B4932BEBE09B24AB76966E5FF9BED500112DC68717A66EAF6F6637999D5D379566F2DB277773EC026BC079CF7D3F37DA575B3B61F54F6C2759F7D349B90FFF2461054F3F035ED405F021EDFF5FDACC74FF3A6B87020E0752DF329749F036ADA9C2DCF2B43621A918F9169D299812FF2369B11618EEBB638CFA62D7D3DCD9B86CDF34F66E59A9A9C2E26F9EC6CF9E5BA5DADDBE3A6C91793F2DA1FEFE3BB9BFB676317E2FCF8CB156BEF6F620884664143C8BF5C3E5917E5CCE2FDACCFCA4320C0232A1F8415B92704EEE2DA427A512D6F0948C9F7345FE54B48D79B7CB12A0958F3E5F27576990FE376330D438A3D7E5A646402173E05E513C5E475463D7B5D5007FE1BAE3FFA93D8959CFCA3FF270000FFFF2B65B379590C0000, '4.3.0')
ALTER TABLE [Blogs] ADD [Url] [nvarchar](max)
INSERT INTO [__MigrationHistory] ([MigrationId], [CreatedOn], [Model], [ProductVersion]) VALUES ('201202171413369_AddBlogUrl', '2012-02-17T14:51:25.079Z', 0x1F8B0800000000000400ECBD07601C499625262F6DCA7B7F4AF54AD7E074A10880601324D8904010ECC188CDE692EC1D69472329AB2A81CA6556655D661640CCED9DBCF7DE7BEFBDF7DE7BEFBDF7BA3B9D4E27F7DFFF3F5C6664016CF6CE4ADAC99E2180AAC81F3F7E7C1F3F22FEC7BFF71F7CFC7BBC5B94E9655E3745B5FCECA3DDF1CE4769BE9C56B36279F1D947EBF67CFBE0A3DFE3E8374E1E9FCE16EFD29F34EDF6D08EDE5C369F7D346FDBD5A3BB779BE93C5F64CD78514CEBAAA9CEDBF1B45ADCCD66D5DDBD9D9D83BBBB3B777302F111C14AD3C7AFD6CBB658E4FC07FD79522DA7F9AA5D67E517D52C2F1BFD9CBE79CD50D317D9226F56D934FFECA32F8A8B3A6B098BE684DA3ECD17D547E971596484C9EBBC3C7F4FB4761E02AD8F6C87D4E529A1D65EBFB95EE5DCED671F3D29AB0BBF05B5F9BDF2EBE003FAE8655DADF2BABD7E959F7BEF9DCD3E4AEF86EFDEEDBE6C5FEDBC07143EFBE86CD9DEDBFB287DB12ECB6C52D207E759D9E41FA5AB4F1FBD6EAB3AFF3C5FE644907CF6326BDBBCA6C9399BE53C0425C5A3D5A7B7A3C6C3BB3B7BA0C6DD6CB9AC5AA6710FF90EAAF8D720FABAAD89693E4A9F15EFF2D9F37C79D1CE2DB25F64EFCC27F4EB47E957CB82788C5E6AEB75EE0F4EFEDEDCE95775F9B3DCE7E3BB8E09FAAC41DCDA660551DD9B2F7C96BF6B3B6C22ED5FE7ADD7B2F92875C08567C7CC61510C6C5F4E24EE8A4C18D9B93B203C8FBFC8562B228E274CFA49FA5A24E964FBF5FB4BCB4260DC9D3611A1B1D8DA9E8843B38BBCF32D754D983E2BEAA67D9AB5D924C3749DCC16BD66B7A0ACE9C9277057341DBD4D6BFC2E6FF4D5C9B82FEF0C45DF7D46035A9080F1D8746484474C4DE88BAFA75999D503427E5295EBC57293C2D80445E4CF87219FDC1E020B930F803FE8BFFFF86E67FC5D227B22A32D3B6AAF3B659BB8BDDBC4F66EB9BEC3DD8F95D36EB61F3DD693261FA54498CB6206B67B7DDDB4F9628C06E3D7BFA83C290B1AAF6BF045B62CCEF3A67D53BDCD610B4932BEBE09B24AB76966E5FF9BED500112DC68717A66EAF6F6637999D5D379566F2DB277773EC026BC079CF7D3F37DA575B3B61F54F6C2759F7D349B90FFF2461054F3F035ED405F021EDFF5FDACC74FF3A6B87020E0752DF329749F036ADA9C2DCF2B43621A918F9169D299812FF2369B11618EEBB638CFA62D7D3DCD9B86CDF34F66E59A9A9C2E26F9EC6CF9E5BA5DADDBE3A6C91793F2DA1FEFE3BB9BFB676317E2FCF8CB156BEF6F620884664143C8BF5C3E5917E5CCE2FDACCFCA4320C0232A1F8415B92704EEE2DA427A512D6F0948C9F7345FE54B48D79B7CB12A0958F3E5F27576990FE376330D438A3D7E5A646402173E05E513C5E475463D7B5D5007FE1BAE3FFA93D8959CFCA3FF270000FFFF2B65B379590C0000, '4.3.0')
CREATE TABLE [Posts] (
[PostId] [int] NOT NULL IDENTITY,
[Title] [nvarchar](200),
[Content] [nvarchar](max),
[BlogId] [int] NOT NULL,
CONSTRAINT [PK_Posts] PRIMARY KEY ([PostId])
)
CREATE INDEX [IX_BlogId] ON [Posts]([BlogId])
CREATE UNIQUE INDEX [IX_Title] ON [Posts]([Title])
ALTER TABLE [Blogs] ADD [Rating] [int] NOT NULL DEFAULT 3
ALTER TABLE [Posts] ADD CONSTRAINT [FK_Posts_Blogs_BlogId] FOREIGN KEY ([BlogId]) REFERENCES [Blogs] ([BlogId]) ON DELETE CASCADE
INSERT INTO [__MigrationHistory] ([MigrationId], [CreatedOn], [Model], [ProductVersion]) VALUES ('201202171424494_AddPostClass', '2012-02-17T14:51:25.092Z', 0x1F8B0800000000000400ECBD07601C499625262F6DCA7B7F4AF54AD7E074A10880601324D8904010ECC188CDE692EC1D69472329AB2A81CA6556655D661640CCED9DBCF7DE7BEFBDF7DE7BEFBDF7BA3B9D4E27F7DFFF3F5C6664016CF6CE4ADAC99E2180AAC81F3F7E7C1F3F22FEC7BFF71F7CFC7BBC5B94E9655E3745B5FCECA3DDF1CE4769BE9C56B36279F1D947EBF67CFBE0A3DFE3E8374E1E9FCE16EFD29F34EDF6D08EDE5C369F7D346FDBD5A3BB779BE93C5F64CD78514CEBAAA9CEDBF1B45ADCCD66D5DDBD9D9D83BBBB3B777302F111C14AD3C7AFD6CBB658E4FC07FD79522DA7F9AA5D67E517D52C2F1BFD9CBE79CD50D317D9226F56D934FFECA32F8A8B3A6B098BE684DA3ECD17D547E971596484C9EBBC3C7F4FB4761E02AD8F6C87D4E529A1D65EBFB95EE5DCED671F3D29AB0BBF05B5F9BDF2EBE003FAE8655DADF2BABD7E959F7BEF9DCD3E4AEF86EFDEEDBE6C5FEDBC07143EFBE86CD9DEDBFB287DB12ECB6C52D207E759D9E41FA5AB4F1FBD6EAB3AFF3C5FE644907CF6326BDBBCA6C9399BE53C0425C5A3D5A7B7A3C6C3BB3B7BA0C6DD6CB9AC5AA6710FF90EAAF8D720FABAAD89693E4A9F15EFF2D9F37C79D1CE2DB25F64EFCC27F4EB47E957CB82788C5E6AEB75EE0F4EFEDEDCE95775F943EFF31591031D6D9E930E8C17D96571C174EC407B59356DF351FA2A2F8593E7C54AB8778C6F7E7F66B8F4595D2D5E55A536E70F7FFF37597D91B78446D5FDE675B5AEA71D141EDF75BCBC91C301E6EB7038DEFB3A1C6EDEFBFF0087BF29DAF27D599C7AFA40762385D8D210DFB3DF0F67F3DBA99EDBB2B9F0F1FB73B9E1E53E971BFEBF0D971F374D352DB86B8FEDA4DB7000A7CB593A88834C80C19AE8BD2EDB62551653EAF2B38FBED5A3460C98155B074C461F02DB0DF122605F2E9FE665DEE6E9F114E320C6C89A6936EBCF01D160167E42529AD71093AC246E6ADA3A2B966D5FA48BE5B45865E510CA9D17DEC3CE01290BBEFBCDD37C952F21C44374FFB07E2DF80E916EA2C9E3BB1ED3F43526A492DEC86B0F0196D4775DED29ED5FE7ADD79214BE63D4800B7A1C14BEACD6A2F7B2F063E7656F0021049537EFFB9E3076E9B8492E2C9216BFDE346C9204EF75A54D97A1C38144E4DDCE86FDEEF15D7117F583C77707FCCAC75F64AB152954CFCFD44FD2D7E2649E6CBF7E7F47722130EE4E9B883F69B1B53D9169CB2EF2CEB750FDB3FC595137EDD3ACCD2619F4EDC96CD16B760BDE333DF92CD89F23C354A6357E9737FA9EF638C6268E7ACF68400B08359BEDCE14F75F4BE1DE6765560F18A193AA5C2F969B657D188AB8A63E0CF9E4F610D8CFF401F007B77FDFF88C3E08F3591FCAE3BB1D2AF6B45A6FAE7A26209CF85BB18508EF07B245DF7F642837B145FCB521721AA7D12767DC01DD0445FD391F887E747B18D63BF3A1D80F6F0FE7FD18FD6795417A4AB5DBC4F66E956B47893E568516685A1E793782EF693869F2514AE4B92C66D06EAFAF9B365F8CD160FCFA17952765C1A4350DBEC896C579DEB46FAAB7F992FDED83AF9F04B04141D3CCCAFF3767020A90E0C688E8266F7D4304BFBCCCEAE93CABB716D9BB3BEF1B3A7851F907C109236D1EF3C621FD7F24C8FD5999BC20383554EF87A2EF37019DD8F3832633C2C05F6B32BF4E2C27FEEE0F33E40A1DDEAF113B7EAD986DC027BBAD16C248DE2B4EFBC0BE2CC80E35BFE1D8ACEF4677E7AA1FA10D066862A03EFB6836A164F31BE1DE78D8D2052BF3DE032B1FC7C04683A99FA5A8CE1FB3F3D36F0AE47AB1DFCF4EECD677278847D64BBB6C40BCD914170E04161196F934E00EDBE66C795E1926ED60649A7434D717799BCD88758EEBB638CFA62D7D3DCD9B864DD34F66E59A9A9C2E26F9EC6CF9E5BA5DAD5B1A72BE9894D73E31C0EC9BFAE70035C4F9F1972B76ADBF8921109A050D21FF72F9645D94338BF7B388DE1D000129527B85B96C61B72EAE2DA417D5F29680947C4F8DF0BFC9172B4A0BE6CD97CBD7D9653E8CDBCD340C29F6F86991517CB2F029289F2826AF33EAD9EB823AF0DF70FDD19FC4AEB46675F4FF040000FFFFFA5ABE06281B0000, '4.3.0')
ALTER TABLE [Posts] ADD [Abstract] [nvarchar](max)
UPDATE Posts SET Abstract = LEFT(Content, 100) WHERE Abstract IS NULL
INSERT INTO [__MigrationHistory] ([MigrationId], [CreatedOn], [Model], [ProductVersion]) VALUES ('201202171437449_AddPostAbstract', '2012-02-17T14:51:25.111Z', 0x1F8B0800000000000400ECBD07601C499625262F6DCA7B7F4AF54AD7E074A10880601324D8904010ECC188CDE692EC1D69472329AB2A81CA6556655D661640CCED9DBCF7DE7BEFBDF7DE7BEFBDF7BA3B9D4E27F7DFFF3F5C6664016CF6CE4ADAC99E2180AAC81F3F7E7C1F3F22FEC7BFF71F7CFC7BBC5B94E9655E3745B5FCECA3DDF1CE4769BE9C56B36279F1D947EBF67CFBE0A3DFE3E8374E1E9FCE16EFD29F34EDF6D08EDE5C369F7D346FDBD5A3BB779BE93C5F64CD78514CEBAAA9CEDBF1B45ADCCD66D5DDBD9D9D83BBBB3B777302F111C14AD3C7AFD6CBB658E4FC07FD79522DA7F9AA5D67E517D52C2F1BFD9CBE79CD50D317D9226F56D934FFECA32F8A8B3A6B098BE684DA3ECD17D547E971596484C9EBBC3C7F4FB4761E02AD8F6C87D4E529A1D65EBFB95EE5DCED671F3D29AB0BBF05B5F9BDF2EBE003FAE8655DADF2BABD7E959F7BEF9DCD3E4AEF86EFDEEDBE6C5FEDBC07143EFBE86CD9DEDBFB287DB12ECB6C52D207E759D9E41FA5AB4F1FBD6EAB3AFF3C5FE644907CF6326BDBBCA6C9399BE53C0425C5A3D5A7B7A3C6C3BB3B7BA0C6DD6CB9AC5AA6710FF90EAAF8D720FABAAD89693E4A9F15EFF2D9F37C79D1CE2DB25F64EFCC27F4EB47E957CB82788C5E6AEB75EE0F4EFEDEDCE95775F943EFF31591031D6D9E930E8C17D96571C174EC407B59356DF351FA2A2F8593E7C54AB8778C6F7E7F66B8F4595D2D5E55A536E70F7FFF37597D91B78446D5FDE675B5AEA71D141EDF75BCBC91C301E6EB7038DEFB3A1C6EDEFBFF0087BF29DAF27D599C7AFA40762385D8D210DFB3DF0F67F3E349D3D6D9F487DFF1ED74DE6DE54B04E8FDC5CB08515FBC8CE0DD46BC8E9BA69A16DCB5C7EFD26D3880D3E52C1DC44126C0604DF45E976DB12A8B2975F9D947DFEA512306CCEA0B074C461F02DB0DF122605F2E9FE665DEE6E9F114E3208ECC9A6936EBCF01D160167E42EA21AF219F59496C0C7E2A966D5F9714CB69B1CACA21943B2FBC87810552167CF79BA7F92A5F427B0CD1FDC3FAB5E03B44BA89268FEF7A4CD357D55007F4465E7B08B08A78D755DBD2FE75DE7A2DC9D238460DB8A0C741E1CB6AA67A2F0B3F765EF60610425079F3BEEF0963978E9BE4C22269F1EB4DC32649F05E57DA74193A1C4844DEED6CD8EF1EDF153F553F787C77C0A17DFC45B65A9142F51C5CFD247D2DDEEDC9F6EBF7F7601702E3EEB48938B2165BDB13D9D4EC22EF7C0B9B33CB9F1575D33ECDDA6C9241DF9ECC16BD66B7E03DD393CF82FD39324C655AE37779A3EFE28F636CE2A8F78C06B48050B3BFD099E2FE6B29E28AACCCEA0123745295EBC572B3AC0F43119FD887219FDC1E023BB83E00FEE0F6EF1B67D507613EEB43797CB743C59E56EBCD55CF0484137F2BB610E1FD40B6E83BAE0CE526B688BF36444EE3ADFAE48C7BBE9BA0A823E903D18F6E0FC3BA853E14FBE1EDE13837CF07E43EBD3DA4F713999F5556EBA9E76E13DBBB55D31D75FC585563A0B379E4DD24444F574A938F5222CF6531839E7C7DDDB4F9628C06E3D7BFA83C290B9E24D3E08B6C599CE74DFBA67A9B2F396438F8FA790C1BD734CDACFC7F7332A300096E0CEA6EF2FB3724219697593D9D67F5D6227B77E77D83102FB1F04170C264018F79E390FE3F12A7FFAC4C5E105F1BAAF7A3E9F79B804EF8FC4193D90D893F0858441ABE16677C9D1053DCF01F662418FAE15F23A4FD5AA1E480AB785B958691BC57F8F8817D59901D6A7EC32163DFBB3FEACC553F701C8C1BC5DA7DF6D16C42C9F737C2BDF168AA0B56E6BD07563E8E818DC6783F4BC1A63F66173EDC145FF642D29F9D90B2EF9B108FAC9776198578B3292E1C082CAA2CF369C01DB6CDD9F2BC324CDAC1C834E968AE2FF2369B912F735CB7C539A943FA7A9A370DDBB99FCCCA3535395D4CF2D9D9F2CB75BB5AB734E47C3129AF7D6280D937F5CF717388F3E32F57ECF17F134320340B1A42FEE5F2C9BA286716EF6711BD3B000252A4C60F73D9C2085E5C5B482FAAE52D0129F99E1AE17F932F5694ADCC9B2F97AFB3CB7C18B79B691852ECF1D322A3B069E153503E514C5E67D4B3D70575E0BFE1FAA33F895D690DEFE8FF090000FFFF18018B3A381C0000, '4.3.0')