Поддерживает ли Entity Framework Code First хранимые процедуры?


112

Я просмотрел несколько презентаций EF Code First и не видел, как EFCF работает с хранимыми процедурами.

Как я могу объявить метод, который будет использовать некоторый sp? Могу ли я передать сущность методу, вызывающему sp, без ручного сопоставления свойств сущности с параметрами sp?

А что будет, если я поменяю модель? Будет ли он уронить мой sp при воссоздании таблицы из модели? А что насчет триггеров?

Если эти вещи не поддерживаются, есть ли планы по их поддержке в будущем?


5
В дорожной карте EF указано, что EF 6 будет поддерживать хранимые процедуры и функции для Code First. entityframework.codeplex.com/wikipage?title=Roadmap
frennky

Ответы:


66

РЕДАКТИРОВАТЬ: мой исходный ответ для EF4.1 (ниже) устарел. См. Ответ ниже от Диего Веги (который работает в команде EF в Microsoft)!


@gsharp и Шон Маклин: Откуда вы берете эту информацию? Разве у вас все еще нет доступа к базовому ObjectContext?

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

Замените оператор «select» на сохраненную процедуру, и готово.

Что касается вашего другого вопроса: да, к сожалению, ваш SP будет сбит. Возможно, вам потребуется добавить в код операторы CREATE PROCEDURE.

Для EF 4.2:

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

Спасибо. Не могли бы вы указать мне на некоторые ссылки, которые имеют больше информации по этой теме.
frennky

1
Вы захотите найти три функции Execute в объекте ObjectContext (ExecuteStoreQuery, ExecuteFunction и ExecuteStoreCommand).
анон

Я неправильно понял вопрос. Я думал, что он хочет сначала создать SP на основе кода.
gsharp

Вы можете переопределить Context.OnModelCreating и добавить настраиваемую логику, чтобы довольно легко создавать элементы базы данных, такие как хранимые процедуры, через код. Не идеально, но в крайнем случае это поможет.
Rick Strahl

Приведение IObjectContextAdapter не требуется. DbContext может обрабатывать sp или пользовательские операторы SQL, используя встроенный объект Database: context.Database.SqlQuery <Dummy> ("sp_GetDummy");
Стивен К.

50

Обновление: начиная с EF6, EF Code First поддерживает сопоставление хранимых процедур для вставки, обновления и удаления. Вы можете указать отображение хранимых процедур во время создания модели с помощью метода MapToStoredProcedures. Мы также поддерживаем автоматическое формирование базовых хранимых процедур для этих операций. См. Спецификацию функции здесь .

Оригинальный ответ: нас не будет поддержки сопоставления хранимых процедур в модели в Code-First в первом выпуске, а также у нас не будет способа автоматически генерировать хранимые процедуры для операций CRUD из ваших типов. Это функции, которые мы хотели бы добавить в будущем.

Как упоминалось в этом потоке, можно вернуться к ObjectContext, но DbContext также предоставляет удобные API-интерфейсы для выполнения собственных запросов и команд SQL (например, DbSet.SqlQuery, DbContext.Database.SqlQuery и DbContext.Database.ExecuteSqlCommand). Различные версии SqlQuery имеют те же базовые функции материализации, которые существуют в EF4 (например, ExecuteStoreQuery: http://msdn.microsoft.com/en-us/library/dd487208.aspx ).

Надеюсь это поможет.


6
Кстати, несколько дней назад я написал в блоге сообщение, в котором подробно описывается, как использовать эти методы для вызова хранимых процедур, даже хранимых процедур с выходными параметрами: blogs.msdn.com/b/diego/archive/2012/01/10/… .
divega

3
В конце 2013 года EF6 все еще находится в разработке. Жду три года только для улучшения поддержки sprocs, вздох.
DOK

1
@divega Существует ли строго типизированная поддержка простого выбора значений из хранимой процедуры - этот подход, ориентированный на код, кажется специфичным для управления временем жизни объекта? В частности, для сложных поисков используется хранимая процедура spFooSearch с выходным параметром TotalRows.
Джон Заброски

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

Более безопасным решением будет следующее:

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

Использование этого класса:

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

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

Ссылка больше не активна, но вот архив: web.archive.org/web/20150430090848/http://www.lucbos.net/2012/…
Arturo Torres Sánchez

2

Для .NET Core (EntityFrameworkCore) мне удалось заставить их работать.

Может быть, не самый изящный, но это определенно работает.

Миграция для добавления хранимой процедуры выглядит как это :

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

Затем я мог бы вызвать его с помощью следующего кода:

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

Позже попытались получить некоторые из связанных данных (данные о взаимоотношениях «один ко многим», например, содержимое сообщения), и блог вернулся с заполненным содержимым сообщения, как указано.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.