Czy Entity Framework Code First obsługuje procedury składowane?

112

Obejrzałem kilka prezentacji EF Code First i nie widziałem, jak EFCF działa z procedurami składowanymi.

Jak mogę zadeklarować metodę, która będzie korzystała z niektórych sp? Czy mogę przekazać jednostkę do metody, która wywołuje sp bez ręcznego mapowania właściwości jednostki na parametry sp?

Co się stanie, jeśli zmienię model? Czy spowoduje to spadek mojego sp podczas odtwarzania tabeli z modelu? A co z wyzwalaczami?

Jeśli te rzeczy nie są obsługiwane, czy są jakieś plany, aby wspierać je w przyszłości?

Frennky
źródło
5
Plan działania EF wskazuje, że EF 6 będzie obsługiwał procedury składowane i funkcje dla Code First. entityframework.codeplex.com/wikipage?title=Roadmap
frennky
Zobacz też: stackoverflow.com/questions/4873607/…
Nathan Koop

Odpowiedzi:

66

EDYCJA: Moja pierwotna odpowiedź dotycząca EF4.1 (poniżej) jest teraz nieaktualna. Zobacz odpowiedź poniżej od Diego Vegi (który pracuje w zespole EF w firmie Microsoft)!


@gsharp i Shawn Mclean: Skąd bierzesz te informacje? Czy nadal nie masz dostępu do bazowego ObjectContext?

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

Zastąp instrukcję „select” zapisanym procesem i gotowe.

A jeśli chodzi o twoje drugie pytanie: tak, niestety twoje sp's zostaną zablokowane. Może być konieczne dodanie instrukcji „CREATE PROCEDURE” do kodu.

Dla EF 4.2:

var customers = context.Database.SqlQuery<Customer>("select * from customers")
zaraz
źródło
Dzięki. Czy możesz wskazać mi linki, które zawierają więcej informacji na ten temat.
frennky
1
Będziesz chciał wyszukać trzy funkcje Execute w obiekcie ObjectContext (ExecuteStoreQuery, ExecuteFunction i ExecuteStoreCommand).
anon
Źle zrozumiałem pytanie. Myślałem, że chce najpierw stworzyć SP na podstawie kodu.
gsharp
Możesz przesłonić Context.OnModelCreating i dodać niestandardową logikę, aby dość łatwo utworzyć elementy bazy danych, takie jak przechowywane procesy za pośrednictwem kodu. Nie jest idealny, ale w mgnieniu oka wystarczy.
Rick Strahl
Nie potrzebujesz rzutowania IObjectContextAdapter. DbContext może obsługiwać sp lub niestandardowe instrukcje SQL przy użyciu wbudowanego obiektu Database: context.Database.SqlQuery <Dummy> ("sp_GetDummy");
Steven K.
50

Aktualizacja: od EF6 EF Code First obsługuje mapowanie procedur składowanych dla wstawiania, aktualizacji i usuwania. Mapowanie procedury składowanej można określić podczas tworzenia modelu przy użyciu metody MapToStoredProcedures. Obsługujemy również automatyczne tworzenie szkieletów podstawowych procedur składowanych dla tych operacji. Zobacz specyfikację funkcji tutaj .

Oryginalna odpowiedź: nie będziemy obsługiwać mapowania procedur składowanych w modelu w Code-First w pierwszej wersji ani nie będziemy mieć możliwości automatycznego generowania procedur składowanych dla operacji CRUD z Twoich typów. Są to funkcje, które chcielibyśmy dodać w przyszłości.

Jak wspomniano w tym wątku, można wrócić do ObjectContext, ale DbContext zapewnia również ładne interfejsy API do wykonywania natywnych zapytań i poleceń SQL (np. DbSet.SqlQuery, DbContext.Database.SqlQuery i DbContext.Database.ExecuteSqlCommand). Różne wersje SqlQuery mają te same podstawowe funkcje materializacji, które istnieją w EF4 (np. ExecuteStoreQuery: http://msdn.microsoft.com/en-us/library/dd487208.aspx ).

Mam nadzieję że to pomoże.

divega
źródło
6
Przy okazji, kilka dni temu napisałem post na blogu, w którym szczegółowo opisałem, jak używać tych metod do wywoływania procedur składowanych, nawet procedur składowanych z parametrami wyjściowymi: blogs.msdn.com/b/diego/archive/2012/01/10/… .
divega
3
Pod koniec 2013 r. EF6 jest nadal w fazie rozwoju. Czekam trzy lata, żeby poprawić wsparcie dla sprocesów, westchnij.
DOK,
1
@divega Czy istnieje silnie wpisana obsługa tylko wybierania wartości z procedury składowanej - to podejście oparte na kodzie wydaje się specyficzne dla zarządzania czasem życia obiektu? W szczególności w przypadku skomplikowanych wyszukiwań przy użyciu procedury składowanej spFooSearch z parametrem wyjściowym 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;
    }
znak
źródło
8

Bardziej bezpieczne rozwiązanie byłoby następujące:

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

Zastosowanie tej klasy to:

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

var result = DbContext.Database.ExecuteStoredProcedure(testProcedureStoredProcedure);
Luc Bos
źródło
Link nie jest już aktywny, ale oto archiwum: web.archive.org/web/20150430090848/http://www.lucbos.net/2012/…
Arturo Torres Sánchez
2

W przypadku platformy .NET Core (EntityFrameworkCore) udało mi się je uruchomić.

Może nie jest to najładniejsze, ale to zdecydowanie działa.

Migracja w celu dodania procedury składowanej wygląda następująco :

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

Mógłbym wtedy zadzwonić za pomocą następującego kodu:

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

Później próbowano pobrać niektóre powiązane dane (dane dotyczące relacji jeden do wielu, np. Treść posta) i blog wrócił z wypełnioną treścią posta zgodnie z oczekiwaniami.

HockeyJ
źródło