c# sql-server asp.net-core-mvc entity-framework-core ef-database-first

c# - ¿Cómo puedo llamar a un procedimiento almacenado de SQL usando EntityFramework 7 y Asp.Net 5?



sql-server asp.net-core-mvc (5)

Al igual que la respuesta anterior, simplemente puede usar FromSQL () en lugar de SqlQuery <> ().

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

Durante los últimos días, busqué algunos tutoriales sobre cómo llamar a un Stored Procedure desde un método de controlador de Web API usando EntityFramework 7 .

Todos los tutoriales que leí lo muestran al revés, es decir Code First enfoque Code First . Pero ya tengo una base de datos y necesito usarla para construir una Web API . Ya se han escrito varias lógicas comerciales como Procedimientos almacenados y Vistas y tengo que consumirlas desde mi API web.

Pregunta 1: ¿Es esto posible continuar con el enfoque de Database First con EF7 y consumir objetos de base de datos como el anterior?

Instalé EntityFramework 6.1.3 en mi paquete con el siguiente comando NuGet :

install-package EntityFramework que agrega la versión 6.1.3 a mi proyecto pero inmediatamente comienza a mostrarme un mensaje de error (vea la captura de pantalla a continuación). No tengo idea de cómo resolver esto.

Tengo otro proyecto de prueba donde en project.json puedo ver dos entradas como las siguientes:

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

Sin embargo, cuando estoy buscando en el administrador de paquetes Nu-Get , ¡no veo esta versión! Solo viene 6.1.3.

Mi objetivo principal es consumir vistas y procedimientos almacenados ya escritos de una base de datos existente.

1) No quiero usar ADO.Net , más bien me gustaría usar ORM usando EntityFramework

2) Si EntityFramework 6.1.3 tiene la capacidad de llamar a Stored Procs y Views Stored Procs desde una base de datos ya existente, ¿cómo puedo resolver el error (captura de pantalla)?

¿Cuál es la mejor práctica para lograr esto?


Espero haber entendido correctamente su problema. Tiene un PROCEDIMIENTO ALMACENADO existente, por ejemplo dbo.spGetSomeData , en la base de datos, que devuelve la lista de algunos elementos con algunos campos y necesita proporcionar los datos del método API web.

La implementación podría ser sobre lo siguiente. Puede definir un DbContext vacío como:

public class MyDbContext : DbContext { }

y para definir appsettings.json con la cadena de conexión a la base de datos

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

Debe usar Microsoft.Extensions.DependencyInjection para agregar MyDbContext al

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) { ... } }

Ahora puede implementar su acción WebApi de la siguiente manera:

[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; } } }

El código anterior solo se ejecuta usando exec dbo.spGetSomeData y usa dataRader para leer todos los resultados y guardarlos en dynamic objeto dynamic . Si realiza $.ajax llamada $.ajax desde api/My , obtendrá los datos devueltos por dbo.spGetSomeData , que puede usar directamente en el código JavaScript. El código anterior es muy transparente. Los nombres de los campos del conjunto de datos devueltos por dbo.spGetSomeData serán los nombres de las propiedades en el código JavaScript. No necesita administrar ninguna clase de entidad en su código C # de ninguna manera. Su código C # no tiene nombres de campos devueltos por el procedimiento almacenado. Por lo tanto, si desea ampliar / cambiar el código de dbo.spGetSomeData (cambiar el nombre de algunos campos, agregar nuevos campos), deberá ajustar solo su código JavaScript, pero no el código C #.


Para el primer enfoque de la base de datos, debe usar el comando Scaffold-DbContext

Instalar paquetes Nuget Microsoft.EntityFrameworkCore.Tools y Microsoft.EntityFrameworkCore.SqlServer.Design

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

pero eso no obtendrá sus procedimientos almacenados. Todavía está en proceso, rastreando el problema #245

Pero, para ejecutar los procedimientos almacenados, use el método FromSql que ejecuta consultas RAW SQL

p.ej

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

Para usar con parámetros

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

o

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

Existen ciertas limitaciones para ejecutar consultas RAW SQL o procedimientos almacenados. No puede usarlo para INSERT / UPDATE / DELETE. si desea ejecutar consultas INSERT, UPDATE, DELETE, use ExecuteSqlCommand

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


Uso del conector MySQL y Entity Framework core 2.0

Mi problema era que estaba recibiendo una excepción como fx. Ex.Message = "La columna requerida ''cuerpo'' no estaba presente en los resultados de una operación ''FromSql''". Por lo tanto, para recuperar filas a través de un procedimiento almacenado de esta manera, debe devolver todas las columnas para ese tipo de entidad con el que está asociado el DBSet, incluso si no necesita todos los datos para esta llamada específica.

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

O con parámetros

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


DbContext tiene una propiedad de Database , que mantiene una conexión con la base de datos con la que puede hacer lo que quiera con:

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

Sin embargo, en lugar de hacer esto en sus acciones de Web Api, sugeriría agregar un método a su contexto o cualquier servicio / repositorio que interactúe con su contexto. Entonces simplemente llame a este método en su acción. Idealmente, desea mantener todas sus cosas SQL en un solo lugar.