代码之家  ›  专栏  ›  技术社区  ›  Andrew

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

  •  6
  • Andrew  · 技术社区  · 7 年前

    我有一个功能( 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;
    }
    

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

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

    1 回复  |  直到 4 年前
        1
  •  4
  •   Timothy G.    3 年前

    DbContext中的数据库集表示数据库中的表。DbContext知道表之间的关系以及如何将LINQ查询转换为数据库能够理解的查询。DbContext的任务是隐藏数据库的内部。每当您想与数据库通信时,都要使用DbContext。

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

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

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

    DbContext将使用DbContext执行存储过程。数据库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
    } 
    

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

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

    最后,关于模型创建:

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        this.CreateMyStoredProcedure(false); // don't force if already exists;
    
        // TODO: add fluent API
    }