Можете ли вы создать представления SQL / хранимую процедуру, используя Entity Framework 4.1 Code первый подход - PullRequest
44 голосов
/ 06 октября 2011

Entity Framework 4.1 Code First отлично работает при создании таблиц и связей. Можно ли создать представления SQL или хранимой процедуры с использованием подхода Code first? Любые указатели по этому поводу будут высоко оценены. Большое спасибо!

Ответы [ 7 ]

83 голосов
/ 02 марта 2013

Мы поддерживаем хранимые процедуры в нашей программе Entry Framework Code First Migrations. Наш подход заключается в создании некоторой папки для хранения файлов .sql (например, ~ / Sql /). Создайте .sql файлы в папке для создания и удаления хранимой процедуры. Например. Create_sp_DoSomething.sql и Drop_sp_DoSomething. Поскольку SQL выполняется в пакете и CREATE PROCEDURE.. должен быть первым оператором в пакете, сделайте CREATE PROCEDURE... первым оператором в файле. Кроме того, не ставьте GO после DROP.... Добавьте файл ресурсов в свой проект, если у вас его еще нет. Перетащите файлы .sql из обозревателя решений в представление «Файлы» конструктора ресурсов. Теперь создайте пустую миграцию (Add-Migration SomethingMeaningful_sp_DoSomething) и используйте:

namespace MyApplication.Migrations
{
    using System;
    using System.Data.Entity.Migrations;

    public partial class SomethingMeaningful_sp_DoSomething : DbMigration
    {
        public override void Up()
        {
            this.Sql(Properties.Resources.Create_sp_DoSomething);
        }

        public override void Down()
        {
            this.Sql(Properties.Resources.Drop_sp_DoSomething);
        }
    }
}

~ / Sql / Create_sp_DoSomething.sql

CREATE PROCEDURE [dbo].[sp_DoSomething] AS
BEGIN TRANSACTION
-- Your stored procedure here
COMMIT TRANSACTION
GO

~ / Sql / Drop_sp_DoSomething.sql

DROP PROCEDURE [dbo].[sp_DoSomething]
25 голосов
/ 28 марта 2014

На первый взгляд, мне действительно нравится подход Carl G, но он требует много ручного взаимодействия В моем сценарии я всегда отбрасываю все хранимые процедуры, представления ... и воссоздаю их всякий раз, когда происходит изменение в базе данных. Таким образом, мы уверены, что все обновлено до последней версии.

Восстановление происходит путем установки следующего инициализатора:

Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, Configuration>());

Тогда наш начальный метод будет вызываться всякий раз, когда будет готова миграция.

protected override void Seed(DeploymentLoggingContext context)
    {
        // Delete all stored procs, views
        foreach (var file in Directory.GetFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Sql\\Seed"), "*.sql"))
        {
            context.Database.ExecuteSqlCommand(File.ReadAllText(file), new object[0]);
        }

        // Add Stored Procedures
        foreach (var file in Directory.GetFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Sql\\StoredProcs"), "*.sql"))
        {
            context.Database.ExecuteSqlCommand(File.ReadAllText(file), new object[0]);
        }
    }

Заявления SQL хранятся в * .sql файлах для удобного редактирования. Убедитесь, что в ваших файлах «Build Action» установлено «Content», а «Copy to Output Directory» установлено в «Copy Always». Мы ищем папки и выполняем все скрипты внутри. Не забудьте исключить операторы «GO» в вашем SQL, потому что они не могут быть выполнены с помощью ExecuteSqlCommand ().

Моя текущая структура каталогов выглядит следующим образом:

Project.DAL
+ Миграции
+ Sql
++ Семя
+++ dbo.cleanDb.sql
++ StoredProcs
+++ dbo.sp_GetSomething.sql

Теперь вам просто нужно добавить дополнительные хранимые процедуры в папку, и все будет обновлено соответствующим образом.

11 голосов
/ 30 декабря 2014

Чтобы раскрыть ответ bbodenmiller , в Entity Framework 6 класс DbMigration имеет такие методы, как AlterStoredProcedure, которые позволяют изменять хранимые процедуры, не опускаясь до сырой SQL.

Вот пример метода миграции Up(), который изменяет существующую хранимую процедуру SQL Server с именем EditItem, которая принимает три параметра типа int, nvarchar(50) и smallmoney, соответственно:

public partial class MyCustomMigration : DbMigration
{
    public override void Up()
    {
        this.AlterStoredProcedure("dbo.EditItem", c => new
        {
            ItemID = c.Int(),
            ItemName = c.String(maxLength:50),
            ItemCost = c.Decimal(precision: 10, scale: 4, storeType: "smallmoney")
        }, @" (Stored procedure body SQL goes here) "   
    }

    //...
}

На моем компьютере этот скрипт миграции выдает следующий SQL:

ALTER PROCEDURE [dbo].[EditItem]
    @ItemID [int],
    @ItemName [nvarchar](50),
    @ItemCost [smallmoney]
AS
BEGIN
    (Stored procedure body SQL goes here)
END
11 голосов
/ 10 ноября 2014

Похоже, что он плохо документирован, однако теперь вы можете выполнять некоторые манипуляции с хранимыми процедурами, используя AlterStoredProcedure , CreateStoredProcedure , DropStoredProcedure , MoveStoredProcedure , RenameStoredProcedure в Entity Framework 6. Я еще не пробовал их, поэтому пока не могу привести пример их использования.

10 голосов
/ 06 октября 2011

EF-код предполагает, что в базе данных нет логики. Это означает, что нет хранимых процедур и нет просмотров базы данных. Из-за этого подхода, основанного на коде, не предусмотрено никакого механизма для автоматической генерации таких конструкций. Как это можно сделать, если это означает генерирование логики?

Вы должны сами создать их в пользовательском инициализаторе базы данных , выполнив сценарии создания вручную. Я не думаю, что эти пользовательские конструкции SQL могут быть обработаны миграциями SQL.

2 голосов
/ 24 октября 2014

Дизайн emp работает как чемпион!Я использую его шаблон, но я также отображаю хранимые процедуры внутри моего класса DbContext, который позволяет просто вызывать эти методы контекста вместо использования SqlQuery () и вызывать процедуры непосредственно из моего репозитория.Поскольку при росте приложения ситуация может немного усложниться, я создал проверку в своем методе Seed, которая удостоверяет, что фактическое количество параметров хранимой процедуры соответствует количеству параметров в методе отображения.Я также обновил упомянутый цикл DROP.Вместо того чтобы сохранять отдельную папку / файл для операторов drop, я просто читаю первую строку каждого файла sql и заменяю CREATE на DROP (просто убедитесь, что первая строка всегда просто CREATE PROCEDURE ProcName).Таким образом, все процедуры в моей папке StoredProcs удаляются и воссоздаются при каждом запуске Update-Database.Дроп также помещается в блок try-catch на случай, если процедура новая.Чтобы количество параметров процедуры работало, вам нужно убедиться, что вы обернули блок BEGIN/END вокруг вашего tsql, поскольку каждая строка файла считывается до BEGIN.Также убедитесь, что каждый параметр sp находится в новой строке.

        // Drop Stored Procs
        foreach (var file in Directory.GetFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..\\DataContext\\SiteMigrations\\StoredProcs"), "*.sql"))
        {
            // Try to drop proc if its already created
            // Without this, for new procs, seed method fail on trying to delete
            try
            {
                StreamReader reader = new StreamReader(file);
                // Read first line of file to create drop command (turning CREATE [dbo].[TheProc] into DROP [dbo].[TheProc])
                string dropCommand = reader.ReadLine().Replace("CREATE", "DROP");

                context.Database.ExecuteSqlCommand(dropCommand, new object[0]);
            }
            catch { }

        }

        // Add Stored Procs
        foreach (var file in Directory.GetFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..\\DataContext\\SiteMigrations\\StoredProcs"), "*.sql"))
        {
            // File/Proc names must match method mapping names in DbContext
            int lastSlash = file.LastIndexOf('\\');
            string fileName = file.Substring(lastSlash + 1);
            string procName = fileName.Substring(0, fileName.LastIndexOf('.'));

            // First make sure proc mapping in DbContext contain matching parameters.  If not throw exception.
            // Get parameters for matching mapping
            MethodInfo mi = typeof(SiteContext).GetMethod(procName);

            if (mi == null)
            {
                throw new Exception(String.Format("Stored proc mapping for {0} missing in DBContext", procName));
            }

            ParameterInfo[] methodParams = mi.GetParameters();
            // Finished getting parameters

            // Get parameters from stored proc
            int spParamCount = 0;
            using (StreamReader reader = new StreamReader(file))
            {
                string line;                    
                while ((line = reader.ReadLine()) != null) 
                {
                    // If end of parameter section, break out
                    if (line.ToUpper() == "BEGIN")
                    {
                        break;
                    }
                    else
                    {
                        if (line.Contains("@"))
                        {
                            spParamCount++;
                        }
                    }                        
                }
            }
            // Finished get parameters from stored proc

            if (methodParams.Count() != spParamCount)
            {
                string err = String.Format("Stored proc mapping for {0} in DBContext exists but has {1} parameter(s)" +
                    " The stored procedure {0} has {2} parameter(s)", procName, methodParams.Count().ToString(), spParamCount.ToString());
                throw new Exception(err);
            }
            else
            {
                context.Database.ExecuteSqlCommand(File.ReadAllText(file), new object[0]);
            }
        }

Наслаждайтесь!

1 голос
/ 06 октября 2011

Как отметил Ладислав, DbContext в целом, как правило, сводит к минимуму логику в базе данных, но можно выполнить пользовательский SQL с помощью context.Database.ExecuteSqlCommand() или context.Database.SqlQuery().

...