如何使实体框架存储过程异步?

And*_*rew 5 c# linq asynchronous entity-framework

我有一个ReturnStatementDetailsForSubRepAsync使用 linq 表达式的函数 ( ),我可以使用诸如.ToListAsync(). 现在因为这个 LINQ 函数是异步的,所以我必须使父函数异步。

父函数如下所示:

public async Task<IEnumerable<StatementDetail>> ReturnStatementDetailsAsync(string cid, string userName, int statementNo)
{
    var statementDetails = new List<StatementDetail>;
    if (HttpContext.Current.User.IsInRole(UserLevel.Subrep.GetDescription()) || HttpContext.Current.User.IsInRole(UserLevel.SubRepMaster.GetDescription()))
    {
        var subRepStmtDetails = await ReturnStatementDetailsForSubRepAsync(cid, userName, statementNo); //Linq query with ToListAsync()
        foreach (var item in subRepStmtDetails)
        {
            statementDetails.Add(new SubRepStatementDetailItem(item));
        }
    }
    else
    {
        var regionalStmtDetails = await Task.Run(() => StoredPrcedureAsyncTest(cid, statementNo); //Entity framework stored procedure call
        foreach (var item in regionalStmtDetails)
        {
            statementDetails.Add(new RegionalStatementDetailItem(item));
        }
    }

    return statementDetails;
}
Run Code Online (Sandbox Code Playgroud)

StoredPrcedureAsyncTest 看起来像这样:

public async Task<IEnumerable<SelectStatementTransno_Result>> StoredPrcedureAsyncTest(string cid, int statementNo)
{
    using (var dbContext = new WebDataEntities())
    {
        return await Task.Run(() => dbContext.SelectStatementTransno(cid, statementNo, null).ToList());
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我知道它StoredPrcedureAsyncTest执行 IO 工作,所以我应该让它异步。那么我实现存储过程调用的方式是否会导致我的方法完全异步,因为目前没有内置的实体框架解决方案来使存储过程调用异步?

Har*_*lse 4

DbContext 中的 DbSet 代表数据库中的表。DbContext 了解表之间的关系以及如何将 LINQ 查询转换为数据库可以理解的查询。DbContext 的任务是隐藏数据库的内部结构。每当您想要与数据库通信时,您都可以使用 DbContext。

因此,DbContext 是放置存储过程的好地方。由于 DbContext 还创建模型(在 DbContext.OnModelCreating 中),因此它也是添加创建存储过程的功能的好地方。

DbContext 的用户可能期望以下功能:

  • 带参数调用存储过程
  • 使用参数异步调用存储过程(您的问题)
  • 存储过程存在吗?
  • 创建模型时创建或更改存储过程

您的 DbContext 将使用 DbContext.Database.ExecuteSqlCommand 执行存储过程。此函数有一个异步等效函数:DbContext.Database.ExecuteSqlAsync

class MyDbContext : DbContext
{
    // TODO: add DbSets

    #region stored procedure
    public void CallMyStoredProcedure(MyParams myParams)
    {
        object[] functionParameters = this.CreateFunctionParams(myParams);
        this.Database.ExecuteSqlCommand(sqlCommandMyStoredProcedure, functionParameters); 
    }

     public async Task CallMyStoredProcedure(MyParams myParams)
    {
        object[] functionParameters = this.CreateFunctionParams(myParams);
        await this.Database.ExecuteSqlCommandAsync(
            sqlCommandMyStoredProcedure,
            functionParameters)
        .ConfigureAwait(false);; 
    }

    // TODO: add more functions
    #endregion stored procedures
} 
Run Code Online (Sandbox Code Playgroud)

这些函数使用了其他几个函数:

// name of the stored procedure, names of the parameters:
private const string myStoredProcedureName = "InsertPoint";
private const string paramProductName = "ProductName";
private const string paramCount = "Count";

// SQL command to execute stored procedure with the parameters
private const string SqlCmndMyStoredProcedure = @"Exec "
    + myStoredProcedureName
    + @" @ParamProductName, @ParamCount";

private object[] CreateFunctionParams(MyParams myParams)
{
     return newObject[]
     {
         new SqlParameter(paramProductName, myParams.ProductName),
         new SqlParameter(paramCount, myParams.Count),
     };
}
Run Code Online (Sandbox Code Playgroud)

要使集合完整:添加一个检查存储过程是否存在的方法以及一个创建存储过程的方法:

检查存储过程是否已经存在

public bool MyStoredProcedureExists()
{
     return this.StoredProcedureExists(myStoredProcedureName);
}

public bool StoredProcedureExists(string procedureName)
{
    object[] functionParameters = new object[]
    {
        new SqlParameter(@"procedurename", procedureName),
    };

    string query = @"select [name] from sys.procedures where name= @procedurename";

    return this.Database.SqlQuery<string>(query, functionParameters)
        .ToList()
        .Where(item => item == procedureName)
        .Any();        
}
Run Code Online (Sandbox Code Playgroud)

创建存储过程:

public void CreateMyStoredProcedure(bool forceCreate)
{
    // do not create if already exists, except if forceCreate:
    bool storedProcedureExists = this.MyStoredProcedureExists;

    if (!storedProcedureExists || forceCreate)
    {   // create the stored procedure:
        var x = new StringBuilder();

        // decide whether to create or Alter
        if (!storedProcedureExists)
        {
            x.Append(@"CREATE");
        }
        else
        {
            x.Append(@"ALTER");
        }

        // procedure  name:
        x.Append(@" PROCEDURE ");
        X.AppendLine(myStoredProcedureName);

        // parameters:
        x.AppendLine(@"@ProductName NVARCHAR(80),"
        X.AppendLine(@"@Count int")

        // procedure code:
        x.AppendLine(@"AS")
        X.AppendLine(@"BEGIN")
        ... // TODO: add procedure code
        x.AppendLine(@"END");

        this.Database.ExecuteSqlComment(x.ToString());
    }
}
Run Code Online (Sandbox Code Playgroud)

最后OnModelCreating:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    this.CreateMyStoredProcedure(false); // don't force if already exists;

    // TODO: add fluent API
}
Run Code Online (Sandbox Code Playgroud)