【问题标题】:How to run stored procedures in Entity Framework Core?如何在 Entity Framework Core 中运行存储过程?
【发布时间】:2015-02-19 05:45:52
【问题描述】:

我在 ASP.NET Core 应用程序中使用 EF Core 1.0。您能否指出执行存储过程的正确方法? ObjectParameters((IObjectContextAdapter)this).ObjectContext.ExecuteFunction 的旧方法不起作用。

【问题讨论】:

  • 您需要运行 SP 并得到一些 DbSet 的结果吗?还是直接运行?
  • 我需要找回结果!

标签: asp.net-core asp.net-core-mvc entity-framework-core


【解决方案1】:

现在解决了 EF Core 1.0 中对存储过程的支持,这也支持多个结果集的映射。

Check here for the fix details

你可以在c#中这样称呼它

var userType = dbContext.Set().FromSql("dbo.SomeSproc @Id = {0}, @Name = {1}", 45, "Ada");

【讨论】:

  • 在这里找到另一个很好的例子 - dotnetjalps.com/2015/11/… 这是不言自明的。
  • 尚不支持多个数据集。见github.com/aspnet/EntityFramework/issues/…
  • 我的 2 美分:var UserId =1; _dbCtx.Set().FromSql($"dbo.StoredProcedureName @UserId= {UserId}"); //只是语法糖...
  • 如果你喜欢我找不到fromSql,请看这个:dotnetthoughts.net/how-to-execute-storedprocedure-in-ef-core“要获取FromSql命令,你需要在你的project.json文件中添加“Microsoft.EntityFrameworkCore.Relational”包的引用。 "还使用 Microsoft.EntityFrameworkCore 添加;
  • 遗憾的是,您似乎无法在 EF Core 1.1 中使用 Set()。只需获取The arguments for DbContext.Set<TEntity>() cannot be inferred from the usage
【解决方案2】:

EF7 中尚未实现存储过程支持(截至 7.0.0-beta3)。您可以使用 issue #245 跟踪此功能的进度。

现在,您可以使用 ADO.NET 以老式的方式进行操作。

var connection = (SqlConnection)context.Database.AsSqlServer().Connection.DbConnection;

var command = connection.CreateCommand();
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "MySproc";
command.Parameters.AddWithValue("@MyParameter", 42);

command.ExecuteNonQuery();

【讨论】:

  • 那是#624。 ;) 您始终可以在 ADO.NET 连接上执行此操作:context.Database.AsRelational().Connection.DbConnection
  • @JimWooley 这似乎有点不道德......但是是的,现在使用FromSql
  • @JimWooley LOL,不确定您是否意识到这一点,但您否决了 EF 团队中一位主要开发人员的回答... :)
  • @RichardMarskell-Drackir StackOverflow 的答案在很大程度上是一个时间点的事情。您真的希望每个人在剩下的时间里保持所有答案都是最新的吗?提问者应该选择不同的答案,或者社区应该支持出现的更好的答案。否决票的错误形式。
  • @bricelam - 我不同意答案应该被视为时间点。我偶尔会回到旧的高投票答案,看看是否有任何变化,所以新的搜索仍然相关,但我意识到并不是每个人都这样做。很多时候,我从搜索中来到 SO 并开始使用接受的答案,只是后来才发现有一个更好的答案。我同意如果提问者更新答案会很好,但他们很可能会得到他们想要的并继续前进。无论如何,我认为来自谷歌的人知道有更好的方法来做到这一点会很有帮助。 :)
【解决方案3】:

要执行存储过程,请使用执行 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);

【讨论】:

  • 方法ExecuteSqlCommand 现已过时。请参阅ExecuteSqlCommand Method 可能的替代方案是ExecuteSqlRawExecuteSqlInterpolated
【解决方案4】:

EF Core 对存储过程的支持与早期版本的 EF Code 类似。

您需要通过从 EF 继承 DbContext 类来创建您的 DbContext 类。存储过程使用 DbContext 执行。

第一步是编写一个从 DbContext 创建 DbCommand 的方法。

public static DbCommand LoadStoredProc(
  this DbContext context, string storedProcName)
{
  var cmd = context.Database.GetDbConnection().CreateCommand();
  cmd.CommandText = storedProcName;
  cmd.CommandType = System.Data.CommandType.StoredProcedure;
  return cmd;
}

要将参数传递给存储过程,请使用以下方法。

public static DbCommand WithSqlParam(
  this DbCommand cmd, string paramName, object paramValue)
{
  if (string.IsNullOrEmpty(cmd.CommandText))
    throw new InvalidOperationException(
      "Call LoadStoredProc before using this method");
  var param = cmd.CreateParameter();
  param.ParameterName = paramName;
  param.Value = paramValue;
  cmd.Parameters.Add(param);
  return cmd;
}

最后,使用 MapToList 方法将结果映射到自定义对象列表中。

private static List<T> MapToList<T>(this DbDataReader dr)
{
  var objList = new List<T>();
  var props = typeof(T).GetRuntimeProperties();

  var colMapping = dr.GetColumnSchema()
    .Where(x => props.Any(y => y.Name.ToLower() == x.ColumnName.ToLower()))
    .ToDictionary(key => key.ColumnName.ToLower());

  if (dr.HasRows)
  {
    while (dr.Read())
    {
      T obj = Activator.CreateInstance<T>();
      foreach (var prop in props)
      {
        var val = 
          dr.GetValue(colMapping[prop.Name.ToLower()].ColumnOrdinal.Value);
          prop.SetValue(obj, val == DBNull.Value ? null : val);
      }
      objList.Add(obj);
    }
  }
  return objList;
}

现在我们准备好使用 ExecuteStoredProc 方法执行存储过程,并将其映射到类型为 T 传入的 List。

public static async Task<List<T>> ExecuteStoredProc<T>(this DbCommand command)
{
  using (command)
  {
    if (command.Connection.State == System.Data.ConnectionState.Closed)
    command.Connection.Open();
    try
    {
      using (var reader = await command.ExecuteReaderAsync())
      {
        return reader.MapToList<T>();
      }
    }
    catch(Exception e)
    {
      throw (e);
    }
    finally
    {
      command.Connection.Close();
    }
  }
}

例如,要使用两个参数“firstparamname”和“secondparamname”执行一个名为“StoredProcedureName”的存储过程,这就是实现。

List<MyType> myTypeList = new List<MyType>();
using(var context = new MyDbContext())
{
  myTypeList = context.LoadStoredProc("StoredProcedureName")
  .WithSqlParam("firstparamname", firstParamValue)
  .WithSqlParam("secondparamname", secondParamValue).
  .ExecureStoredProc<MyType>();
}

【讨论】:

    【解决方案5】:

    我尝试了所有其他解决方案,但都没有为我工作。但我找到了一个合适的解决方案,它可能对这里的人有所帮助。

    要调用存储过程并将结果放入 EF Core 中的模型列表中,我们必须遵循 3 个步骤。

    第 1 步。 您需要添加一个新类,就像您的实体类一样。哪个应该具有您的 SP 中所有列的属性。例如,如果您的 SP 返回两个名为 IdName 的列,那么您的新类应该类似于

    public class MySPModel
    {
        public int Id {get; set;}
        public string Name {get; set;}
    }
    

    第 2 步。

    然后您必须为您的 SP 将一个 DbQuery 属性添加到您的 DBContext 类中。

    public partial class Sonar_Health_AppointmentsContext : DbContext
    {
            public virtual DbSet<Booking> Booking { get; set; } // your existing DbSets
            ...
    
            public virtual DbQuery<MySPModel> MySP { get; set; } // your new DbQuery
            ...
    }
    

    第 3 步。

    现在您将能够从您的 DBContext 调用您的 SP 并获取结果。

    var result = await _context.Query<MySPModel>().AsNoTracking().FromSql(string.Format("EXEC {0} {1}", functionName, parameter)).ToListAsync();
    

    我使用的是通用 UnitOfWork & Repository。所以我执行 SP 的功能是

    /// <summary>
    /// Execute function. Be extra care when using this function as there is a risk for SQL injection
    /// </summary>
    public async Task<IEnumerable<T>> ExecuteFuntion<T>(string functionName, string parameter) where T : class
    {
        return await _context.Query<T>().AsNoTracking().FromSql(string.Format("EXEC {0} {1}", functionName, parameter)).ToListAsync();
    }
    

    希望对某人有所帮助!!!

    【讨论】:

    • 我希望避免这样做。我编写了自己的库(前 EF)来处理动态数据集(例如连接等),然后完美地工作。每个人都在抱怨,所以我搬到了 EF,你现在不能这样做。你猜怎么着,他们让我重写我的库。我的反应并不愉快。
    【解决方案6】:

    "(SqlConnection)context" -- 这种类型转换不再有效。你可以这样做:"SqlConnection context;

    ".AsSqlServer()" -- 不存在。

    "command.ExecuteNonQuery();" -- 不返回结果。 reader=command.ExecuteReader() 确实有效。

    使用 dt.load(reader)... 那么您必须将框架从 5.0 切换回 4.51,因为 5.0 尚不支持数据表/数据集。注意:这是 VS2015 RC。

    【讨论】:

      【解决方案7】:

      ExecuteSqlCommandExecuteSqlCommandAsync 遇到了很多麻烦,IN 参数很容易,但 OUT 参数很难。

      我不得不像这样恢复使用DbCommand -

      DbCommand cmd = _context.Database.GetDbConnection().CreateCommand();
      
      cmd.CommandText = "dbo.sp_DoSomething";
      cmd.CommandType = CommandType.StoredProcedure;
      
      cmd.Parameters.Add(new SqlParameter("@firstName", SqlDbType.VarChar) { Value = "Steve" });
      cmd.Parameters.Add(new SqlParameter("@lastName", SqlDbType.VarChar) { Value = "Smith" });
      
      cmd.Parameters.Add(new SqlParameter("@id", SqlDbType.BigInt) { Direction = ParameterDirection.Output });
      

      我在this post 中写了更多关于它的内容。

      【讨论】:

        【解决方案8】:

        目前 EF 7 或 EF Core 不支持在设计器中导入存储过程并直接调用它们的旧方法。您可以查看路线图以了解未来将支持的内容: EF core roadmap.

        所以现在最好使用 SqlConnection 来调用存储过程或任何原始查询,因为您不需要整个 EF 来完成这项工作。这里有两个例子:

        调用返回单个值的存储过程。在这种情况下为字符串。

        CREATE PROCEDURE [dbo].[Test]
            @UserName nvarchar(50)
        AS
        BEGIN
            SELECT 'Name is: '+@UserName;
        END
        

        调用返回列表的存储过程。

        CREATE PROCEDURE [dbo].[TestList]
        AS
        BEGIN
            SELECT [UserName], [Id] FROM [dbo].[AspNetUsers]
        END
        

        要调用这些存储过程,最好创建一个包含所有这些函数的静态类,例如,我称之为DataAccess类,如下:

        public static class DataAccess
        
            {
                private static string connectionString = ""; //Your connection string
                public static string Test(String userName)
                {
                    using (SqlConnection conn = new SqlConnection(connectionString))
                    {
                        conn.Open();
        
                        // 1.  create a command object identifying the stored procedure
                        SqlCommand cmd = new SqlCommand("dbo.Test", conn);
        
                        // 2. set the command object so it knows to execute a stored procedure
                        cmd.CommandType = CommandType.StoredProcedure;
        
                        // 3. add parameter to command, which will be passed to the stored procedure
                        cmd.Parameters.Add(new SqlParameter("@UserName", userName));
        
                        // execute the command
                        using (var rdr = cmd.ExecuteReader())
                        {
                            if (rdr.Read())
                            {
                                return rdr[0].ToString();
                            }
                            else
                            {
                                return null;
                            }
                        }
                    }
                }
        
                public static IList<Users> TestList()
                {
                    using (SqlConnection conn = new SqlConnection(connectionString))
                    {
                        conn.Open();
        
                        // 1.  create a command object identifying the stored procedure
                        SqlCommand cmd = new SqlCommand("dbo.TestList", conn);
        
                        // 2. set the command object so it knows to execute a stored procedure
                        cmd.CommandType = CommandType.StoredProcedure;
        
                        // execute the command
                        using (var rdr = cmd.ExecuteReader())
                        {
                            IList<Users> result = new List<Users>();
                            //3. Loop through rows
                            while (rdr.Read())
                            {
                                //Get each column
                                result.Add(new Users() { UserName = (string)rdr.GetString(0), Id = rdr.GetString(1) });
                            }
                            return result;
                        }
                    }
        
                }
            }
        

        而用户类是这样的:

        public class Users
        {
             public string UserName { set; get; }
             public string Id { set; get; }
        }
        

        顺便说一句,您无需担心为每个对 sql 的请求打开和关闭连接的性能,因为 asp.net 正在为您管理这些。 我希望这会有所帮助。

        【讨论】:

          【解决方案9】:

          我发现这个扩展非常有用:StoredProcedureEFCore

          那么用法是这样的

          List<Model> rows = null;
          
          ctx.LoadStoredProc("dbo.ListAll")
             .AddParam("limit", 300L)
             .AddParam("limitOut", out IOutParam<long> limitOut)
             .Exec(r => rows = r.ToList<Model>());
          
          long limitOutValue = limitOut.Value;
          
          ctx.LoadStoredProc("dbo.ReturnBoolean")
             .AddParam("boolean_to_return", true)
             .ReturnValue(out IOutParam<bool> retParam)
             .ExecNonQuery();
          
          bool b = retParam.Value;
          
          ctx.LoadStoredProc("dbo.ListAll")
             .AddParam("limit", 1L)
             .ExecScalar(out long l);
          

          【讨论】:

            【解决方案10】:

            由于我的团队已经同意我们将使用通用 UnitOfWork 模式,所以我在创建自己的解决方案时采取了一些大家的解决方案。

            我还发布了一些 UnitOfWork 代码,以便您了解我为什么需要这样实现它。

            public interface IUnitOfWork : IDisposable
            {
                DbContext Context { get; }
                Task<List<T>> ExecuteStoredProc<T>(string storedProcName, Dictionary<string, object> procParams) where T : class;
            }
            

            接口实现:

            public class UnitOfWork : IUnitOfWork
            {
                public DbContext Context { get; private set; }
            
            /// <summary>
            /// Execute procedure from database using it's name and params that is protected from the SQL injection attacks.
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="storedProcName">Name of the procedure that should be executed.</param>
            /// <param name="procParams">Dictionary of params that procedure takes. </param>
            /// <returns>List of objects that are mapped in T type, returned by procedure.</returns>
                public async Task<List<T>> ExecuteStoredProc<T>(string storedProcName, Dictionary<string, object> procParams) where T : class
                {
                    DbConnection conn = Context.Database.GetDbConnection();
                    try
                    {
                        if(conn.State != ConnectionState.Open)
                            await conn.OpenAsync();
            
                        await using (DbCommand command = conn.CreateCommand())
                        {
                            command.CommandText = storedProcName;
                            command.CommandType = CommandType.StoredProcedure;
            
                            foreach (KeyValuePair<string, object> procParam in procParams)
                            {
                                DbParameter param = command.CreateParameter();
                                param.ParameterName = procParam.Key;
                                param.Value = procParam.Value;
                                command.Parameters.Add(param);
                            }
            
                            DbDataReader reader = await command.ExecuteReaderAsync();
                            List<T> objList = new List<T>();
                            IEnumerable<PropertyInfo> props = typeof(T).GetRuntimeProperties();
                            Dictionary<string, DbColumn> colMapping = reader.GetColumnSchema()
                                .Where(x => props.Any(y => y.Name.ToLower() == x.ColumnName.ToLower()))
                                .ToDictionary(key => key.ColumnName.ToLower());
            
                            if (reader.HasRows)
                            {
                                while (await reader.ReadAsync())
                                {
                                    T obj = Activator.CreateInstance<T>();
                                    foreach (PropertyInfo prop in props)
                                    {
                                        object val =
                                            reader.GetValue(colMapping[prop.Name.ToLower()].ColumnOrdinal.Value);
                                        prop.SetValue(obj, val == DBNull.Value ? null : val);
                                    }
                                    objList.Add(obj);
                                }
                            }
                            reader.Dispose();
            
                            return objList;
                        }
                    }
                    catch (Exception e)
                    {
                        System.Diagnostics.Debug.WriteLine(e.Message, e.InnerException);
                    }
                    finally
                    {
                        conn.Close();
                    }
            
                    return null; // default state
                }
            

            示例用法如下:

            public class MyService : IMyService 
            {
                    private readonly IUnitOfWork _uow;
                    public MyService(IUnitOfWork uow)
                    {
                        _uow = uow;
                    }
                    
                    public async Task<List<TreeViewModel>> GetTreeOptions()
                    {
                        var procParams = new Dictionary<string, object>()
                        {
                            {"@Id", 2}
                        };
                        var result = await _uow.ExecuteStoredProc<TreeViewModel>("FetchTreeProcedure", procParams);
                        return result;
                    }
            }
            

            【讨论】:

              【解决方案11】:

              使用 MySQL 连接器和 Entity Framework Core 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(); 
              

              【讨论】:

                【解决方案12】:

                我将 Entity Framework Core 与我的 ASP.Net Core 3.x WebAPI 一起使用。我希望我的终点之一只是执行一个特定的存储过程,这就是我需要的代码:

                namespace MikesBank.Controllers
                {
                    [Route("api/[controller]")]
                    [ApiController]
                    public class ResetController : ControllerBase
                    {
                        private readonly MikesBankContext _context;
                
                        public ResetController(MikesBankContext context)
                        {
                            _context = context;
                        }
                
                        [HttpGet]
                        public async Task<ActionResult> Get()
                        {
                            try
                            {
                                using (DbConnection conn = _context.Database.GetDbConnection())
                                {
                                    if (conn.State != System.Data.ConnectionState.Open)
                                        conn.Open();
                                    var cmd = conn.CreateCommand();
                                    cmd.CommandText = "Reset_Data";
                                    await cmd.ExecuteNonQueryAsync();
                                }
                                return new OkObjectResult(1);
                            }
                            catch (Exception ex)
                            {
                                return new BadRequestObjectResult(ex.Message);
                            }
                        }
                    }
                }
                

                注意我需要如何获取已注入的DbContext,但我还需要Open() 这个连接。

                【讨论】:

                  【解决方案13】:

                  我使用了 https://github.com/verdie-g/StoredProcedureEFCore,EnterpriseLibrary.Data.NetCore,EFCor.SqlServer,EFCore.Tools 的 StoredProcedureEFCore nuget 包

                  我尝试使用 {Repository pattern} 的 DbFirst 方法.. 我想是的

                  startup.cs

                  ConfigureServices(IServiceCollection services){
                      services.AddDbContext<AppDbContext>(opt => opt
                                     .UseSqlServer(Configuration.GetConnectionString("SampleConnectionString")));
                      services.AddScoped<ISomeDAL, SomeDAL>();
                  
                  }
                              
                      public  class AppDbContext : DbContext{
                  public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
                      {}
                  }
                  

                  ISomeDAl 接口有{GetPropertiesResponse GetAllPropertiesByCity(int CityId);}

                  public class SomeDAL : ISomeDAL
                  {
                       private readonly AppDbContext context;
                  
                       public SomeDAL(AppDbContext context)
                           {
                               this.context = context;
                           }
                       public  GetPropertiesResponse GetAllPropertiesByCity(int CityId)
                       {
                           //Create Required Objects for response 
                           //wont support ref Objects through params
                           context.LoadStoredProc(SQL_STATEMENT)
                              .AddParam("CityID", CityId).Exec( r =>
                               {
                                    while (r.Read())
                                    {
                  
                                         ORMapping<GenericRespStatus> orm = new  ORMapping<GenericRespStatus>();
                                         orm.AssignObject(r, _Status);
                                    }
                  
                                    if (r.NextResult())
                                    {
                  
                                         while (r.Read())
                                         {
                                             Property = new Property();
                                             ORMapping<Property> orm = new ORMapping<Property>();
                                             orm.AssignObject(r, Property);
                                             _propertyDetailsResult.Add(Property);
                                         }
                                    }    
                             });
                      return new GetPropertiesResponse{Status=_Status,PropertyDetails=_propertyDetailsResult}; 
                      }
                  }
                  
                  public class GetPropertiesResponse
                  {
                       public GenericRespStatus Status;
                       public List<Property> PropertyDetails;
                       public GetPropertiesResponse()
                           {
                               PropertyDetails = new List<Property>();
                           }
                  }
                  public class GenericRespStatus
                  {
                       public int ResCode { get; set; }
                       public string ResMsg { get; set; }
                  }
                  internal class ORMapping<T>
                  {
                      public void AssignObject(IDataReader record, T myClass)
                      {
                          PropertyInfo[] propertyInfos = typeof(T).GetProperties();
                          for (int i = 0; i < record.FieldCount; i++)
                          {
                              if (propertyInfos.Any(obj => obj.Name == record.GetName(i))) //&& record.GetValue(i) != DBNull.Value
                              {
                                  propertyInfos.Single(obj => obj.Name == record.GetName(i)).SetValue(myClass, Convert.ChangeType(record.GetValue(i), record.GetFieldType(i)));
                              }
                          }
                      }
                  }
                  

                  【讨论】:

                    【解决方案14】:

                    如果您使用 EntityFrameworkCore 从 Informix 执行存储过程,则需要包含命令 EXECUTE PROCEDURE

                    var spresult = _informixContext.procdata.FromSql("EXECUTE PROCEDURE dummyproc ({0},{1},{2})", parameters: new[] { p0, p1,p2 }).ToList();
                    

                    【讨论】:

                      【解决方案15】:

                      无需执行任何操作...当您为代码优先方法初始化创建 dbcontext 时 fluent API 区域下方的命名空间列出 sp 并在您想要的另一个地方使用它。

                      public partial class JobScheduleSmsEntities : DbContext
                      {
                          public JobScheduleSmsEntities()
                              : base("name=JobScheduleSmsEntities")
                          {
                              Database.SetInitializer<JobScheduleSmsEntities>(new CreateDatabaseIfNotExists<JobScheduleSmsEntities>());
                          }
                      
                          public virtual DbSet<Customer> Customers { get; set; }
                          public virtual DbSet<ReachargeDetail> ReachargeDetails { get; set; }
                          public virtual DbSet<RoleMaster> RoleMasters { get; set; }
                      
                          protected override void OnModelCreating(DbModelBuilder modelBuilder)
                          {
                              //modelBuilder.Types().Configure(t => t.MapToStoredProcedures());
                      
                              //modelBuilder.Entity<RoleMaster>()
                              //     .HasMany(e => e.Customers)
                              //     .WithRequired(e => e.RoleMaster)
                              //     .HasForeignKey(e => e.RoleID)
                              //     .WillCascadeOnDelete(false);
                          }
                          public virtual List<Sp_CustomerDetails02> Sp_CustomerDetails()
                          {
                              //return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<Sp_CustomerDetails02>("Sp_CustomerDetails");
                              //  this.Database.SqlQuery<Sp_CustomerDetails02>("Sp_CustomerDetails");
                              using (JobScheduleSmsEntities db = new JobScheduleSmsEntities())
                              {
                                 return db.Database.SqlQuery<Sp_CustomerDetails02>("Sp_CustomerDetails").ToList();
                      
                              }
                      
                          }
                      
                      }
                      

                      }

                      public partial class Sp_CustomerDetails02
                      {
                          public long? ID { get; set; }
                          public string Name { get; set; }
                          public string CustomerID { get; set; }
                          public long? CustID { get; set; }
                          public long? Customer_ID { get; set; }
                          public decimal? Amount { get; set; }
                          public DateTime? StartDate { get; set; }
                          public DateTime? EndDate { get; set; }
                          public int? CountDay { get; set; }
                          public int? EndDateCountDay { get; set; }
                          public DateTime? RenewDate { get; set; }
                          public bool? IsSMS { get; set; }
                          public bool? IsActive { get; set; }
                          public string Contact { get; set; }
                      }
                      

                      【讨论】:

                        【解决方案16】:

                        根据存储过程的 Select 查询中的字段创建特殊类。 例如我将这个类称为 ResulData

                        添加到你 EF 的上下文中

                        modelBuilder.Entity<ResultData>(e =>
                                {
                                    e.HasNoKey();
                                });
                        

                        这是一个使用存储过程获取数据的示例函数

                        public async Task<IEnumerable<ResultData>> GetDetailsData(int id, string name)
                        {
                            var pId = new SqlParameter("@Id", id);
                          var pName = new SqlParameter("@Name", name);
                            return await _context.Set<ResultData>()
                                     .FromSqlRaw("Execute sp_GetDeailsData  @Id @Name", parameters: new[] { pId, pName })
                                    .ToArrayAsync();
                        }
                        

                        【讨论】:

                          【解决方案17】:

                          我正在使用 Entity Framework Core。对存储过程和即席查询的支持不像在 Framework 中那样流畅。

                          以下是一些示例供将来参考:

                          根据存储过程的结果填充实体列表:

                          [dbo].[GetUnarchivedJobs] 存储过程返回与Job 实体匹配的记录列表。

                          我们可以使用Jobs 属性上的FromSqlInterpolated() 方法来调用存储过程并返回Job 的列表。

                          NoTracking() 用于加速性能,在这种情况下我们不会更新实体。

                          using Microsoft.EntityFrameworkCore;
                          
                          public class DbContext : Microsoft.EntityFrameworkCore.DbContext
                          {
                              protected DbSet<Job> Jobs { get; set; }
                          
                              // Populate a list of entities from the results of a stored procedure
                              public Task<List<Job>> GetUnarchivedJobs(int maxQty, CancellationToken cancellationToken) =>
                                  Jobs.FromSqlInterpolated($"EXEC [dbo].[GetUnarchivedJobs] @MaxQty = {maxQty}")
                                      .AsNoTracking()
                                      .ToListAsync(cancellationToken)
                                      ;
                          
                              public DbContext(DbContextOptions<DbContext> options) : base(options) { }
                          }
                          

                          将整数数组发送到存储过程:

                          [dbo].[SetJobListArchiveFlags] 存储过程有一个 integer_list_tbltype 类型的参数。

                          我们需要创建一个DataTable 来匹配integer_list_tbltype 类型,它有一个名为n 的列。

                          int 值需要添加到DataTable

                          SqlParameter 用于将填充的DataTable 传递给存储过程。

                          因为我们没有填充任何实体,所以我们需要使用 Database 属性上的方法来调用存储过程。

                          using Microsoft.Data.SqlClient;
                          using Microsoft.EntityFrameworkCore;
                          using System.Data;
                          
                          public class DbContext : Microsoft.EntityFrameworkCore.DbContext
                          {
                              // Send an array of integers to a stored procedure
                              public async Task<int> MarkJobsAsArchived(IEnumerable<int> jobIds, CancellationToken cancellationToken)
                              {
                                  // This DataTable matches the `integer_list_tbltype` db type
                                  var table = new DataTable();
                                  table.Columns.Add("n", typeof(int));
                                  foreach (var id in jobIds) table.Rows.Add(id);
                          
                                  var parameter = new SqlParameter("@jobIds", SqlDbType.Structured);
                                  parameter.Value = table;
                                  parameter.TypeName = "integer_list_tbltype";
                          
                                  var rowsUpdatedCount = await Database.ExecuteSqlRawAsync("EXEC [dbo].[SetJobListArchiveFlags] @jobIds", new[] { parameter }, cancellationToken);
                                  return rowsUpdatedCount;
                              }
                          }
                          

                          【讨论】:

                            【解决方案18】:

                            我们应该在模型中为 db 上下文创建一个带有 DbQuery 而不是 DbSet 的属性,如下所示...

                            public class MyContextContext : DbContext
                            {
                                public virtual DbQuery<CheckoutInvoiceModel> CheckoutInvoice { get; set; } 
                            }
                            

                            比一个可以用来返回结果的方法

                            public async Task<IEnumerable<CheckoutInvoiceModel>> GetLabReceiptByReceiptNo(string labReceiptNo)
                            {
                                var listing = new List<CheckoutInvoiceModel>();
                                try
                                {
                                    var sqlCommand = $@"[dbo].[Checkout_GetLabReceiptByReceiptNo] {labReceiptNo}";
                                    
                                    listing = await db.Set<CheckoutInvoiceModel>().FromSqlRaw(sqlCommand).ToListAsync();                       
                                                    
                                }
                                catch (Exception ex)
                                {
                                    return null;
                                }
                                return listing;
                            }
                            

                            从上面的例子中,我们可以使用任何你喜欢的选项。

                            希望对您有所帮助!

                            【讨论】:

                            • 您从不使用此属性CheckoutInvoice。而且还不清楚LoadStoredProc来自哪里。
                            • 是的,这可以在 DbContext 中添加 CheckoutInvoice,但应该是 DbQuery,而不是 DbSet。好吧,LoadStoredProc 来自 注意:我使用的是核心版本:3.18 谢谢@GertArnold
                            猜你喜欢
                            • 2020-03-05
                            • 1970-01-01
                            • 2018-08-13
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            相关资源
                            最近更新 更多