我有一个SSIS包,其中包含一些脚本。其中一个通过REST API获取令牌。
我写了下面的内容,在.net 4.5中正常工作-但是,我们的服务器不是最新的,我们必须降级到.net 4.0,这是我们打h的地方,因为4.0没有GetAwaiter(以及其他各种方便的功能)。
为了增加乐趣,如上所述,这是SSIS,我相信您不能在其中添加NuGet软件包。
public void Main()
{
//read the token from the file
string ref_token = File.ReadAllText(Dts.Variables["User::PATH_refresh_token"].Value.ToString());
**Dts.Variables["User::API_returned"].Value = GetToken(Dts.Variables["$Package::API_token_client_id"].Value.ToString(), Dts.Variables["$Package::API_token_client_secret"].Value.ToString(), ref_token, Dts.Variables["$Package::API_token_endpoint"].Value.ToString()).GetAwaiter().GetResult();**
}
static async Task<string> GetToken(string client_id, string client_secret, string ref_token, string token_url)
{
try
{
var client = new System.Net.Http.HttpClient();
HttpContent content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("client_id", client_id),
new KeyValuePair<string, string>("client_secret", client_secret),
new KeyValuePair<string, string>("grant_type", "refresh_token"),
new KeyValuePair<string, string>("refresh_token", ref_token),
new KeyValuePair<string, string>("expiration", "20160")
});
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls;
**var response = await client.PostAsync(token_url, content);**
var responseString = await response.Content.ReadAsStringAsync();
return responseString;
}
catch (Exception exception)
{
return exception.ToString();
}
}
所以我们目前在GetAwaiter()上遇到问题(代码的第五行,突出显示):
''Task(string)'不包含'GetAwaiter'的定义...
并且也在此行中等待(也突出显示):
var response = await client.PostAsync(token_url, content);
''Task(HttpResponseMessage)'不包含'GetAwaiter'的定义...
不幸的是,我尝试连接的一方使用TLS12和TLS11,.net 4.0中不支持。
好吧,然后解决GetAwaiter
问题将无济于事。
但为后代:
使用await
的任何代码都可以转换为不使用await
的等效代码。为此,您需要手动拆分代码,并使用ContinueWith
代替await
,并使用TaskCompletionSource<T>
代替async
。无论如何,这大约是what the compiler is doing to your code。
逐步:
首先,您需要将async
替换为TaskCompletionSource<T>
:
static Task<string> GetToken(...)
{
var tcs = new TaskCompletionSource<string>();
try
{
...
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls;
var response = await client.PostAsync(token_url, content);
var responseString = await response.Content.ReadAsStringAsync();
return responseString;
}
catch (Exception exception)
{
return exception.ToString();
}
}
[所有return
语句现在成为设置TCS结果的代码:
static Task<string> GetToken(...)
{
var tcs = new TaskCompletionSource<string>();
try
{
...
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls;
var response = await client.PostAsync(token_url, content);
var responseString = await response.Content.ReadAsStringAsync();
tcs.TrySetResult(responseString);
}
catch (Exception exception)
{
tcs.TrySetResult(exception.ToString());
}
}
下一步,删除try
/ catch
(但请记住它在那儿)。使用ContinueWith
,我们需要继续处理错误:
static Task<string> GetToken(...)
{
var tcs = new TaskCompletionSource<string>();
...
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls;
var response = await client.PostAsync(token_url, content);
var responseString = await response.Content.ReadAsStringAsync();
tcs.TrySetResult(responseString);
// catch (Exception exception)
// {
// tcs.TrySetResult(exception.ToString());
// }
}
现在您可以开始将await
语句转换为ContinueWith
。对于每个方法,将方法的其余部分移至一个继续。请注意ContinueWith
is dangerous,因此请确保传递正确的调度程序。该代码看起来好像不需要原始上下文,因此我正在使用ContinueWith
。因此,从技术上讲,这是TaskScheduler.Default
与await
的转换,而不仅仅是普通的ConfigureAwait(false)
,后者会更复杂。
延续得到一个任务,它可以查询异常或结果。注意哪些成员在await
中包装了异常;可以更改您的异常处理代码。
这是第一个AggregateException
转换的样子:
await
第二个static Task<string> GetToken(...)
{
var tcs = new TaskCompletionSource<string>();
...
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls;
client.PostAsync(token_url, content).ContinueWith(task =>
{
if (task.IsFaulted)
{
tcs.TrySetResult(task.Exception.InnerException.ToString());
return;
}
var response = task.Result;
var responseString = await response.Content.ReadAsStringAsync();
tcs.TrySetResult(responseString);
}, TaskScheduler.Default);
// catch (Exception exception)
// {
// tcs.TrySetResult(exception.ToString());
// }
}
转换可以类似的方式进行:
await
或者,通过简单的static Task<string> GetToken(...)
{
var tcs = new TaskCompletionSource<string>();
...
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls;
client.PostAsync(token_url, content).ContinueWith(task =>
{
if (task.IsFaulted)
{
tcs.TrySetResult(task.Exception.InnerException.ToString());
return;
}
var response = task.Result;
response.Content.ReadAsStringAsync().ContinueWith(task2 =>
{
if (task2.IsFaulted)
{
tcs.TrySetResult(task2.Exception.InnerException.ToString());
return;
}
var responseString = task2.Result;
tcs.TrySetResult(responseString);
}, TaskScheduler.Default);
}, TaskScheduler.Default);
}
语句一个接一个,您可以“链接”延续。您确实需要使用await
,因为第一个延续返回任务。这种方法看起来像这样:
Unwrap
作为最后的说明,通过“链式”延续,许多人更喜欢让异常通过并在末尾将它们压平;这样的代码要短一些:
static Task<string> GetToken(...)
{
var tcs = new TaskCompletionSource<string>();
...
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls;
client.PostAsync(token_url, content).ContinueWith(task =>
{
if (task.IsFaulted)
{
tcs.TrySetResult(task.Exception.InnerException.ToString());
return;
}
var response = task.Result;
return response.Content.ReadAsStringAsync();
}, TaskScheduler.Default)
.Unwrap()
.ContinueWith(task =>
{
if (task.IsFaulted)
{
tcs.TrySetResult(task.Exception.InnerException.ToString());
return;
}
var responseString = task.Result;
tcs.TrySetResult(responseString);
}, TaskScheduler.Default);
}
因此,开发人员喜欢static Task<string> GetToken(...)
{
var tcs = new TaskCompletionSource<string>();
...
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls;
client.PostAsync(token_url, content).ContinueWith(task =>
{
var response = task.Result;
return response.Content.ReadAsStringAsync();
}, TaskScheduler.Default)
.Unwrap()
.ContinueWith(task =>
{
if (task.IsFaulted)
{
tcs.TrySetResult(task.Exception.Flatten().InnerException.ToString());
return;
}
var responseString = task.Result;
tcs.TrySetResult(responseString);
}, TaskScheduler.Default);
}
和async
关键字。 ;)