您可以使用实体框架 4.1 代码优先方法创建 sql 视图/存储过程吗

发布于 2024-12-08 01:44:43 字数 97 浏览 1 评论 0原文

Entity Framework 4.1 Code First 非常适合创建表和关系。是否可以使用代码优先方法创建 sql 视图或存储过程?任何有关此的指示都将受到高度赞赏。多谢!

Entity Framework 4.1 Code First works great creating tables and relationships. Is it possible to create sql views or stored procedure using Code first approach? Any pointers regarding this will be highly appreciated. Thanks a lot!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

救星 2024-12-15 01:44:43

我们在实体框架代码优先迁移中支持存储过程。我们的方法是创建一些文件夹来保存 .sql 文件(例如 ~/Sql/)。在该文件夹中创建 .sql 文件以用于创建和删除存储过程。例如Create_sp_DoSomething.sqlDrop_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]

We support stored procedures in our Entity Framework Code First Migrations. Our approach is to create some folder to hold the .sql files (~/Sql/ for example). Create .sql files in the folder for both creating and dropping the stored procedure. E.g. Create_sp_DoSomething.sql and Drop_sp_DoSomething. Because the SQL runs in a batch and CREATE PROCEDURE.. must be the first statement in a batch, make the CREATE PROCEDURE... the first statement in the file. Also, don't put GO after the DROP.... Add a resources file to your project, if you don't have one already. Drag the .sql files from solution explorer into the Files view of the Resources designer. Now create an empty migration (Add-Migration SomethingMeaningful_sp_DoSomething) and use:

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]
皓月长歌 2024-12-15 01:44:43

乍一看,我真的很喜欢 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() 执行。

我当前的目录布局如下:

项目.DAL
+ 迁移
+ SQL
++ 种子
+++ dbo.cleanDb.sql
++ 存储过程
+++ dbo.sp_GetSomething.sql

现在您只需将额外的存储过程放入文件夹中,所有内容都会得到适当更新。

At first sight I really like the approach of Carl G but it involves a lot of manual interaction. In my scenario, I always drop all stored procedures, views... and recreate them whenever there is a change in the database. This way we are sure everything is up-to-date with the latest version.

Recreation happens by setting the following Initializer:

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

Then our seed method will get called whenever there is a migration ready

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 Statements are stored in *.sql files for easy editing. Make sure your files have "Build Action" set to "Content" and "Copy to Output Directory" set to "Copy Always". We lookup the folders and execute all scripts inside. Don't forget to exclude "GO" statements in your SQL because they cannot be executed with ExecuteSqlCommand().

My current directory layout is as follows:

Project.DAL
+ Migrations
+ Sql
++ Seed
+++ dbo.cleanDb.sql
++ StoredProcs
+++ dbo.sp_GetSomething.sql

Now you just need to drop extra stored procedures in the folder and everything will get updated appropriately.

眼眸 2024-12-15 01:44:43

要扩展 bbodenmiller 的答案,在 Entity Framework 6 中,DbMigration 类 具有诸如 AlterStoredProcedure 之类的方法,这些方法允许修改存储过程,而不必一直下降到原始 SQL。

下面是 Up() 迁移方法的示例,该方法更改名为 EditItem 的现有 SQL Server 存储过程,该过程采用三个类型为 intnvarchar(50)< 的参数。分别是 /code> 和 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

To expand on bbodenmiller's answer, In Entity Framework 6, the DbMigration class has methods such as AlterStoredProcedure which allow for modification of stored procedures without having to drop all the way down to raw SQL.

Here's an example of an Up() migration method which alters an existing SQL Server stored procedure named EditItem which takes three parameters of type int, nvarchar(50), and smallmoney, respectively:

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

    //...
}

On my machine, this migration script produces the following SQL:

ALTER PROCEDURE [dbo].[EditItem]
    @ItemID [int],
    @ItemName [nvarchar](50),
    @ItemCost [smallmoney]
AS
BEGIN
    (Stored procedure body SQL goes here)
END
烟雨凡馨 2024-12-15 01:44:43

EF 代码优先方法期望数据库中没有逻辑。这意味着没有存储过程,也没有数据库视图。因为代码优先方法不提供任何机制来自动为您生成此类构造。如果这意味着生成逻辑,它怎么能做到这一点呢?

您必须通过手动执行在自定义数据库初始值设定项中自行创建它们创建脚本。我不认为这种自定义 SQL 构造可以通过 SQL 迁移来处理。

EF code-first approach expects that there is no logic in the database. That means no stored procedures and no database views. Because of that code-first approach doesn't provide any mechanism to generate such constructs automatically for you. How could it do that if it means generating logic?

You must create them yourselves in custom database initializer by manual executing creation scripts. I don't think that this custom SQL constructs can be handled by SQL migrations.

鸠魁 2024-12-15 01:44:43

似乎记录很少,但是您现在可以使用 AlterStoredProcedure, CreateStoredProcedure, DropStoredProcedure, MoveStoredProcedure, 实体框架 6 中的RenameStoredProcedure我还没有尝试过,所以还不能给出如何使用它们的例子。

It appears to be poorly documented however it appears you can now do some Stored Procedure manipulation using AlterStoredProcedure, CreateStoredProcedure, DropStoredProcedure, MoveStoredProcedure, RenameStoredProcedure in Entity Framework 6. I haven't tried them yet so can't yet give an example of how to use them.

安穩 2024-12-15 01:44:43

emp 的设计就像冠军一样!我正在使用他的模式,但我还在 DbContext 类中映射存储过程,该类允许简单地调用这些上下文方法,而不是使用 SqlQuery() 并直接从我的存储库调用过程。由于随着应用程序的增长,事情可能会变得有点棘手,因此我在 Seed 方法中创建了一个检查,以确保实际的存储过程参数计数与映射方法上的参数计数相匹配。我还更新了提到的 DROP 循环 emp。我不必为 drop 语句维护单独的文件夹/文件,只需读取每个 sql 文件的第一行并将 CREATE 替换为 DROP (只需确保第一个行始终只是CREATE PROCEDURE ProcName)。这样,每次运行 Update-Database 时,StoredProcs 文件夹中的所有过程都会被删除并重新创建。该 drop 也包含在 try-catch 块中,以防过程是新的。为了使过程参数计数起作用,您需要确保在 tsql 周围包裹一个 BEGIN/END 块,因为文件的每一行都会被读取到 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]);
            }
        }

享受!

emp's design works like a champion! I'm using his pattern but I also map stored procedures inside of my DbContext class which allows simply calling those context methods instead of using SqlQuery() and calling the procedures directly from my repository. As things can get a bit hairy when the application grows, I've created a check within my Seed method that makes sure the actual stored procedure parameter count match up to the parameter count on the mapping method. I've also updated the DROP loop emp mentioned. Instead of having to maintain a separate folder/file for the drop statements, I simply read the first line of each sql file and replace CREATE with DROP (just make sure the first line is always just CREATE PROCEDURE ProcName). This way all procedures in my StoredProcs folder get dropped and recreated each time Update-Database is ran. The drop is also wrapped in a try-catch block in case the procedure is new. For the procedure parameter count to work, you'll need to make sure you wrap a BEGIN/END block around your tsql since each line of the file is read up to BEGIN. Also make sure each sp parameter is on new line.

        // 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]);
            }
        }

Enjoy!

爱要勇敢去追 2024-12-15 01:44:43

正如 Ladislav 指出的那样,DbContext 通常确实倾向于最小化数据库中的逻辑,但可以通过使用 context.Database.ExecuteSqlCommand()context.Database.SqlQuery()

As Ladislav pointed out, DbContext in general does tend to minimize the logic in the database, but it is possible to execute custom SQL by using context.Database.ExecuteSqlCommand() or context.Database.SqlQuery().

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文