[英]How can I call a SQL Stored Procedure using EntityFramework 7 and Asp.Net 5
在过去的几天里,我正在寻找一些关于如何使用EntityFramework 7
从Web 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 Procs
和Views
,我如何解决错误(屏幕截图)?
实现这一目标的最佳做法是什么?
我希望我正确理解你的问题。 您在数据库中有现有的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.DependencyInjection
将MyDbContext
添加到
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.