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

时间:2018-01-08 23:22:44

标签: c# entity-framework linq asynchronous

我有一个使用linq表达式的函数(ReturnStatementDetailsForSubRepAsync),我可以使用.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;
}

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, Nothing).ToList());
    }
}

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

1 个答案:

答案 0 :(得分:1)

DbContext中的DbSets表示数据库中的表。 DbContext知道这些表之间的关系以及如何将LINQ查询转换为数据库理解的查询。 DbContext的任务是隐藏数据库的内部。只要您想与数据库通信,就可以使用DbContext。

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

DbContext的用户可能需要以下功能:

  • 使用参数
  • 调用存储过程
  • 使用参数async调用存储过程(您的问题)
  • 存储过程是否存在?
  • 创建模型时创建或更改存储过程

您的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.ExecuteSqlComment(sqlCommandMyStoredProcedure, functionParameters); 
    }

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

    // TODO: add more functions
    #endregion stored procedures
} 

这些功能使用其他几个功能:

// 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),
     };
}

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

检查存储过程是否已存在

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

创建存储过程:

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

最后OnModelCreating:

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

    // TODO: add fluent API
}