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

后端 未结 5 481
夕颜
夕颜 2020-11-29 10:48

For last couple of days I am searching for some tutorials about how to call a Stored Procedure from inside a Web API controller method using

相关标签:
5条回答
  • 2020-11-29 11:08

    For Database first approach , you have to use Scaffold-DbContext command

    Install Nuget packages Microsoft.EntityFrameworkCore.Tools and Microsoft.EntityFrameworkCore.SqlServer.Design

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

    but that will not get your stored procedures. It is still in the works,tracking issue #245

    But, To execute the stored procedures, use FromSql method which executes RAW SQL queries

    e.g.

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

    To use with parameters

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

    or

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

    There are certain limitations to execute RAW SQL queries or stored procedures.You can’t use it for INSERT/UPDATE/DELETE. if you want to execute INSERT, UPDATE, DELETE queries, use the ExecuteSqlCommand

    var categoryName = "Electronics";
    dataContext.Database
               .ExecuteSqlCommand("dbo.InsertCategory @p0", categoryName);
    
    0 讨论(0)
  • 2020-11-29 11:12

    DbContext has a Database property, which holds a connection to the database that you can do whatever you want with:

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

    However, rather than doing this in your Web Api actions, I would suggest either adding a method to your context or to whatever service/repository that interacts with your context. Then just call this method in your action. Ideally, you want to keep all your SQL-stuff in one place.

    0 讨论(0)
  • 2020-11-29 11:17

    I hope that I correctly understand your problem. You have existing STORED PROCEDURE, for example dbo.spGetSomeData, in the database, which returns the list of some items with some fields and you need to provide the data from Web API method.

    The implementation could be about the following. You can define an empty DbContext like:

    public class MyDbContext : DbContext
    {
    }
    

    and to define appsettings.json with the connection string to the database

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

    You should use Microsoft.Extensions.DependencyInjection to add MyDbContext to the

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

    Now you can implement your WebApi action as the following:

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

    The above code just execute using exec dbo.spGetSomeData and use dataRader to read all results and save there in dynamic object. If you would make $.ajax call from api/My you will get the data returned from dbo.spGetSomeData, which you can directly use in JavaScript code. The above code is very transparent. The names of the fields from the dataset returned by dbo.spGetSomeData will be the names of the properties in the JavaScript code. You don't need to manage any entity classes in your C# code in any way. Your C# code have no names of fields returned from the stored procedure. Thus if you would extend/change the code of dbo.spGetSomeData (rename some fields, add new fields) you will need to adjust only your JavaScript code, but no C# code.

    0 讨论(0)
  • 2020-11-29 11:22

    Using MySQL connector and Entity Framework core 2.0

    My issue was that I was getting an exception like fx. Ex.Message = "The required column 'body' was not present in the results of a 'FromSql' operation.". So, in order to fetch rows via a stored procedure in this manner, you must return all columns for that entity type which the DBSet is associated with, even if you don't need all the data for this specific call.

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

    OR with parameters

    var result = _context.DBSetName.FromSql($"call storedProcedureName({optionalParam1})").ToList(); 
    
    0 讨论(0)
  • 2020-11-29 11:27

    Just as the above answer, you could simply use the FromSQL() instead of SqlQuery<>().

    context.Set().FromSql("[dbo].[GetFoo] @Bar = {0}", 45);
    
    0 讨论(0)
提交回复
热议问题