如何使用 EntityFramework 7 和 Asp.Net 5 调用 SQL 存储过程
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:这完全有可能继续使用 Database First
方法和 EF7
并像上面那样使用数据库对象吗?
我通过以下 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 即将推出。
我的主要 objective 是使用现有数据库中已编写的存储过程和视图。
1) 我不想使用 ADO.Net
,而是想使用 ORM
使用 EntityFramework
2) 如果 EntityFramework 6.1.3
能够从现有数据库调用 Stored Procs
和 Views
,我该如何解决错误(屏幕截图)?
实现此目标的最佳做法是什么?
DbContext
有一个 Database
属性,它保持到数据库的连接,你可以用它做任何你想做的事:
context.Database.SqlQuery<Foo>("exec [dbo].[GetFoo] @Bar = {0}", bar);
但是,与其在您的 Web Api 操作中执行此操作,我建议将方法添加到您的上下文或任何与您的上下文交互的 service/repository。然后只需在您的操作中调用此方法即可。理想情况下,您希望将所有 SQL-stuff 放在一个地方。
希望我正确理解了你的问题。您在数据库中有现有的存储过程,例如 dbo.spGetSomeData
,其中 returns 具有某些字段的某些项目的列表,您需要从 Web API 方法提供数据。
实施可能与以下内容有关。您可以定义一个 empty 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# 代码没有从存储过程返回的字段名称。因此,如果您要 extend/change dbo.spGetSomeData
的代码(重命名一些字段,添加新字段),您将只需要调整 JavaScript 代码,而不需要调整 C# 代码。
正如上面的答案,您可以简单地使用 FromSQL() 而不是 SqlQuery<>()。
context.Set().FromSql("[dbo].[GetFoo] @Bar = {0}", 45);
对于数据库优先方法,您必须使用 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 查询或存储的 procedures.You 不能用于 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 = "The required column 'body' was not present in the results of a 'FromSql' operation."。
因此,为了以这种方式通过存储过程获取行,您必须 return 与 DBSet 关联的实体类型的所有列,即使您不需要此特定调用的所有数据。
var result = _context.DBSetName.FromSql($"call storedProcedureName()").ToList();
或带参数
var result = _context.DBSetName.FromSql($"call storedProcedureName({optionalParam1})").ToList();
最近几天,我正在搜索一些关于如何使用 EntityFramework 7
.
Web API
控制器方法内部调用 Stored Procedure
的教程
我学习的所有教程都以相反的方式展示它,即 Code First
方法。但是我已经有了一个数据库,我需要用它来构建一个 Web API
。各种业务逻辑已经编写为存储过程和视图,我必须从我的 Web API.
问题 1:这完全有可能继续使用 Database First
方法和 EF7
并像上面那样使用数据库对象吗?
我通过以下 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 即将推出。
我的主要 objective 是使用现有数据库中已编写的存储过程和视图。
1) 我不想使用 ADO.Net
,而是想使用 ORM
使用 EntityFramework
2) 如果 EntityFramework 6.1.3
能够从现有数据库调用 Stored Procs
和 Views
,我该如何解决错误(屏幕截图)?
实现此目标的最佳做法是什么?
DbContext
有一个 Database
属性,它保持到数据库的连接,你可以用它做任何你想做的事:
context.Database.SqlQuery<Foo>("exec [dbo].[GetFoo] @Bar = {0}", bar);
但是,与其在您的 Web Api 操作中执行此操作,我建议将方法添加到您的上下文或任何与您的上下文交互的 service/repository。然后只需在您的操作中调用此方法即可。理想情况下,您希望将所有 SQL-stuff 放在一个地方。
希望我正确理解了你的问题。您在数据库中有现有的存储过程,例如 dbo.spGetSomeData
,其中 returns 具有某些字段的某些项目的列表,您需要从 Web API 方法提供数据。
实施可能与以下内容有关。您可以定义一个 empty 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# 代码没有从存储过程返回的字段名称。因此,如果您要 extend/change dbo.spGetSomeData
的代码(重命名一些字段,添加新字段),您将只需要调整 JavaScript 代码,而不需要调整 C# 代码。
正如上面的答案,您可以简单地使用 FromSQL() 而不是 SqlQuery<>()。
context.Set().FromSql("[dbo].[GetFoo] @Bar = {0}", 45);
对于数据库优先方法,您必须使用 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 查询或存储的 procedures.You 不能用于 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 = "The required column 'body' was not present in the results of a 'FromSql' operation."。 因此,为了以这种方式通过存储过程获取行,您必须 return 与 DBSet 关联的实体类型的所有列,即使您不需要此特定调用的所有数据。
var result = _context.DBSetName.FromSql($"call storedProcedureName()").ToList();
或带参数
var result = _context.DBSetName.FromSql($"call storedProcedureName({optionalParam1})").ToList();