Puede crear vistas sql / procedimiento almacenado utilizando Entity Framework 4.1 Code first approach


Entity Framework 4.1 Code First funciona muy bien creando tablas y relaciones. ¿Es posible crear vistas sql o procedimientos almacenados utilizando el método Code first? Cualquier sugerencia con respecto a esto será muy apreciada. ¡Muchas gracias!

Author: Ryan The Leach, 2011-10-06

7 answers

El enfoque EF code-first espera que no haya lógica en la base de datos. Eso significa que no hay procedimientos almacenados ni vistas de base de datos. Debido a que el enfoque de código primero no proporciona ningún mecanismo para generar tales construcciones automáticamente para usted. ¿Cómo podría hacer eso si significa generar lógica?

Deben crearlos ustedes mismos en inicializador de base de datos personalizada mediante la ejecución manual de scripts de creación. No creo que estas construcciones SQL personalizadas puedan ser manejadas por SQL migración.

 9
Author: Ladislav Mrnka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:54

Admitimos procedimientos almacenados en nuestras primeras migraciones de Código de Entity Framework. Nuestro enfoque es crear una carpeta para contener la .archivos sql (~/Sql/ por ejemplo). Crear .archivos sql en la carpeta para crear y eliminar el procedimiento almacenado. Por ejemplo, Create_sp_DoSomething.sql y Drop_sp_DoSomething. Debido a que el SQL se ejecuta en un lote y CREATE PROCEDURE.. debe ser la primera instrucción en un lote, haga que CREATE PROCEDURE... sea la primera instrucción en el archivo. Además, no pongas GO después de DROP.... Agregue un archivo de recursos a su proyecto, si no lo hace tiene uno ya. Arrastre el .archivos sql desde el explorador de soluciones en la vista Archivos del diseñador de recursos. Ahora crea una migración vacía (Add-Migration SomethingMeaningful_sp_DoSomething) y usa:

namespace MyApplication.Migrations
{
    using System;
    using System.Data.Entity.Migrations;

    public partial class SomethingMeaningful_sp_DoSomething : DbMigration
    {
        public override void Up()
        {
            this.Sql(Properties.Resources.Create_sp_DoSomething);
        }

        public override void Down()
        {
            this.Sql(Properties.Resources.Drop_sp_DoSomething);
        }
    }
}

~/Sql / Create_sp_DoSomething.sql

CREATE PROCEDURE [dbo].[sp_DoSomething] AS
BEGIN TRANSACTION
-- Your stored procedure here
COMMIT TRANSACTION
GO

~/Sql / Drop_sp_DoSomething.sql

DROP PROCEDURE [dbo].[sp_DoSomething]
 72
Author: Carl G,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-02 07:17:49

A primera vista me gusta mucho el enfoque de Carl G, pero implica mucha interacción manual. En mi escenario, siempre dejo todos los procedimientos almacenados, vistas... y recrearlos siempre que haya un cambio en la base de datos. De esta manera estamos seguros de que todo está actualizado con la última versión.

La recreación se realiza configurando el siguiente inicializador:

Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, Configuration>());

Entonces nuestro método seed será llamado cada vez que haya una migración lista

protected override void Seed(DeploymentLoggingContext context)
    {
        // Delete all stored procs, views
        foreach (var file in Directory.GetFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Sql\\Seed"), "*.sql"))
        {
            context.Database.ExecuteSqlCommand(File.ReadAllText(file), new object[0]);
        }

        // Add Stored Procedures
        foreach (var file in Directory.GetFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Sql\\StoredProcs"), "*.sql"))
        {
            context.Database.ExecuteSqlCommand(File.ReadAllText(file), new object[0]);
        }
    }

Las sentencias SQL son almacenado en *.archivos sql para una fácil edición. Asegúrese de que sus archivos tienen " Build Action "establecido en" Content "y" Copy to Output Directory "establecido en"Copy Always". Buscamos las carpetas y ejecutamos todos los scripts dentro. No olvide excluir las sentencias " GO " en su SQL porque no se pueden ejecutar con ExecuteSqlCommand().

Mi disposición actual del directorio es la siguiente:

Proyecto.DAL
+ Migraciones
+ Sql
++ Seed
+ + + dbo.cleanDb.sql
++ StoredProcs
+ + + dbo.Sp_getsing algo.sql

Ahora solo tiene que soltar los procedimientos almacenados adicionales en la carpeta y todo se actualizará adecuadamente.

 22
Author: emp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-28 13:16:51

Parece estar bien documentada, sin embargo parece que ahora se puede hacer algún Procedimiento Almacenado utilizando la manipulación AlterStoredProcedure, CreateStoredProcedure, DropStoredProcedure, MoveStoredProcedure, RenameStoredProcedure en Entity Framework 6. Aún no los he probado, así que aún no puedo dar un ejemplo de cómo usarlos.

 10
Author: bbodenmiller,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-10 09:22:38

Para ampliar la respuesta de bbodenmiller, En Entity Framework 6, la clase DbMigration tiene métodos como AlterStoredProcedure que permiten la modificación de procedimientos almacenados sin tener que bajar hasta el SQL sin procesar.

Aquí hay un ejemplo de un método de migración Up() que altera un procedimiento almacenado existente de SQL Server llamado editItem que toma tres parámetros de tipo int, nvarchar(50), y smallmoney, respectivamente:

public partial class MyCustomMigration : DbMigration
{
    public override void Up()
    {
        this.AlterStoredProcedure("dbo.EditItem", c => new
        {
            ItemID = c.Int(),
            ItemName = c.String(maxLength:50),
            ItemCost = c.Decimal(precision: 10, scale: 4, storeType: "smallmoney")
        }, @" (Stored procedure body SQL goes here) "   
    }

    //...
}

En mi máquina, esto el script de migración produce el siguiente SQL:

ALTER PROCEDURE [dbo].[EditItem]
    @ItemID [int],
    @ItemName [nvarchar](50),
    @ItemCost [smallmoney]
AS
BEGIN
    (Stored procedure body SQL goes here)
END
 9
Author: Jon Schneider,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:59

El diseño de Emp funciona como un campeón! Estoy usando su patrón, pero también mapeo procedimientos almacenados dentro de mi clase DbContext que permite simplemente llamar a esos métodos de contexto en lugar de usar SqlQuery() y llamar a los procedimientos directamente desde mi repositorio. Como las cosas pueden ponerse un poco peludas cuando la aplicación crece, he creado una comprobación dentro de mi método Seed que se asegura de que el recuento de parámetros del procedimiento almacenado real coincida con el recuento de parámetros del método de asignación. También he actualizado el emp del bucle de caída mencionado. En lugar de tener que mantener una carpeta/archivo separado para las instrucciones drop, simplemente leo la primera línea de cada archivo sql y sustituyo CREATE por DROP (solo asegúrese de que la primera línea siempre sea CREATE PROCEDURE ProcName). De esta manera todos los procedimientos en mi carpeta StoredProcs se dejan caer y se vuelven a crear cada vez que se ejecuta Update-Database. La gota también está envuelta en un bloque try-catch en caso de que el procedimiento sea nuevo. Para que el recuento de parámetros del procedimiento funcione, deberá asegurarse de que envuelva un bloque BEGIN/END alrededor de su tsql ya que cada línea del archivo se lee para COMENZAR. También asegúrese de que cada parámetro sp esté en una nueva línea.

        // Drop Stored Procs
        foreach (var file in Directory.GetFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..\\DataContext\\SiteMigrations\\StoredProcs"), "*.sql"))
        {
            // Try to drop proc if its already created
            // Without this, for new procs, seed method fail on trying to delete
            try
            {
                StreamReader reader = new StreamReader(file);
                // Read first line of file to create drop command (turning CREATE [dbo].[TheProc] into DROP [dbo].[TheProc])
                string dropCommand = reader.ReadLine().Replace("CREATE", "DROP");

                context.Database.ExecuteSqlCommand(dropCommand, new object[0]);
            }
            catch { }

        }

        // Add Stored Procs
        foreach (var file in Directory.GetFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..\\DataContext\\SiteMigrations\\StoredProcs"), "*.sql"))
        {
            // File/Proc names must match method mapping names in DbContext
            int lastSlash = file.LastIndexOf('\\');
            string fileName = file.Substring(lastSlash + 1);
            string procName = fileName.Substring(0, fileName.LastIndexOf('.'));

            // First make sure proc mapping in DbContext contain matching parameters.  If not throw exception.
            // Get parameters for matching mapping
            MethodInfo mi = typeof(SiteContext).GetMethod(procName);

            if (mi == null)
            {
                throw new Exception(String.Format("Stored proc mapping for {0} missing in DBContext", procName));
            }

            ParameterInfo[] methodParams = mi.GetParameters();
            // Finished getting parameters

            // Get parameters from stored proc
            int spParamCount = 0;
            using (StreamReader reader = new StreamReader(file))
            {
                string line;                    
                while ((line = reader.ReadLine()) != null) 
                {
                    // If end of parameter section, break out
                    if (line.ToUpper() == "BEGIN")
                    {
                        break;
                    }
                    else
                    {
                        if (line.Contains("@"))
                        {
                            spParamCount++;
                        }
                    }                        
                }
            }
            // Finished get parameters from stored proc

            if (methodParams.Count() != spParamCount)
            {
                string err = String.Format("Stored proc mapping for {0} in DBContext exists but has {1} parameter(s)" +
                    " The stored procedure {0} has {2} parameter(s)", procName, methodParams.Count().ToString(), spParamCount.ToString());
                throw new Exception(err);
            }
            else
            {
                context.Database.ExecuteSqlCommand(File.ReadAllText(file), new object[0]);
            }
        }

Disfrute!

 2
Author: Natas0007,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-10-23 20:34:50

Como Ladislav señaló, DbContext en general tiende a minimizar la lógica en la base de datos, pero es posible ejecutar SQL personalizado usando context.Database.ExecuteSqlCommand() o context.Database.SqlQuery().

 1
Author: Mark Stafford - MSFT,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-16 14:55:53