Entity Framework Code First prend-il en charge les procédures stockées?


112

J'ai regardé plusieurs présentations d'EF Code First et je n'ai pas vu comment EFCF fonctionne avec les procédures stockées.

Comment puis-je déclarer une méthode qui utilisera des sp? Puis-je passer une entité à une méthode qui appelle sp sans mapper manuellement les propriétés d'entité aux paramètres sp?

Aussi, que se passe-t-il si je change de modèle? Est-ce que cela ferait tomber mon sp en recréant la table à partir du modèle? Et qu'en est-il des déclencheurs?

Si ces éléments ne sont pas pris en charge, existe-t-il des plans pour les soutenir à l'avenir?


5
La feuille de route EF indique qu'EF 6 prendra en charge les procédures stockées et les fonctions pour Code First. entityframework.codeplex.com/wikipage?title=Roadmap
frennky

Réponses:


66

EDIT: Ma réponse originale pour EF4.1 (ci-dessous) est maintenant obsolète. Veuillez consulter la réponse ci-dessous de Diego Vega (qui travaille dans l'équipe EF chez Microsoft)!


@gsharp et Shawn Mclean: Où obtenez-vous ces informations? N'avez-vous toujours pas accès à l'ObjectContext sous-jacent?

IEnumerable<Customer> customers = 
    ((IObjectContextAdapter)this)
    .ObjectContext.ExecuteStoreQuery<Customer>("select * from customers");

Remplacez l'instruction "select" par un proc stocké, et c'est parti.

Quant à votre autre question: oui, malheureusement, vos sp seront écrasés. Vous devrez peut-être ajouter les instructions "CREATE PROCEDURE" dans votre code.

Pour EF 4.2:

var customers = context.Database.SqlQuery<Customer>("select * from customers")

Merci. Pourriez-vous m'indiquer quelques liens qui contiennent plus d'informations sur ce sujet.
frennky

1
Vous voudrez rechercher les trois fonctions Execute sur l'objet ObjectContext (ExecuteStoreQuery, ExecuteFunction et ExecuteStoreCommand).
Anon

J'ai mal compris la question. Je pensais qu'il voulait créer des SP sur une base de code.
gsharp

Vous pouvez remplacer Context.OnModelCreating et ajouter une logique personnalisée pour créer assez facilement des éléments de base de données tels que des processus stockés via du code. Pas idéal mais à la rigueur ça fera l'affaire.
Rick Strahl

Vous n'avez pas besoin du cast IObjectContextAdapter. Le DbContext peut gérer des sp ou des instructions SQL personnalisées à l'aide de l'objet Database intégré: context.Database.SqlQuery <Dummy> ("sp_GetDummy");
Steven K.

50

Mise à jour: à partir d'EF6, EF Code First prend en charge le mappage de procédure stockée pour les insertions, les mises à jour et les suppressions. Vous pouvez spécifier le mappage de procédure stockée lors de la création du modèle à l'aide de la méthode MapToStoredProcedures. Nous prenons également en charge l'échafaudage automatique des procédures stockées de base pour ces opérations. Voir la spécification des fonctionnalités ici .

Réponse originale: nous n'aurons pas de support pour le mappage des procédures stockées dans le modèle dans Code-First dans la première version, ni nous n'aurons un moyen de générer automatiquement des procédures stockées pour les opérations CRUD à partir de vos types. Ce sont des fonctionnalités que nous aimerions ajouter à l'avenir.

Comme cela a été mentionné dans ce fil, il est possible de revenir à ObjectContext mais DbContext fournit également de belles API pour exécuter des requêtes et des commandes SQL natives (par exemple DbSet.SqlQuery, DbContext.Database.SqlQuery et DbContext.Database.ExecuteSqlCommand). Les différentes versions de SqlQuery ont la même fonctionnalité de matérialisation de base qui existe dans EF4 (comme ExecuteStoreQuery: http://msdn.microsoft.com/en-us/library/dd487208.aspx ).

J'espère que cela t'aides.


6
BTW, j'ai écrit un article de blog il y a quelques jours qui détaille comment utiliser ces méthodes pour appeler des procédures stockées, même des procédures stockées avec des paramètres de sortie: blogs.msdn.com/b/diego/archive/2012/01/10/… .
divega

3
Fin 2013, EF6 est toujours en développement. Attendre trois ans juste pour améliorer le support des sprocs, soupir.
DOK

1
@divega Existe-t-il une prise en charge fortement typée pour simplement sélectionner des valeurs à partir d'une procédure stockée - cette approche basée sur le code semble spécifique à la gestion de la durée de vie des objets? Plus précisément, pour les recherches complexes, à l'aide d'une procédure stockée spFooSearch avec un paramètre de sortie TotalRows.
John Zabroski

31
    public IList<Product> GetProductsByCategoryId(int categoryId)
    {
        IList<Product> products;

        using (var context = new NorthwindData())
        {
            SqlParameter categoryParam = new SqlParameter("@categoryID", categoryId);
            products = context.Database.SqlQuery<Product>("Products_GetByCategoryID @categoryID", categoryParam).ToList();
        }

        return products;
    }

    public Product GetProductById(int productId)
    {
        Product product = null;

        using (var context = new NorthwindData())
        {
            SqlParameter idParameter = new SqlParameter("@productId", productId);
            product = context.Database.SqlQuery<Product>("Product_GetByID @productId", idParameter).FirstOrDefault();
        }

        return product;
    }

8

Une solution plus sûre de type serait la suivante:

http://strugglesofacoder.blogspot.be/2012/03/calling-stored-procedure-with-entity.html

L'utilisation de cette classe est:

var testProcedureStoredProcedure = new TestProcedureStoredProcedure() { Iets = 5, NogIets = true };

var result = DbContext.Database.ExecuteStoredProcedure(testProcedureStoredProcedure);


2

Pour .NET Core (EntityFrameworkCore), j'ai pu les faire fonctionner.

Ce n'est peut-être pas le plus soigné, mais cela fonctionne définitivement.

La migration pour ajouter la procédure stockée ressemble à ceci :

using Microsoft.EntityFrameworkCore.Migrations;
using System.Text;

namespace EFGetStarted.AspNetCore.NewDb.Migrations
{
    public partial class StoredProcedureTest : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("CREATE PROCEDURE GetBlogForAuthorName");
            sb.AppendLine("@authorSearch varchar(100)");
            sb.AppendLine("AS");
            sb.AppendLine("BEGIN");
            sb.AppendLine("-- SET NOCOUNT ON added to prevent extra result sets from interfering with SELECT statements.");
            sb.AppendLine("SET NOCOUNT ON;");
            sb.AppendLine("SELECT  Distinct Blogs.BlogId, Blogs.Url");
            sb.AppendLine("FROM Blogs INNER JOIN");
            sb.AppendLine("Posts ON Blogs.BlogId = Posts.BlogId INNER JOIN");
            sb.AppendLine("PostsAuthors ON Posts.PostId = PostsAuthors.PostId Inner JOIN");
            sb.AppendLine("Authors on PostsAuthors.AuthorId = Authors.AuthorId");
            sb.AppendLine("Where Authors.[Name] like '%' + @authorSearch + '%'");
            sb.AppendLine("END");

            migrationBuilder.Sql(sb.ToString());
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.Sql("DROP PROCEDURE GetBlogForAuthorName");
        }
    }
}

Je pourrais alors l'appeler avec le code suivant :

var blogs = _context.Blogs.FromSql("exec GetBlogForAuthorName @p0", "rod").Distinct();

Plus tard, j'ai essayé d'obtenir certaines des données associées (une à plusieurs données de relation, par exemple le contenu de la publication) et le blog est revenu avec le contenu de la publication rempli comme prévu.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.