【问题标题】:Async and Await issue in c#c#中的异步和等待问题
【发布时间】:2021-05-24 19:27:23
【问题描述】:

我需要帮助来使我的方法异步:

这是一种方法,现在可以正常工作,但是如果我删除 GetAwaiter().GetResult() ,我不会得到值,并且我被告知 GetAwaiter().GetResult() 不是一个好习惯。所以我想知道,我将如何更改它以使其异步。如果我编写内部异步任务,我会从我的其他方法中得到错误,其中它被称为返回类型不正确。我该如何解决?

internal string GeneratePartitionKey( Dictionary<string, EntityProperty> arg)
        {      
            var partitionKey = RunScript( PSScript, filter );
            return partitionKey.GetAwaiter().GetResult();
        }


 public async Task<string> RunScript( string scriptContents, List<string> scriptParameters )
        {string script = @"Get-ChildItem C:\ ";
            using( PowerShell ps = PowerShell.Create() )
            {                
                ps.AddScript( script );
                var pipelineObjects = await ps.InvokeAsync().ConfigureAwait( false );
                StringBuilder stringBuilder = new StringBuilder();
                foreach( var item in pipelineObjects )
                {
                    stringBuilder.AppendLine( item.BaseObject.ToString() );
                }
                  return stringBuilder.ToString();
            }            
        }

//我们从这里开始,将 GeneratePartitionKey 更改为 async 时发生错误

 await csvCopy.UploadNonCsvData( rule.GeneratePartitionKey,rule.GenerateRowKey, unzipper, schemaFormat, true, csvSchema );
        

public async Task UploadNonCsvData( Func<Dictionary<string, EntityProperty>, string> genPartitionKey,
            Func<Dictionary<string, EntityProperty>, string> genRowKey,
            Stream lines, string format, bool upsert, IDataClassifier classifier )
        {
            var dataclass = classifier.Classes.Where( c => c.Format.Equals( format, StringComparison.OrdinalIgnoreCase ) ).FirstOrDefault();

            await WriteToTable( lines, dataclass,
                genPartitionKey,
                genRowKey, upsert );
        }

public async Task WriteToTable( Stream lines, DataClass dataclass,
        Func<Dictionary<string, EntityProperty>, string> genPartitionKey,
        Func<Dictionary<string, EntityProperty>, string> genRowKey, bool upsert )
    {
        const int BatchSize = 100;
        if( HasPartitionAndRowKey( dataclass.TableSchema.Fields ) )
        {
            genPartitionKey = ( Dictionary<string, EntityProperty> props ) => props["PartitionKey"].StringValue;
            genRowKey = ( Dictionary<string, EntityProperty> props ) => props["RowKey"].ToString();
        }
        
        var tableRecords = ReadCSV( lines, dataclass.TableSchema.Fields )
            .Select( props => new DynamicTableEntity( genPartitionKey( props ), genRowKey( props ), string.Empty, props ) )
            .ToList();
        await BatchInsertIntoTableStorage( BatchSize, tableRecords, upsert );

    }

 private IEnumerable<Dictionary<string, EntityProperty>> ReadCSV( Stream source, IEnumerable<TableField> cols )
        {
            using( TextReader reader = new StreamReader( source, Encoding.UTF8 ) )
            {....... while( csv.Read() )
                {
                    yield return map.ToDictionary(
                        col => col.Name,
                        col => EntityProperty.CreateEntityPropertyFromObject( csv.GetField( col.Type, col.Index ) ) );
                }
            }




private async Task BatchInsertIntoTableStorage( int batchSize, List<Task<DynamicTableEntity>> tableEntries, bool upsert )
    {
        CloudStorageAccount storageAccount;


        if( string.IsNullOrEmpty( _account ) && string.IsNullOrEmpty( _key ) )
        {
            storageAccount = CloudStorageAccount.DevelopmentStorageAccount;
        }
        else
        {
            storageAccount = new CloudStorageAccount(
                             new Microsoft.Azure.Cosmos.Table.StorageCredentials( _account, _key ),
                             true );
        }

        var tableClient = storageAccount.CreateCloudTableClient();
        var table = tableClient.GetTableReference( _table );

        foreach( var tableEntryBatch in tableEntries.Batch( batchSize ) )
        {
            var batchOperation = new TableBatchOperation();
            foreach( var tableEntry in tableEntryBatch )
                batchOperation.Add( upsert ? TableOperation.InsertOrReplace((ITableEntity) tableEntry ) : TableOperation.Insert((ITableEntity) tableEntry ) );

            await table.ExecuteBatchAsync( batchOperation );
        }
    }
   
}

【问题讨论】:

  • async / await 上升到调用堆栈的顶部,理想情况下你应该允许它这样做,只在最顶层阻塞。
  • 为了避免 Sync-over-Async 的整个问题(就是这样),您可以尝试创建 RunScript 的非异步版本。但是,我并没有确定您正在使用的所有方法都有非异步版本。
  • 理想情况下,您可以将签名更改为internal async Task&lt;string&gt; GeneratePartitionKey,并将任何调用方法更改为异步等
  • @JohnathanBarclay “只在最顶层阻塞。” 为什么阻塞?最顶层应该是async void 类型的事件。哪个不应该阻止
  • @PeterB B 创建非异步版本的 RunScript 很容易,但我想将其设为异步

标签: c#


【解决方案1】:

将您的 GeneratePartitionKey 更改为异步方法:

internal Task<string> GeneratePartitionKey( Dictionary<string, EntityProperty> arg)
{      
   var partitionKey = RunScript( PSScript, filter );
   return await partitionKey;
}

为了避免编译错误,你必须将你的委托类型改为Func&lt;Dictionary&lt;string, EntityProperty&gt;, Task&lt;string&gt;&gt;

public async Task UploadNonCsvData( Func<Dictionary<string, EntityProperty>, Task<string>> genPartitionKey,
            Func<Dictionary<string, EntityProperty>, string> genRowKey,
            Stream lines, string format, bool upsert, IDataClassifier classifier )
        {
            var dataclass = classifier.Classes.Where( c => c.Format.Equals( format, StringComparison.OrdinalIgnoreCase ) ).FirstOrDefault();

            await WriteToTable( lines, dataclass,
                genPartitionKey,
                genRowKey, upsert );
        }

public async Task WriteToTable( Stream lines, DataClass dataclass,
        Func<Dictionary<string, EntityProperty>, Task<string>> genPartitionKey,
        Func<Dictionary<string, EntityProperty>, string> genRowKey, bool upsert )
    {
        const int BatchSize = 100;
        if( HasPartitionAndRowKey( dataclass.TableSchema.Fields ) )
        {
            genPartitionKey = ( Dictionary<string, EntityProperty> props ) => Task.FromResult(props["PartitionKey"].StringValue);
            genRowKey = ( Dictionary<string, EntityProperty> props ) => props["RowKey"].ToString();
        }
        
        var csv = ReadCSV( lines, dataclass.TableSchema.Fields );
        var tableRecords = new List<DynamicTableEntity>();
        foreach(var entry in csv)
        {
            tableRecords.Add(new DynamicTableEntity( await genPartitionKey( props ), genRowKey( props ), string.Empty, props );
        }
        await BatchInsertIntoTableStorage( BatchSize, tableRecords, upsert );

    }

【讨论】:

  • 然后我在 genpartitionKey() 的 WriteTotable 方法上收到错误,说无法将类型字符串转换为任务
  • @zzzsharepoint 你确定你包含了“async”和“await”位吗?
  • @MarcGravell Gotcha :)
  • 是的,我在 WriteToTable 方法中的 lambda 表达式中遇到错误
  • 可能是因为ReadCsv方法的问题,我现在已经把它包含在问题中了,请检查并提出建议
【解决方案2】:

See this article about the GetAwaiter().GetResult()

不惜一切代价避免使用“.GetAwaiter().GetResult()”、“.Result”或“.Wait()”。 将方法“GeneratePartitionKey”重构为异步。

然后 LINQ 表达式将类似于以下内容:

var tableRecors = (await Task.WhenAll(ReadCSV(lines, dataclass.TableSchema.Fields)
                            .Select( props =>
                                    new DynamicTableEntity(genPartitionKeyAsync(props), genRowKey(props), string.Empty, props)
                                ))).Where(result => result != null).ToList();

【讨论】:

  • 尝试过,现在我收到错误,因为无法将类型为“System.Threading.Tasks.Task`1[Microsoft.Azure.Cosmos.Table.DynamicTableEntity]”的对象转换为类型“Microsoft.Azure。 Cosmos.Table.ITableEntity'.. 在 WriteToTable 中有 BatchInsert() 导致错误。现在也包括这个问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-03
  • 1970-01-01
  • 2021-10-11
  • 1970-01-01
  • 2018-12-09
  • 2017-04-01
相关资源
最近更新 更多