web-dev-qa-db-ja.com

Entity Framework 4.1 Code Firstアプローチを使用してSQLビュー/ストアドプロシージャを作成できますか

Entity Framework 4.1 Code Firstは、テーブルとリレーションシップの作成に最適です。コードファーストアプローチを使用してSQLビューまたはストアドプロシージャを作成することは可能ですか?これに関するポインタは高く評価されます。どうもありがとう!

44
user629161

EFコードファーストアプローチは、データベースにロジックがないことを想定しています。つまり、ストアドプロシージャもデータベースビューもありません。そのため、コードファーストアプローチでは、このような構造を自動的に生成するメカニズムは提供されません。ロジックの生成を意味する場合、どうすればそれができますか?

作成スクリプトを手動で実行して、 カスタムデータベース初期化子 で作成する必要があります。このカスタムSQLコンストラクトは、SQLの移行で処理できるとは思いません。

10
Ladislav Mrnka

Entity Framework Code First Migrationsでストアドプロシージャをサポートしています。私たちのアプローチは、.sqlファイルを保持するフォルダーを作成することです(たとえば、〜/ Sql /)。ストアドプロシージャを作成および削除するために、フォルダーに.sqlファイルを作成します。例えば。 Create_sp_DoSomething.sqlおよびDrop_sp_DoSomething。 SQLはバッチで実行され、CREATE PROCEDURE..がバッチの最初のステートメントである必要があるため、CREATE PROCEDURE...をファイルの最初のステートメントにします。また、DROP...の後にGOを置かないでください。リソースファイルをまだ持っていない場合は、プロジェクトに追加します。ソリューションエクスプローラーから.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]
84
Carl G

一見したところ、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ファイルに保存されます。ファイルの[ビルドアクション]が[コンテンツ]に、[出力ディレクトリにコピー]が[常にコピー]に設定されていることを確認してください。フォルダを検索し、内部のすべてのスクリプトを実行します。 SQLの「GO」ステートメントは、ExecuteSqlCommand()で実行できないため、除外することを忘れないでください。

現在のディレクトリレイアウトは次のとおりです。

Project.DAL
+移行
+ SQL
++シード
+++ dbo.cleanDb.sql
++ StoredProcs
+++ dbo.sp_GetSomething.sql

これで、追加のストアドプロシージャをフォルダにドロップするだけで、すべてが適切に更新されます。

25
emp

bbodenmiller's answer を拡張するために、Entity Framework 6では、 DbMigrationクラス にはAlterStoredProcedureなどのメソッドがあり、ストアドプロシージャをrawにドロップすることなく変更できます。 SQL。

タイプintUp()、およびsmallmoneyの3つのパラメーターを取るEditItemという名前の既存のSQL Serverストアドプロシージャを変更するnvarchar(50)移行方法の例を次に示します。 、それぞれ:

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
12
Jon Schneider

不十分に文書化されているようですが、 AlterStoredProcedureCreateStoredProcedureDropStoredProcedureMoveStoredProcedureRenameStoredProcedure Entity Framework6。私はまだ試していないので、それらの使用方法の例を与えることができません。

11
bbodenmiller

empのデザインはチャンピオンのように機能します!私は彼のパターンを使用していますが、DbContextクラス内のストアドプロシージャもマップします。これにより、SqlQuery()を使用する代わりにこれらのコンテキストメソッドを呼び出し、リポジトリから直接プロシージャを呼び出すことができます。アプリケーションが大きくなると事態が少し難しくなる可能性があるため、Seedメソッド内で、実際のストアドプロシージャのパラメーターカウントがマッピングメソッドのパラメーターカウントと一致することを確認するチェックを作成しました上記のDROPループempも更新しました。ドロップステートメント用に個別のフォルダー/ファイルを維持する代わりに、各sqlファイルの最初の行を読み取り、CREATEDROP(最初の行が常にCREATE PROCEDURE ProcNameであることを確認してください。このように、Updated-Databaseが実行されるたびに、StoredProcsフォルダー内のすべてのプロシージャが削除され、再作成されます。プロシージャが新しい場合プロシージャのパラメータカウントを機能させるには、ファイルの各行がBEGINまで読み込まれるため、tsqlをBEGIN/ENDブロックでラップする必要があります。 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]);
            }
        }

楽しい!

2
Natas0007

Ladislavが指摘したように、一般にDbContextはデータベース内のロジックを最小化する傾向がありますが、context.Database.ExecuteSqlCommand()またはcontext.Database.SqlQuery()を使用してカスタムSQLを実行することは可能です。