繁体   English   中英

实体框架核心存储过程

[英]Entity Framework core stored procedure

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

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

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

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

任何帮助,将不胜感激。

这很容易。

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

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

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

您需要通过从 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 传入的列表。

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

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

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

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

这可以在不定义任何 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);

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

我的原始答案 - 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步。

然后您必须将一个DbQuery属性添加到您的 SP 的 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();
}

希望对某人有帮助!!!

string sqlquery = "EXEC dbo.Stored_Proc"; 
var result = _context.Entities.FromSqlRaw(sqlquery).ToList();

检查上面的代码。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM