¿Entity Framework Code First admite procedimientos almacenados?

112

He visto varias presentaciones de EF Code First y no he visto cómo funciona EFCF con procedimientos almacenados.

¿Cómo puedo declarar un método que usará algún sp? ¿Puedo pasar una entidad a un método que llama a sp sin mapear manualmente las propiedades de la entidad a los parámetros de sp?

Además, ¿qué pasa si cambio mi modelo? ¿Dejaría caer mi sp al recrear la tabla desde el modelo? ¿Y los desencadenantes?

Si estas cosas no son compatibles, ¿hay algún plan para apoyarlas en el futuro?

frenético
fuente
5
La hoja de ruta de EF señala que EF 6 admitirá procedimientos y funciones almacenados para Code First. entityframework.codeplex.com/wikipage?title=Roadmap
frennky
Ver también: stackoverflow.com/questions/4873607/…
Nathan Koop

Respuestas:

66

EDITAR: Mi respuesta original para EF4.1 (a continuación) ahora está desactualizada. ¡Vea la respuesta a continuación de Diego Vega (que trabaja en el equipo EF en Microsoft)!


@gsharp y Shawn Mclean: ¿De dónde sacas esta información? ¿Todavía no tienes acceso al ObjectContext subyacente?

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

Reemplace la declaración "select" con un proceso almacenado, y listo.

En cuanto a su otra pregunta: Sí, desafortunadamente sus sp serán golpeados. Es posible que deba agregar las declaraciones "CREAR PROCEDIMIENTO" en su código.

Para EF 4.2:

var customers = context.Database.SqlQuery<Customer>("select * from customers")
luego
fuente
Gracias. ¿Podría señalarme algunos enlaces que tienen más información sobre este tema?
frennky
1
Querrá buscar las tres funciones Execute en el objeto ObjectContext (ExecuteStoreQuery, ExecuteFunction y ExecuteStoreCommand).
anon
Entendí mal la pregunta. Estaba pensando que él quería crear SP y un código primero.
gsharp
Puede anular Context.OnModelCreating y agregar lógica personalizada para crear elementos de base de datos como procesos almacenados a través de código con bastante facilidad. No es ideal, pero en caso de apuro funcionará.
Rick Strahl
No necesita la conversión IObjectContextAdapter. El DbContext puede manejar sp's o sentencias SQL personalizadas usando el objeto de base de datos integrado: context.Database.SqlQuery <Dummy> ("sp_GetDummy");
Steven K.
50

Actualización: a partir de EF6, EF Code First admite la asignación de procedimientos almacenados para inserciones, actualizaciones y eliminaciones. Puede especificar la asignación de procedimientos almacenados durante la creación del modelo mediante el método MapToStoredProcedures. También apoyamos el andamiaje automático de procedimientos almacenados básicos para esas operaciones. Consulte la especificación de funciones aquí .

Respuesta original: No tendremos soporte para mapear procedimientos almacenados en el modelo en Code-First en la primera versión, ni tendremos una forma de generar automáticamente procedimientos almacenados para operaciones CRUD a partir de sus tipos. Estas son características que nos gustaría agregar en el futuro.

Como se mencionó en este hilo, es posible recurrir a ObjectContext pero DbContext también proporciona buenas API para ejecutar consultas y comandos SQL nativos (por ejemplo, DbSet.SqlQuery, DbContext.Database.SqlQuery y DbContext.Database.ExecuteSqlCommand). Las diferentes versiones de SqlQuery tienen la misma funcionalidad de materialización básica que existe en EF4 (como ExecuteStoreQuery: http://msdn.microsoft.com/en-us/library/dd487208.aspx ).

Espero que esto ayude.

divega
fuente
6
Por cierto, escribí una publicación de blog hace unos días que detalla cómo usar estos métodos para invocar procedimientos almacenados, incluso procedimientos almacenados con parámetros de salida: blogs.msdn.com/b/diego/archive/2012/01/10/… .
divega
3
A finales de 2013, EF6 todavía está en desarrollo. Esperando tres años solo para mejorar el soporte para sprocs, suspiro.
DOK
1
@divega ¿Existe un soporte fuertemente tipado para seleccionar valores de un procedimiento almacenado? ¿Este enfoque de código primero parece específico para administrar el tiempo de vida de los objetos? Específicamente, para búsquedas complejas, se usa un procedimiento almacenado spFooSearch con un parámetro de salida 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;
    }
marca
fuente
8

Una solución más segura de tipo sería esta:

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

El uso de esta clase es:

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

var result = DbContext.Database.ExecuteStoredProcedure(testProcedureStoredProcedure);
Luc Bos
fuente
El enlace ya no está activo, pero aquí está el archivo: web.archive.org/web/20150430090848/http://www.lucbos.net/2012/…
Arturo Torres Sánchez
2

Para .NET Core (EntityFrameworkCore), he podido hacer que funcionen.

Puede que no sea el mejor, pero definitivamente funciona.

La migración de la adición de las miradas de procedimiento almacenado como esta :

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

Luego podría llamarlo con el siguiente código:

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

Más tarde, intenté obtener algunos de los datos relacionados (uno a muchos datos de relación, por ejemplo, contenido de la publicación) y el blog volvió con el contenido de la publicación completo como se esperaba.

HockeyJ
fuente