web-dev-qa-db-fra.com

Pouvez-vous créer des vues SQL / procédure stockée en utilisant la première approche du code Entity Framework 4.1

Entity Framework 4.1 Code First fonctionne très bien pour créer des tables et des relations. Est-il possible de créer des vues sql ou des procédures stockées en utilisant la première approche du code? Tout pointeur à ce sujet sera très apprécié. Merci beaucoup!

44
user629161

L'approche EF code-first s'attend à ce qu'il n'y ait pas de logique dans la base de données. Cela signifie aucune procédure stockée et aucune vue de base de données. En raison de cette approche basée sur le code ne fournit aucun mécanisme pour générer automatiquement de telles constructions pour vous. Comment pourrait-il faire cela si cela signifie générer de la logique?

Vous devez les créer vous-même dans initialiseur de base de données personnalisé en exécutant manuellement des scripts de création. Je ne pense pas que ces constructions SQL personnalisées puissent être gérées par des migrations SQL.

10
Ladislav Mrnka

Nous prenons en charge les procédures stockées dans nos premières migrations de Entity Framework Code. Notre approche consiste à créer un dossier pour contenir les fichiers .sql (~/Sql/par exemple). Créez des fichiers .sql dans le dossier pour créer et supprimer la procédure stockée. Par exemple. Create_sp_DoSomething.sql et Drop_sp_DoSomething. Parce que le SQL s'exécute dans un lot et CREATE PROCEDURE.. doit être la première instruction d'un lot, faites le CREATE PROCEDURE... la première instruction du fichier. Aussi, ne mettez pas GO après le DROP.... Ajoutez un fichier de ressources à votre projet, si vous n'en avez pas déjà un. Faites glisser les fichiers .sql de l'Explorateur de solutions dans la vue Fichiers du concepteur de ressources. Créez maintenant une migration vide (Add-Migration SomethingMeaningful_sp_DoSomething) et utilise:

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]
84
Carl G

À première vue, j'aime beaucoup l'approche de Carl G mais cela implique beaucoup d'interactions manuelles. Dans mon scénario, je supprime toujours toutes les procédures stockées, vues ... et les recrée chaque fois qu'il y a un changement dans la base de données. De cette façon, nous sommes sûrs que tout est à jour avec la dernière version.

La récréation se produit en définissant l'initialiseur suivant:

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

Ensuite, notre méthode de départ sera appelée chaque fois qu'une migration est prête

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

Les instructions SQL sont stockées dans des fichiers * .sql pour une édition facile. Assurez-vous que "Créer une action" est défini sur "Contenu" pour vos fichiers et "Copier dans le répertoire de sortie" sur "Copier toujours". Nous recherchons les dossiers et exécutons tous les scripts à l'intérieur. N'oubliez pas d'exclure les instructions "GO" dans votre SQL car elles ne peuvent pas être exécutées avec ExecuteSqlCommand ().

La disposition actuelle de mon répertoire est la suivante:

Project.DAL
+ Migrations
+ Sql
++ Graine
+++ dbo.cleanDb.sql
++ StoredProcs
+++ dbo.sp_GetSomething.sql

Maintenant, il vous suffit de déposer des procédures stockées supplémentaires dans le dossier et tout sera mis à jour de manière appropriée.

25
emp

Pour développer réponse de bbodenmiller , dans Entity Framework 6, la classe DbMigration a des méthodes telles que AlterStoredProcedure qui permettent la modification des procédures stockées sans avoir à descendre complètement vers le brut SQL.

Voici un exemple d'une méthode de migration Up() qui modifie une procédure stockée SQL Server existante nommée EditItem qui prend trois paramètres de type int, nvarchar(50) et smallmoney, respectivement:

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) "   
    }

    //...
}

Sur ma machine, ce script de migration produit le SQL suivant:

ALTER PROCEDURE [dbo].[EditItem]
    @ItemID [int],
    @ItemName [nvarchar](50),
    @ItemCost [smallmoney]
AS
BEGIN
    (Stored procedure body SQL goes here)
END
12
Jon Schneider

Il semble être mal documenté, mais il semble que vous puissiez maintenant effectuer certaines manipulations de procédures stockées en utilisant AlterStoredProcedure , CreateStoredProcedure , DropStoredProcedure , MoveStoredProcedure , RenameStoredProcedure dans Entity Framework 6. Je ne les ai pas encore essayés donc je ne peux pas encore donner un exemple de la façon de les utiliser.

11
bbodenmiller

le design d'Emp fonctionne comme un champion! J'utilise son modèle mais je mappe également des procédures stockées à l'intérieur de ma classe DbContext qui permet simplement d'appeler ces méthodes de contexte au lieu d'utiliser SqlQuery () et d'appeler les procédures directement à partir de mon référentiel. Comme les choses peuvent devenir un peu velues lorsque l'application grandit, j'ai créé une vérification dans ma méthode Seed qui s'assure que le nombre réel de paramètres de procédure stockée correspond au nombre de paramètres sur la méthode de mappage . J'ai également mis à jour l'emp. De boucle DROP mentionné. Au lieu d'avoir à maintenir un dossier/fichier séparé pour les instructions drop, je lis simplement la première ligne de chaque fichier sql et je remplace CREATE par DROP (assurez-vous simplement que la première ligne est toujours juste CREATE PROCEDURE ProcName). De cette façon, toutes les procédures de mon dossier StoredProcs sont supprimées et recréées à chaque exécution de Update-Database. La goutte est également enveloppée dans un bloc try-catch au cas où la procédure est nouvelle. Pour que le nombre de paramètres de procédure fonctionne, vous devez vous assurer d'envelopper un BEGIN/END bloquez autour de votre tsql puisque chaque ligne du fichier est lue jusqu'à BEGIN. Assurez-vous également que chaque paramètre sp est sur une nouvelle ligne.

        // 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]);
            }
        }

Prendre plaisir!

2
Natas0007

Comme l'a souligné Ladislav, DbContext en général a tendance à minimiser la logique dans la base de données, mais il est possible d'exécuter du SQL personnalisé en utilisant context.Database.ExecuteSqlCommand() ou context.Database.SqlQuery().

1