繁体   English   中英

如何使用EntityFramework 7和Asp.Net 5调用SQL存储过程

[英]How can I call a SQL Stored Procedure using EntityFramework 7 and Asp.Net 5

在过去的几天里,我正在寻找一些关于如何使用EntityFramework 7Web API控制器方法中调用Stored Procedure教程。

我通过的所有教程都反过来展示了它,即Code First方法。 但我已经有了一个数据库,我需要用它来构建一个Web API 各种业务逻辑已经编写为存储过程和视图,我必须使用来自Web API的那些逻辑。

问题1:这是否可以继续使用EF7 Database First方法并使用上面的数据库对象?

我通过以下NuGet命令将EntityFramework 6.1.3安装到我的包中:

install-package EntityFramework ,它将版本6.1.3添加到我的项目中,但立即开始显示错误消息(请参阅下面的屏幕截图)。 我不清楚如何解决这个问题。

在此输入图像描述

我有另一个测试项目,在project.json我可以看到两个如下的条目:

"EntityFramework.MicrosoftSqlServer": "7.0.0-rc1-final", "EntityFramework.MicrosoftSqlServer.Design": "7.0.0-rc1-final",

但是,当我在Nu-Get包管理器中搜索时,我不会看到这个版本! 只有6.1.3即将到来。

我的主要目标是从现有数据库中使用已编写的存储过程和视图。

1)我不想使用ADO.Net ,而是想使用EntityFramework来使用ORM

2)如果EntityFramework 6.1.3能够从现有数据库调用Stored ProcsViews ,我如何解决错误(屏幕截图)?

实现这一目标的最佳做法是什么?

我希望我正确理解你的问题。 您在数据库中有现有的STORED PROCEDURE,例如dbo.spGetSomeData ,它返回包含某些字段的某些项的列表,您需要从Web API方法提供数据。

实施可以是以下几点。 您可以定义一个空的 DbContext如:

public class MyDbContext : DbContext
{
}

并使用数据库的连接字符串定义appsettings.json

{
  "Data": {
    "DefaultConnection": {
      "ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=MyDb;Trusted_Connection=True;MultipleActiveResultSets=true"
    }
  }
}

您应该使用Microsoft.Extensions.DependencyInjectionMyDbContext添加到

public class Startup
{
    // property for holding configuration
    public IConfigurationRoot Configuration { get; set; }

    public Startup(IHostingEnvironment env)
    {
        // Set up configuration sources.
        var builder = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json")
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
            .AddEnvironmentVariables();
        // save the configuration in Configuration property
        Configuration = builder.Build();
    }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        // Add framework services.
        services.AddMvc()
            .AddJsonOptions(options => {
                options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
            });

        services.AddEntityFramework()
            .AddSqlServer()
            .AddDbContext<MyDbContext>(options => {
                options.UseSqlServer(Configuration["ConnectionString"]);
            });
    }
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        ...
    }
}

现在,您可以按以下方式实施WebApi操作:

[Route("api/[controller]")]
public class MyController : Controller
{
    public MyDbContext _context { get; set; }

    public MyController([FromServices] MyDbContext context)
    {
        _context = context;
    }

    [HttpGet]
    public async IEnumerable<object> Get()
    {
        var returnObject = new List<dynamic>();

        using (var cmd = _context.Database.GetDbConnection().CreateCommand()) {
            cmd.CommandText = "exec dbo.spGetSomeData";
            cmd.CommandType = CommandType.StoredProcedure;
            // set some parameters of the stored procedure
            cmd.Parameters.Add(new SqlParameter("@someParam",
                SqlDbType.TinyInt) { Value = 1 });

            if (cmd.Connection.State != ConnectionState.Open)
                cmd.Connection.Open();

            var retObject = new List<dynamic>();
            using (var dataReader = await cmd.ExecuteReaderAsync())
            {
                while (await dataReader.ReadAsync())
                {
                    var dataRow = new ExpandoObject() as IDictionary<string, object>;
                    for (var iFiled = 0; iFiled < dataReader.FieldCount; iFiled++) {
                        // one can modify the next line to
                        //   if (dataReader.IsDBNull(iFiled))
                        //       dataRow.Add(dataReader.GetName(iFiled), dataReader[iFiled]);
                        // if one want don't fill the property for NULL
                        // returned from the database
                        dataRow.Add(
                            dataReader.GetName(iFiled),
                            dataReader.IsDBNull(iFiled) ? null : dataReader[iFiled] // use null instead of {}
                        );
                    }

                    retObject.Add((ExpandoObject)dataRow);
                }
            }
            return retObject;
        }
    }
}

上面的代码只使用exec dbo.spGetSomeData执行,并使用dataRader读取所有结果并保存在dynamic对象中。 如果您要从api/My进行$.ajax调用,您将获得从dbo.spGetSomeData返回的数据,您可以直接在JavaScript代码中使用它。 上面的代码非常透明。 dbo.spGetSomeData返回的数据集中的字段名称将是JavaScript代码中属性的名称。 您无需以任何方式管理C#代码中的任何实体类。 您的C#代码没有从存储过程返回的字段名称。 因此,如果您要扩展/更改dbo.spGetSomeData的代码(重命名某些字段,添加新字段),您将只需要调整JavaScript代码,但不需要调整C#代码。

DbContext有一个Database属性,它保存与数据库的连接,您可以随意执行以下操作:

context.Database.SqlQuery<Foo>("exec [dbo].[GetFoo] @Bar = {0}", bar);

但是,我建议您不要在Web Api操作中执行此操作,而是向您的上下文或与您的上下文交互的任何服务/存储库添加方法。 然后在您的操作中调用此方法。 理想情况下,您希望将所有SQL内容保存在一个位置。

正如上面的答案,你可以简单地使用FromSQL()而不是SqlQuery <>()。

context.Set().FromSql("[dbo].[GetFoo] @Bar = {0}", 45);

使用MySQL连接器和Entity Framework核心2.0

我的问题是我得到了像fx这样的例外。 Ex.Message =“'FromSql'操作的结果中没有出现所需的列'body'。” 因此,为了以这种方式通过存储过程获取行,您必须返回与DBSet关联的实体类型的所有列,即使您不需要此特定调用的所有数据。

var result = _context.DBSetName.FromSql($"call storedProcedureName()").ToList(); 

或与参数

var result = _context.DBSetName.FromSql($"call storedProcedureName({optionalParam1})").ToList(); 

对于数据库第一种方法,您必须使用Scaffold-DbContext命令

安装Nuget包Microsoft.EntityFrameworkCore.Tools和Microsoft.EntityFrameworkCore.SqlServer.Design

Scaffold-DbContext "Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models

但这不会得到您的存储过程。 它仍处于工作中,跟踪问题#245

但是,要执行存储过程,请使用执行RAW SQL查询的FromSql方法

例如

var products= context.Products
    .FromSql("EXECUTE dbo.GetProducts")
    .ToList();

用于参数

var productCategory= "Electronics";

var product = context.Products
    .FromSql("EXECUTE dbo.GetProductByCategory {0}", productCategory)
    .ToList();

要么

var productCategory= new SqlParameter("productCategory", "Electronics");

var product = context.Product
    .FromSql("EXECUTE dbo.GetProductByName  @productCategory", productCategory)
    .ToList();

执行RAW SQL查询或存储过程有一些限制。您不能将它用于INSERT / UPDATE / DELETE。 如果要执行INSERT,UPDATE,DELETE查询,请使用ExecuteSqlCommand

var categoryName = "Electronics";
dataContext.Database
           .ExecuteSqlCommand("dbo.InsertCategory @p0", categoryName);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM