Entity Framework Code First supporta le stored procedure?


112

Ho guardato diverse presentazioni di EF Code First e non ho visto come funziona EFCF con le stored procedure.

Come posso dichiarare un metodo che utilizzerà alcuni sp? Posso passare un'entità a un metodo che chiama sp senza mappare manualmente le proprietà dell'entità ai parametri sp?

Inoltre, cosa succede se cambio il mio modello? Cadrebbe il mio sp durante la ricreazione della tabella dal modello? E per quanto riguarda i trigger?

Se queste cose non sono supportate, ci sono piani per supportarle in futuro?


5
La roadmap di EF indica che EF 6 supporterà stored procedure e funzioni per Code First. entityframework.codeplex.com/wikipage?title=Roadmap
frennky

Risposte:


66

EDIT: la mia risposta originale per EF4.1 (sotto) non è aggiornata. Si prega di vedere la risposta di seguito da Diego Vega (che lavora nel team EF di Microsoft)!


@gsharp e Shawn Mclean: Dove stai ottenendo queste informazioni? Non hai ancora accesso all'ObjectContext sottostante?

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

Sostituisci l'istruzione "select" con un proc memorizzato, e il gioco è fatto.

Per quanto riguarda l'altra tua domanda: Sì, sfortunatamente le tue sp verranno distrutte. Potrebbe essere necessario aggiungere le istruzioni "CREATE PROCEDURE" nel codice.

Per EF 4.2:

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

Grazie. Potresti indicarmi alcuni link che contengono maggiori informazioni su questo argomento.
frennky

1
Ti consigliamo di cercare le tre funzioni Execute sull'oggetto ObjectContext (ExecuteStoreQuery, ExecuteFunction e ExecuteStoreCommand).
anon

Ho frainteso la domanda. Stavo pensando che volesse creare SP e una prima base di codice.
gsharp

È possibile eseguire l'override di Context.OnModelCreating e aggiungere logica personalizzata per creare elementi di database come processi archiviati tramite codice abbastanza facilmente. Non è l'ideale, ma in un attimo farà il trucco.
Rick Strahl

Non è necessario il cast di IObjectContextAdapter. Il DbContext può gestire le istruzioni SQL personalizzate o sp utilizzando l'oggetto Database incorporato: context.Database.SqlQuery <Dummy> ("sp_GetDummy");
Steven K.

50

Aggiornamento: da EF6 in poi, EF Code First supporta il mapping di stored procedure per inserimenti, aggiornamenti ed eliminazioni. È possibile specificare la mappatura della procedura memorizzata durante la creazione del modello utilizzando il metodo MapToStoredProcedures. Supportiamo anche lo scaffolding automatico delle stored procedure di base per tali operazioni. Vedere le specifiche delle funzionalità qui .

Risposta originale: non avremo supporto per la mappatura di stored procedure nel modello in Code-First nella prima versione, né avremo un modo per generare automaticamente stored procedure per operazioni CRUD dai tuoi tipi. Queste sono caratteristiche che vorremmo aggiungere in futuro.

Come è stato menzionato in questo thread, è possibile ripiegare su ObjectContext ma DbContext fornisce anche API utili per eseguire query e comandi SQL nativi (ad esempio DbSet.SqlQuery, DbContext.Database.SqlQuery e DbContext.Database.ExecuteSqlCommand). Le diverse versioni di SqlQuery hanno la stessa funzionalità di materializzazione di base che esiste in EF4 (come ExecuteStoreQuery: http://msdn.microsoft.com/en-us/library/dd487208.aspx ).

Spero che questo ti aiuti.


6
A proposito, alcuni giorni fa ho scritto un post sul blog che descrive in dettaglio come utilizzare questi metodi per invocare stored procedure, anche stored procedure con parametri di output: blogs.msdn.com/b/diego/archive/2012/01/10/… .
divega

3
Alla fine del 2013, EF6 è ancora in fase di sviluppo. Aspettando tre anni solo per migliorare il supporto per sprocs, sigh.
DOK

1
@divega Esiste un supporto fortemente tipizzato per la selezione dei valori da una stored procedure: questo approccio code-first sembra specifico per la gestione della durata degli oggetti? In particolare, per ricerche complesse, utilizzando una stored procedure spFooSearch con un parametro di output 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

Una soluzione più sicura per i tipi sarebbe questa:

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

L'utilizzo di questa classe è:

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

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


2

Per .NET Core (EntityFrameworkCore), sono riuscito a farli funzionare.

Potrebbe non essere il più accurato, ma funziona sicuramente.

La migrazione per l'aggiunta della stored procedure ha questo aspetto :

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

Potrei quindi chiamarlo con il seguente codice:

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

Successivamente ho provato a ottenere alcuni dei dati correlati (uno a molti dati di relazione, ad esempio il contenuto del post) e il blog è tornato con il contenuto del post pieno come previsto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.