C# 当存在';包含的异步函数
我的网站需要初始化一些数据,只有当应用程序需要它,它必须只做一次。初始化必须调用API来获取数据 因此,基本上,我认为我需要创建一个惰性单例,它将在首次访问api时调用api。但是当我使用HttpClient类中的异步方法GetAsync时,我遇到了一个问题。调用API时发生崩溃,但我的TryCatch没有捕获异常 我试图将C# 当存在';包含的异步函数,c#,async-await,singleton,lazy-initialization,C#,Async Await,Singleton,Lazy Initialization,我的网站需要初始化一些数据,只有当应用程序需要它,它必须只做一次。初始化必须调用API来获取数据 因此,基本上,我认为我需要创建一个惰性单例,它将在首次访问api时调用api。但是当我使用HttpClient类中的异步方法GetAsync时,我遇到了一个问题。调用API时发生崩溃,但我的TryCatch没有捕获异常 我试图将Piv GetPiv()转换为Task GetPiv(),但我不知道如何将其用于惰性对象 public class PivHelper { private stati
Piv GetPiv()
转换为Task GetPiv()
,但我不知道如何将其用于惰性对象
public class PivHelper
{
private static readonly Lazy<Piv> _lazyPiv = new Lazy<Piv>(GetPiv);
public string Header()
{
return _lazyPiv.Value.Header;
}
public string Footer()
{
return _lazyPiv.Value.Footer;
}
public string Scripts()
{
return _lazyPiv.Value.Scripts;
}
public string Styles()
{
return _lazyPiv.Value.Styles;
}
private static Piv GetPiv()
{
try
{
var client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = client.GetAsync("http://some-web-site.com").Result;
if (response.IsSuccessStatusCode)
{
return response.Content.ReadAsAsync<Piv>().Result;
}
else
{
return new Piv();
}
}
catch (Exception ex)
{
return new Piv();
}
}
}
public class Piv
{
public string Header { get; set; }
public string Footer { get; set; }
public string Styles { get; set; }
public string Scripts { get; set; }
}
public static class MtoHelper
{
private static readonly Lazy<PivHelper> lazyPivHelper = new Lazy<PivHelper>(() => new PivHelper());
public static PivHelper Piv
{
get
{
return lazyPivHelper.Value;
}
}
}
公共类PivHelper
{
private static readonly Lazy _lazyPiv=new Lazy(GetPiv);
公共字符串头()
{
返回_lazyPiv.Value.Header;
}
公共字符串页脚()
{
返回_lazyPiv.Value.Footer;
}
公共字符串脚本()
{
返回_lazyPiv.Value.Scripts;
}
公共字符串样式()
{
返回_lazyPiv.Value.style;
}
私有静态Piv GetPiv()
{
尝试
{
var client=新的HttpClient();
client.DefaultRequestHeaders.Accept.Add(新的MediaTypeWithQualityHeaderValue(“应用程序/json”);
HttpResponseMessage response=client.GetAsync(“http://some-web-site.com三、结果;
if(响应。IsSuccessStatusCode)
{
返回response.Content.ReadAsAsync().Result;
}
其他的
{
返回新的Piv();
}
}
捕获(例外情况除外)
{
返回新的Piv();
}
}
}
公共类Piv
{
公共字符串头{get;set;}
公共字符串页脚{get;set;}
公共字符串样式{get;set;}
公共字符串脚本{get;set;}
}
公共静态类MtoHelper
{
private static readonly lazyPivHelper=new Lazy(()=>new PivHelper());
公共静态PivHelper Piv
{
得到
{
返回lazyPivHelper.Value;
}
}
}
My_Layout.cshtml文件
<!DOCTYPE html>
<html>
<head>
<!-- Instead of using our own copy of the piv.css, we use the one
coming from the webservices -->
<!--<link href="~/content/piv.css" rel="stylesheet" type="text/css">-->
@MtoHelper.Piv.Styles()
<link rel="shortcut icon" href="/favicon.ico" />
</head>
<body>
<header id="pageHeaderContainer" class="container">
@MtoHelper.Piv.Header()
</header>
<section id="main" class="container">
@RenderBody()
</section>
<footer id="pageFooterContainer" class="container">
@MtoHelper.Piv.Footer()
</footer>
@MtoHelper.Piv.Scripts()
</body>
</html>
@MtoHelper.Piv.Styles()
@MtoHelper.Piv.Header()
@RenderBody()
@MtoHelper.Piv.Footer()
@MtoHelper.Piv.Scripts()
您需要使用async
和wait
关键字。使用.Result
会导致问题。您可以实现如下所示的AsyncLazy
类,这将允许您确保对GetPiv
的调用只发生一次
public class AsyncLazy<T> : Lazy<Task<T>>
{
public AsyncLazy(Func<T> valueFactory) : base(() => Task.Run(valueFactory))
{
}
public AsyncLazy(Func<Task<T>> taskFactory) : base(() => Task.Run(taskFactory))
{
}
}
公共类异步延迟:延迟
{
public(Func valueFactory):基(()=>Task.Run(valueFactory))
{
}
公共异步(Func taskFactory):基(()=>Task.Run(taskFactory))
{
}
}
然后,您必须修改代码以利用上述关键字:
public class PivHelper
{
static readonly AsyncLazy<Piv> _lazyPiv = new AsyncLazy<Piv>(() => GetPivAsync());
public Task<Piv> PivTask => _lazyPiv.Value;
static Task<Piv> GetPivAsync()
{
try
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.GetAsync("http://some-web-site.com");
if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsAsync<Piv>();
}
else
{
return new Piv();
}
}
}
catch (Exception ex)
{
return new Piv();
}
}
}
public class Piv
{
public string Header { get; set; }
public string Footer { get; set; }
public string Styles { get; set; }
public string Scripts { get; set; }
}
公共类PivHelper
{
静态只读AsyncLazy _lazyPiv=new AsyncLazy(()=>GetPivAsync());
公共任务PivTask=>\u lazyPiv.Value;
静态任务GetPivAsync()
{
尝试
{
使用(var client=new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(新的MediaTypeWithQualityHeaderValue(“应用程序/json”);
var response=wait client.GetAsync(“http://some-web-site.com");
if(响应。IsSuccessStatusCode)
{
return wait response.Content.ReadAsAsync();
}
其他的
{
返回新的Piv();
}
}
}
捕获(例外情况除外)
{
返回新的Piv();
}
}
}
公共类Piv
{
公共字符串头{get;set;}
公共字符串页脚{get;set;}
公共字符串样式{get;set;}
公共字符串脚本{get;set;}
}
然后,在API代码中,您可以这样使用帮助程序(我假设使用ASP.NET Core作为其Web API):
公共类PIV控制器:控制器
{
私人只读PivHelper\u助手;
公共PIV控制器(PivHelper PivHelper)
{
_helper=pivHelper;
}
公共异步任务GetPiv()
{
var piv=等待_helper.PivTask;
//按piv操作,或直接返回它。。。
}
}
更新
根据OPs编辑,我需要更新我的答案
你没有正确地进行MVC。整个想法是分离关注点,并根据以下原则(如SOLID)推广最佳实践。您的视图不应了解PivHelper
,而应了解Piv
模型,您的控制器应了解PivHelper
,因为它是Piv
的提供者
至于需要一个模型作为布局的一部分,这是一场不同的战斗。我建议您不要这样做,并建议您将布局仅限于
.css
的链接和.js
的脚本标记,并且使用最少的元素。调用.Result
通常是个坏主意。你能让GetPiv()异步吗<代码>公共静态异步任务GetPiv()
?然后,您可以等待其中调用的异步操作。这是我试图做的,但是,如果GetPiv是一个任务,那么如何使用惰性对象呢。这就是我被困的地方。问得好。这看起来很有用:我正在MVC5项目中使用_layour.cshtml文件中的结果。事实上,我在描述中的例子是真实事物的一个较小版本。public string Header()
实际上是一个public MvcHtmlString Header()
,因此我可以在视图中直接使用它。因此,它不能是任务确实可以,向我显示与视图相对应的控制器操作,我将更新我的an
public class PivController : Controller
{
private readonly PivHelper _helper;
public PivController (PivHelper pivHelper)
{
_helper = pivHelper;
}
public async Task<IActionResult> GetPiv()
{
var piv = await _helper.PivTask;
// Act on the piv, or simply return it...
}
}