【问题标题】:Entity Framework core stored procedure实体框架核心存储过程
【发布时间】:2016-09-18 22:25:46
【问题描述】:

我正在使用 EF Core(代码优先),并希望从存储过程中获取数据。返回的结果应该是我根据结果返回定义的类。

如果返回类型是实体之一,我可以让它工作。前任。

_context.Set<Entity>().FromSql("dbo.Stored_Proc").ToList(); 

但如果我的返回值不是上下文中的实体,则不是。

任何帮助将不胜感激。

【问题讨论】:

  • 所以您希望实体框架...不返回实体?你确定你不是在寻找像 Dapper 这样的东西吗?
  • 我想返回数据,而不是那些直接映射到表的实体。我的数据没有精确映射到我上下文中的任何实体。

标签: c# stored-procedures entity-framework-core


【解决方案1】:

这很容易。

以下是 3 个步骤:

  1. 创建具有与您的 SP 返回的相同属性的模型类,例如
    public class SPModel
    {
        public int Id { get; set; }
        public DateTime? CreatedDateTime { get; set; }
        etc...
    }
  1. 将此模型类插入到您的 dbContext 类中,如:
    public class YourDbContext : DbContext
    {
        public virtual DbSet<SPModel> YourDbSet { get; set; }
        ....
    }

不要将该类映射到任何 SQL 表!

  1. 在调用 SP 时使用它:
    var spModels = await dbContext.YourDbSet
        .FromSqlRaw("EXECUTE yourStoredProcedure {0},{1}", param1, param2)
        .ToListAsync();

一些有用的东西:

  • 您可以使用 try/catch 块来查看是否遗漏了某些属性
  • 您可以扩展模型类以添加新属性,但避免使用“set”访问器,例如:public bool IsLoaded { get; }
  • 如果您的 SP 返回一些可空类型,请小心,在这种情况下,模型也必须具有可空类型

【讨论】:

  • 正确。我正在讨论 EF Core。 docs.microsoft.com/en-us/ef/core/querying/raw-sql 我在我的项目中使用它。至少在 EF Core 1.1 中
  • 啊...对不起。我的错。我忘了在顶部添加“使用”指令。傻我。难怪编译器无法识别FromSql 方法。
  • 无论如何...我正在使用 PostgreSQL 并且对 FromSql() 中的参数感到头疼。我有 DateTime 值,我想在 PostgreSQL 中将其转换为 date,并且系统会自动将 DateTime 转换为 timestamp without timezone。有没有办法像在 AdoDB 使用中一样指定NpgsqlDbType?我的意思是,在 AdoDB 中,我们可以使用这个:cmd.Parameters.Add("param", NpgsqlDbType.Date);。如何使用FromSql() 做到这一点?
  • 没关系...看来FromSql() 可以使用SqlParameter 对象来定义参数。在 PostgreSQL 中,我使用 NpgsqlParameternpgsql 实现 SqlParameter)。结案。 :)
  • 另外你需要设置modelBuilder.Entity&lt;SPModel&gt;().HasNoKey();并用[NotMapped]属性标记SPModel
【解决方案2】:

Entity Framework Net Core 2.0:执行存储过程并将结果映射到自定义对象列表中

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>();
}

我希望这就是你所需要的。

【讨论】:

【解决方案3】:

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

我原来的答案-https://stackoverflow.com/a/57224037/1979465

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

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

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

第 2 步。

然后你必须在你的 SP 的 DBContext 类中添加一个 DbQuery 属性。

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();
}

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

【讨论】:

    【解决方案4】:

    这可以在不定义任何 DbQuery 或 DbSet 的情况下实现,但在以下扩展的帮助下。 Efcore 3 及以上

    public class CustomType 
    {
       public int Id { get; set; }
       public string Name { get; set; }
    }
    
    public static class DbContextExtensions
    {
        public static IList<T> SqlQuery<T>(this DbContext context, string sql, params object[] parameters) where T : class
        {
            using (var dbcontext = new ContextForQueryType<T>(context.Database.GetDbConnection()))
            {
                return dbcontext.Set<T>().FromSqlRaw(sql, parameters).AsNoTracking().ToList();
            }
        }
    
        public static async Task<IList<T>> SqlQueryAsync<T>(this DbContext context, string sql, params object[] parameters) where T : class
        {
            using (var dbcontext = new ContextForQueryType<T>(context.Database.GetDbConnection()))
            {
                return await dbcontext.Set<T>().FromSqlRaw(sql, parameters).AsNoTracking().ToListAsync();
            }
        }
    
    private class ContextForQueryType<T> : DbContext where T : class
    {
        private readonly System.Data.Common.DbConnection connection;
    
        public ContextForQueryType(System.Data.Common.DbConnection connection)
        {
            this.connection = connection;
        }
    
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(connection, options => options.EnableRetryOnFailure());
    
            base.OnConfiguring(optionsBuilder);
        }
    
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<T>().HasNoKey();
            base.OnModelCreating(modelBuilder);
        }
      } 
    }
    

    并像这样执行:

    var param = new SqlParameter("@IdParam", SqlDbType.VarChar, 10);
    param.Value = Id.ToString();
    
    string sqlQuery = "Exec [dbo].[usp_get_custom_type] @IdParam";
    
    await context.SqlQueryAsync<CustomType>(sqlQuery);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-10-29
      • 1970-01-01
      • 2021-04-08
      • 1970-01-01
      • 2016-12-02
      • 1970-01-01
      • 2021-07-14
      相关资源
      最近更新 更多