Можете ли вы создавать представления sql/хранимую процедуру с использованием Entity Framework 4.1 Первый подход к коду

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

Ответ 1

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

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

Ответ 2

Мы поддерживаем хранимые процедуры в First Entity 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]

Ответ 3

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

Отдых происходит, устанавливая следующий Инициализатор:

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

Затем наш метод seed будет вызван всякий раз, когда будет готов к миграции

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

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

Ответ 4

Чтобы раскрыть ответ 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

Ответ 5

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

Ответ 6

Дизайн emp работает как чемпион! Я использую его шаблон, но я также сопоставляю хранимые процедуры внутри моего класса DbContext, который позволяет просто называть эти контекстные методы вместо использования SqlQuery() и вызывать процедуры непосредственно из моего репозитория. По мере того, как приложение становится немного волосатым, когда приложение растет, я создал проверку в моем методе Seed, чтобы убедиться, что фактическое количество параметров хранимой процедуры соответствует счету параметров метода сопоставления. Я также обновил упомянутый emp контура 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]);
            }
        }

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

Ответ 7

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