enable-migrations add-migration Initial -IgnoreChanges update-database
سؤال: چگونه میتوان همین عملیات را با کدنویسی انجام داد؟
متد UpdateDatabase کلاس ذیل، دقیقا معادل است با اجرای سه دستور فوق :
using System.Data.Entity.Migrations; using System.Data.Entity.Migrations.Design; namespace EFTests { /// <summary> /// Using Entity Framework Code First with an existing database. /// </summary> public static class CreateMigrationHistory { /// <summary> /// Creates a new '__MigrationHistory' table. /// Enables migrations using Entity Framework Code First on an existing database. /// </summary> public static void UpdateDatabase(DbMigrationsConfiguration configuration) { var scaffolder = new MigrationScaffolder(configuration); // Creates an empty migration, so that the future migrations will start from the current state of your database. var scaffoldedMigration = scaffolder.Scaffold("IgnoreChanges", ignoreChanges: true); // enable-migrations // add-migration Initial -IgnoreChanges configuration.MigrationsAssembly = new MigrationCompiler(ProgrammingLanguage.CSharp.ToString()) .Compile(configuration.MigrationsNamespace, scaffoldedMigration); // update-database var dbMigrator = new DbMigrator(configuration); dbMigrator.Update(); } } }
MigrationScaffolder کار تولید خودکار کلاسهای cs مهاجرتهای EF را انجام میدهد. زمانیکه به متد Scaffold آن پارامتر ignoreChanges: true ارسال شود، کلاس مهاجرتی را ایجاد میکند که خالی است (متدهای up و down آن خالی تشکیل میشوند). سپس این کلاسها کامپایل شده و در حین اجرای برنامه مورد استفاده قرار میگیرند.
برای استفاده از آن، نیاز به کلاس MigrationCompiler خواهید داشت. این کلاس در مجموعه آزمونهای واحد EF به عنوان یک کلاس کمکی وجود دارد: MigrationCompiler.cs
صرفا جهت تکمیل بحث و همچنین سهولت ارجاعات آتی، کدهای آن در ذیل نیز ذکر خواهد شد:
using System; using System.CodeDom.Compiler; using System.Collections.Generic; using System.ComponentModel; using System.Data.Common; using System.Data.Entity; using System.Data.Entity.Migrations; using System.Data.Entity.Migrations.Design; using System.Data.Entity.Spatial; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Resources; using System.Text; namespace EF_General.Models.Ex22 { public enum ProgrammingLanguage { CSharp, VB } public class MigrationCompiler { private readonly CodeDomProvider _codeProvider; public MigrationCompiler(string language) { _codeProvider = CodeDomProvider.CreateProvider(language); } public Assembly Compile(string @namespace, params ScaffoldedMigration[] scaffoldedMigrations) { var options = new CompilerParameters { GenerateExecutable = false, GenerateInMemory = true }; options.ReferencedAssemblies.Add(typeof(string).Assembly.Location); options.ReferencedAssemblies.Add(typeof(Expression).Assembly.Location); options.ReferencedAssemblies.Add(typeof(DbMigrator).Assembly.Location); options.ReferencedAssemblies.Add(typeof(DbContext).Assembly.Location); options.ReferencedAssemblies.Add(typeof(DbConnection).Assembly.Location); options.ReferencedAssemblies.Add(typeof(Component).Assembly.Location); options.ReferencedAssemblies.Add(typeof(MigrationCompiler).Assembly.Location); options.ReferencedAssemblies.Add(typeof(DbGeography).Assembly.Location); var embededResources = GenerateEmbeddedResources(scaffoldedMigrations, @namespace); foreach (var resource in embededResources) options.EmbeddedResources.Add(resource); var sources = scaffoldedMigrations.SelectMany(g => new[] { g.UserCode, g.DesignerCode }); var compilerResults = _codeProvider.CompileAssemblyFromSource(options, sources.ToArray()); foreach (var resource in embededResources) File.Delete(resource); if (compilerResults.Errors.Count > 0) { throw new InvalidOperationException(BuildCompileErrorMessage(compilerResults.Errors)); } return compilerResults.CompiledAssembly; } private static string BuildCompileErrorMessage(CompilerErrorCollection errors) { var stringBuilder = new StringBuilder(); foreach (CompilerError error in errors) { stringBuilder.AppendLine(error.ToString()); } return stringBuilder.ToString(); } private static IEnumerable<string> GenerateEmbeddedResources(IEnumerable<ScaffoldedMigration> scaffoldedMigrations, string @namespace) { foreach (var scaffoldedMigration in scaffoldedMigrations) { var className = GetClassName(scaffoldedMigration.MigrationId); var embededResource = Path.Combine( Path.GetTempPath(), @namespace + "." + className + ".resources"); using (var writer = new ResourceWriter(embededResource)) { foreach (var resource in scaffoldedMigration.Resources) writer.AddResource(resource.Key, resource.Value); } yield return embededResource; } } private static string GetClassName(string migrationId) { return migrationId .Split(new[] { '_' }, 2) .Last() .Replace(" ", string.Empty); } } }
استفاده از این کلاسها نیز بسیار ساده است. یکبار دستور ذیل را در ابتدای کار برنامه فراخوانی کنید تا جدول MigrationHistory دوباره ساخته شود:
CreateMigrationHistory.UpdateDatabase(new Configuration());
public class Configuration : DbMigrationsConfiguration<MyContext> { public Configuration() { AutomaticMigrationsEnabled = true; AutomaticMigrationDataLossAllowed = true; } }