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

在过去的几天里,我正在寻找一些关于如何使用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(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 Get() { var returnObject = new List(); 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(); using (var dataReader = await cmd.ExecuteReaderAsync()) { while (await dataReader.ReadAsync()) { var dataRow = new ExpandoObject() as IDictionary; 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("exec [dbo].[GetFoo] @Bar = {0}", bar); 

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

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

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

对于Database first方法,您必须使用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); 

使用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();