Hace ya un tiempo, el buen amigo @iceoverflow ,hizo un pequeño pero muy interesante pull request que nos habilita la posibilidad de crear y enchufar nuevas operaciones de migración que no tengamos por defecto en Entity Framework. Aunque el proceso es un poco mecánico, abre un montón de posibilidades para hacer nuevas contribuciones, incluso aunque no sea directamente en el código de EF y si en alguna contribución ( tengo una pequeña sorpresa con esto pero ya os la contaré cuando esté más avanzada ). A continuación me gustaría enseñaros un pequeño ejemplo de lo que podemos hacer y, por supuesto, todo aquello que os gustaría tener me encantaría escucharlo e incluso hacerlo…
Bien, empezamos, trataremos de hacer una operación de migración para crear una vista, lo primero por lo tanto es definir la operación, para ello, solamente tenemos que heredar de MigrationOperation, como por ejemplo vemos a continuación:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
<span class="kwrd">namespace</span> System.Data.Entity.Migrations.Model { <span class="kwrd">using</span> System.Data.Entity.Utilities; <span class="rem">/// <summary></span> <span class="rem">/// Represent a create view in database</span> <span class="rem">/// </summary></span> <span class="kwrd">public</span> <span class="kwrd">class</span> CreateViewOperation :MigrationOperation { <span class="rem">/// <summary></span> <span class="rem">/// The view name</span> <span class="rem">/// </summary></span> <span class="kwrd">public</span> <span class="kwrd">string</span> ViewName { get; <span class="kwrd">private</span> set; } <span class="rem">/// <summary></span> <span class="rem">/// The view body sql</span> <span class="rem">/// </summary></span> <span class="kwrd">public</span> <span class="kwrd">string</span> BodySql { get; <span class="kwrd">private</span> set; } <span class="rem">/// <summary></span> <span class="rem">/// Create a new instance</span> <span class="rem">/// </summary></span> <span class="rem">/// <param name="viewName">the view name to create</param></span> <span class="rem">/// <param name="bodySql">the view body sql </param></span> <span class="kwrd">public</span> CreateViewOperation(<span class="kwrd">string</span> viewName, <span class="kwrd">string</span> bodySql) :<span class="kwrd">base</span>(<span class="kwrd">null</span>) { Check.NotEmpty(viewName,<span class="str">"viewName"</span>); Check.NotEmpty(bodySql,<span class="str">"bodySql"</span>); <span class="kwrd">this</span>.ViewName = viewName; <span class="kwrd">this</span>.BodySql = bodySql; } <span class="rem">/// <inheritdoc/></span> <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">bool</span> IsDestructiveChange { get { <span class="kwrd">return</span> <span class="kwrd">false</span>; } } <span class="rem">/// <inheritdoc/></span> <span class="kwrd">public</span> <span class="kwrd">override</span> MigrationOperation Inverse { get { <span class="kwrd">return</span> <span class="kwrd">new</span> DropViewOperation(ViewName); } } } } |
Bien, ahora que ya tenemos definida nuestra operación, ya la podemos incluir dentro de nuestras migraciones, para que quede de una forma consistente con las operaciones actuales, podríamos crear un método extensor de de DbMigration como el siguiente:
1 2 3 4 5 6 7 8 9 10 11 |
<span class="rem">/// <summary></span> <span class="rem">/// Create a new view</span> <span class="rem">/// </summary></span> <span class="rem">/// <param name="migration">The DBMigration</param></span> <span class="rem">/// <param name="viewName">The name of view to create</param></span> <span class="rem">/// <param name="bodySql">The sql body of view to create</param></span> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">void</span> CreateView(<span class="kwrd">this</span> DbMigration migration, <span class="kwrd">string</span> viewName, <span class="kwrd">string</span> bodySql) { ((IDbMigration)migration) .AddOperation(<span class="kwrd">new</span> CreateViewOperation(viewName, bodySql)); } |
Una vez hecho este trabajo, cuando una operación se ejecute, esta operación se enviará al generador sql que tengamos configurado,recuerde que puede establecerlo ahora con nuestro punto central de configuración DbConfiguration. Por lo tanto, este generador debe de estar preparado para procesar estas operaciones, por lo tanto, lo normal será partir de un generador dado como por ejemplo SqlServerMigrationSqlGenerator para extenderlo con esta nueva operación. Para nuestro ejemplo podríamos hacer algo como lo siguiente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<span class="rem">/// <summary></span> <span class="rem">/// Extented Sql Server Migrations Sql Generator with more</span> <span class="rem">/// operations.</span> <span class="rem">/// <remarks></span> <span class="rem">/// For set this migrations sql generator you need register it in</span> <span class="rem">/// your DbMigrationsConfiguration{TContext} using the method </span> <span class="rem">/// SetSqlGenerator</span> <span class="rem">/// <example></span> <span class="rem">/// SetSqlGenerator("System.Data.SqlClient", new AdvancedSqlServerMigrationSqlGenerator());</span> <span class="rem">/// </example></span> <span class="rem">/// </remarks></span> <span class="rem">/// </summary></span> <span class="kwrd">public</span> <span class="kwrd">class</span> ExtendedSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator { <span class="rem">/// <inheritdoc/></span> <span class="kwrd">protected</span> <span class="kwrd">override</span> <span class="kwrd">void</span> Generate(MigrationOperation migrationOperation) { } } |
Ahora, en nuestro Generate deberíamos ver como procesar cada una de las posibles operaciones personalizadas que podamos querer incluir, para facilitar la tarea y no ver demasiada conversión o switch un truquito rápido sería este:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
/// <inheritdoc/> <span class="kwrd">protected</span> override void Generate(MigrationOperation migrationOperation) { this.Generate((dynamic)migrationOperation); } /// <summary> /// Generate a SQL <span class="kwrd">to</span> create a <span class="kwrd">new</span> view <span class="kwrd">in</span> database /// </summary> /// <param name=<span class="str">"createViewOperation"</span>>The operation <span class="kwrd">to</span> produce sql <span class="kwrd">for</span>.</param> <span class="kwrd">public</span> virtual void Generate(CreateViewOperation createViewOperation) { using (var writer = Writer()) { writer.Write(<span class="str">"IF object_id(N'{0}', 'V') IS NOT NULL "</span>,createViewOperation.ViewName); writer.WriteLine(<span class="str">"DROP VIEW {0} "</span>,createViewOperation.ViewName); writer.WriteLine(<span class="str">"GO "</span>); writer.WriteLine(<span class="str">"CREATE VIEW {0} AS {1}"</span>,createViewOperation.ViewName,createViewOperation.BodySql); this.Statement(writer); } } |
Al ponerlo como dynamic podemos crear un método para cada una de las operaciones personalizadas y por lo tanto simplificar tanto la lectura como el mantenimiento del código.
Espero que os haya gustado esta posibilidad, por supuesto, para los que os habéis fijado en el código esto forma parte de un proyecto que ya contiene muchas más posibles operaciones de migración que pronto compartiré con todos vosotros, de ahí que os preguntará que os gustaría tener!!, animaros…
Saludos
unai
Muy interesante la verdad!
Gracias
Tal y como hicimos en la entrega anterior vamos a ir poniendo ejemplos completos de algunos de los puntos