我的网站只有在应用需要时才需要初始化一些数据,而且必须只进行一次。初始化必须调用API来获取数据。
所以基本上,我认为我需要制作一个 lazy singleton ,它将在第一次访问时调用api。但是当我使用来自HttpClient类的异步方法GetAsync时,我遇到了问题。调用API时有些东西崩溃但我的TryCatch没有抓住异常。
我尝试将Piv GetPiv()
转换为Task<Piv> GetPiv()
,但我不知道如何将它与Lazy对象一起使用。
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;
}
}
}
我的_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>
答案 0 :(得分:0)
您需要使用async
和await
关键字。使用.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 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; }
}
然后在你的API代码中你可以使用帮助器(我做了一些关于将ASP.NET Core用于其Web API的假设):
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...
}
}
<强>更新强>
根据OP编辑,我需要更新我的答案。
你错误地做了MVC。整个想法是根据SOLID等以下原则分离关注点并推广最佳实践。您的观点不应该了解PivHelper
而是Piv
模型,而您的控制器应该了解PivHelper
as它是Piv
。
至于要求将模型作为布局的一部分,这是一场不同的战斗。我建议不要这样做,并建议您使用最少量的元素将您的布局保留为.css
的链接和.js
的脚本标记。