如何以及在何处判断 ViewComponent 是否已在视图中被调用 x 次?
How and Where to tell if a ViewComponent has been invoked x times in a view?
我有一个 ViewComponent
,我只需要调用两次!我如何以及在哪里可以知道调用计数?
目前我可以使用会话,但我不喜欢在 mvc 应用程序中使用会话!我怎样才能做到这一点?
namespace Partials.Components
{
public class MyComponent : ViewComponent
{
public IViewComponentResult Invoke()
{
Session["invoked"]=(int)Session["invoked"]+1;
var model = new{
Website="Stack Overflow",
Url="www.http://whosebug.com"
};
return View("_MyComponent ", model);
}
}
}
在我看来
@Component.Invoke("MyComponent")
<span>Invoked ViewComponent <span>@Session["invoked"]</span> times</span>
您可以使用临时数据。它只持续到下一个请求。
TempData["invoked"]=(int)TempData["invoked"]+1;
查看:
<span>Invoked ViewComponent <span>@TempData["invoked"]</span> times</span>
注意:TempData 在幕后使用会话。
您可以使用 HttpContext.Items
,它的优点是不使用会话。这些项目根据请求存储和共享,这也适合您的 objective.
在您的 viewComponent 中,您可以 add/retrieve 一个项目,如 this.Context.Items["MyComponentInvocationCount"]
。只要计数大于 2,您就可以 return 空内容 return Content(String.Empty)
.
您可以将其与扩展方法结合使用,这样您就可以从 class:
外部获取计数
[ViewComponent(Name = "MyComponent")]
public class MyViewComponent : ViewComponent
{
internal static readonly string ContextItemName = "InvocationCount";
public IViewComponentResult Invoke()
{
this.InvocationCount = this.InvocationCount + 1;
if (this.InvocationCount > 2) return Content(String.Empty);
//return your content here
return Content("Can be invoked");
}
private int InvocationCount
{
get
{
return this.Context.InvocationCount();
}
set
{
this.Context.Items[ContextItemName] = value;
}
}
}
public static class MyViewComponentExtensions
{
public static int InvocationCount(this HttpContext context)
{
var count = context.Items[MyViewComponent.ContextItemName];
return count == null ? 0 : (int)count;
}
}
然后您可以在视图中使用它,如下所示:
@Component.Invoke("MyComponent")
<span>Invoked ViewComponent <span>@Context.InvocationCount()</span> times</span>
如果您在一个视图中添加上述行 3 次,您将看到第三行没有添加任何内容。
编辑 - 使用 ViewComponentInvoker
我一直在探索如何通过添加自定义 ViewComponentInvoker
.
来实现此功能
我首先添加了一个可用于装饰 ViewComponents 的新属性,以便将它们限制为每个请求的特定调用次数:
public class PerRequestInvocationLimitAttribute: Attribute
{
public int PerRequestInvocationLimit { get; set; }
}
然后您将像往常一样创建视图组件,唯一的变化是添加此属性:
[PerRequestInvocationLimit(PerRequestInvocationLimit = 2)]
public class MyViewComponent : ViewComponent
{
//implementation of view component
}
然后我们可以创建一个自定义 IViewComponentInvoker
来装饰 DefaultViewComponentInvoker
。
- 此自定义视图组件调用程序将跟踪
在当前请求中调用视图组件的次数。
- 调用具有新属性的视图组件时,它只会
如果调用次数低于限制,则真正调用它。
实现此视图组件调用程序如下所示:
public class LimitedPerRequestViewComponentInvoker : IViewComponentInvoker
{
private readonly IViewComponentInvoker _defaultViewComponentInvoker;
public LimitedPerRequestViewComponentInvoker(IViewComponentInvoker defaultViewComponentInvoker)
{
this._defaultViewComponentInvoker = defaultViewComponentInvoker;
}
public void Invoke(ViewComponentContext context)
{
if (!CanInvokeViewComponent(context)) return;
this._defaultViewComponentInvoker.Invoke(context);
}
public Task InvokeAsync(ViewComponentContext context)
{
if (!CanInvokeViewComponent(context)) return Task.WhenAll();
return this._defaultViewComponentInvoker.InvokeAsync(context);
}
private bool CanInvokeViewComponent(ViewComponentContext context)
{
// 1. Increase invocation count
var increasedCount = context.ViewContext.HttpContext.IncreaseInvocationCount(
context.ViewComponentDescriptor.ShortName);
// 2. check if there is any limit for this viewComponent, if over the limit then return false
var limitAttribute = context.ViewComponentDescriptor.Type
.GetCustomAttributes(true)
.OfType<PerRequestInvocationLimitAttribute>()
.FirstOrDefault();
if (limitAttribute != null && limitAttribute.PerRequestInvocationLimit < increasedCount)
{
return false;
}
// 3. There is no limit set or the limit has not been reached yet
return true;
}
}
它使用一些扩展方法 set/get 来自 HttpContext.Items
的调用计数(您也可以在视图中使用它来获取视图组件被调用的次数)
public static class ViewComponentExtensions
{
public static int InvocationCount(this HttpContext context, string viewComponentName)
{
var count = context.Items[GetHttpContextItemsName(viewComponentName)];
return count == null ? 0 : (int)count;
}
internal static int IncreaseInvocationCount(this HttpContext context, string viewComponentName)
{
var count = context.InvocationCount(viewComponentName);
context.Items[GetHttpContextItemsName(viewComponentName)] = ++count;
return count;
}
private static string GetHttpContextItemsName(string viewComponentName)
{
return string.Format("InvocationCount-{0}", viewComponentName);
}
}
最后一部分是创建一个新的 IViewComponentInvokerFactory
替换 default one,因此它创建了一个新的自定义视图组件调用程序的实例,而不是默认的调用程序。您还需要在 Startup.cs:
上注册
public class MyViewComponentInvokerFactory : IViewComponentInvokerFactory
{
private readonly IServiceProvider _serviceProvider;
private readonly ITypeActivatorCache _typeActivatorCache;
private readonly IViewComponentActivator _viewComponentActivator;
public MyViewComponentInvokerFactory(IServiceProvider serviceProvider, ITypeActivatorCache typeActivatorCache, IViewComponentActivator viewComponentActivator)
{
_serviceProvider = serviceProvider;
_typeActivatorCache = typeActivatorCache;
_viewComponentActivator = viewComponentActivator;
}
public IViewComponentInvoker CreateInstance(ViewComponentDescriptor viewComponentDescriptor, object[] args)
{
return new LimitedPerRequestViewComponentInvoker(
new DefaultViewComponentInvoker(_serviceProvider, _typeActivatorCache, _viewComponentActivator));
}
}
//Configure the ViewComponentInvokerFactory in Startup.ConfigureServices
services.AddTransient<IViewComponentInvokerFactory, MyViewComponentInvokerFactory>();
所有这些都准备就绪后,您可以使用视图组件 3 次,您将看到它如何只渲染两次:
@Component.Invoke("MyComponent")
<span>Invoked ViewComponent <span>@Context.InvocationCount("MyComponent")</span> times</span>
出于以下几个原因,我更喜欢此解决方案:
- 它基于新的mvc框架提供的钩子。
- 除了添加设置调用限制的属性外,不需要更改您的视图组件。
- 异步调用视图组件时有效
我有一个 ViewComponent
,我只需要调用两次!我如何以及在哪里可以知道调用计数?
目前我可以使用会话,但我不喜欢在 mvc 应用程序中使用会话!我怎样才能做到这一点?
namespace Partials.Components
{
public class MyComponent : ViewComponent
{
public IViewComponentResult Invoke()
{
Session["invoked"]=(int)Session["invoked"]+1;
var model = new{
Website="Stack Overflow",
Url="www.http://whosebug.com"
};
return View("_MyComponent ", model);
}
}
}
在我看来
@Component.Invoke("MyComponent")
<span>Invoked ViewComponent <span>@Session["invoked"]</span> times</span>
您可以使用临时数据。它只持续到下一个请求。
TempData["invoked"]=(int)TempData["invoked"]+1;
查看:
<span>Invoked ViewComponent <span>@TempData["invoked"]</span> times</span>
注意:TempData 在幕后使用会话。
您可以使用 HttpContext.Items
,它的优点是不使用会话。这些项目根据请求存储和共享,这也适合您的 objective.
在您的 viewComponent 中,您可以 add/retrieve 一个项目,如 this.Context.Items["MyComponentInvocationCount"]
。只要计数大于 2,您就可以 return 空内容 return Content(String.Empty)
.
您可以将其与扩展方法结合使用,这样您就可以从 class:
外部获取计数[ViewComponent(Name = "MyComponent")]
public class MyViewComponent : ViewComponent
{
internal static readonly string ContextItemName = "InvocationCount";
public IViewComponentResult Invoke()
{
this.InvocationCount = this.InvocationCount + 1;
if (this.InvocationCount > 2) return Content(String.Empty);
//return your content here
return Content("Can be invoked");
}
private int InvocationCount
{
get
{
return this.Context.InvocationCount();
}
set
{
this.Context.Items[ContextItemName] = value;
}
}
}
public static class MyViewComponentExtensions
{
public static int InvocationCount(this HttpContext context)
{
var count = context.Items[MyViewComponent.ContextItemName];
return count == null ? 0 : (int)count;
}
}
然后您可以在视图中使用它,如下所示:
@Component.Invoke("MyComponent")
<span>Invoked ViewComponent <span>@Context.InvocationCount()</span> times</span>
如果您在一个视图中添加上述行 3 次,您将看到第三行没有添加任何内容。
编辑 - 使用 ViewComponentInvoker
我一直在探索如何通过添加自定义 ViewComponentInvoker
.
我首先添加了一个可用于装饰 ViewComponents 的新属性,以便将它们限制为每个请求的特定调用次数:
public class PerRequestInvocationLimitAttribute: Attribute
{
public int PerRequestInvocationLimit { get; set; }
}
然后您将像往常一样创建视图组件,唯一的变化是添加此属性:
[PerRequestInvocationLimit(PerRequestInvocationLimit = 2)]
public class MyViewComponent : ViewComponent
{
//implementation of view component
}
然后我们可以创建一个自定义 IViewComponentInvoker
来装饰 DefaultViewComponentInvoker
。
- 此自定义视图组件调用程序将跟踪 在当前请求中调用视图组件的次数。
- 调用具有新属性的视图组件时,它只会 如果调用次数低于限制,则真正调用它。
实现此视图组件调用程序如下所示:
public class LimitedPerRequestViewComponentInvoker : IViewComponentInvoker
{
private readonly IViewComponentInvoker _defaultViewComponentInvoker;
public LimitedPerRequestViewComponentInvoker(IViewComponentInvoker defaultViewComponentInvoker)
{
this._defaultViewComponentInvoker = defaultViewComponentInvoker;
}
public void Invoke(ViewComponentContext context)
{
if (!CanInvokeViewComponent(context)) return;
this._defaultViewComponentInvoker.Invoke(context);
}
public Task InvokeAsync(ViewComponentContext context)
{
if (!CanInvokeViewComponent(context)) return Task.WhenAll();
return this._defaultViewComponentInvoker.InvokeAsync(context);
}
private bool CanInvokeViewComponent(ViewComponentContext context)
{
// 1. Increase invocation count
var increasedCount = context.ViewContext.HttpContext.IncreaseInvocationCount(
context.ViewComponentDescriptor.ShortName);
// 2. check if there is any limit for this viewComponent, if over the limit then return false
var limitAttribute = context.ViewComponentDescriptor.Type
.GetCustomAttributes(true)
.OfType<PerRequestInvocationLimitAttribute>()
.FirstOrDefault();
if (limitAttribute != null && limitAttribute.PerRequestInvocationLimit < increasedCount)
{
return false;
}
// 3. There is no limit set or the limit has not been reached yet
return true;
}
}
它使用一些扩展方法 set/get 来自 HttpContext.Items
的调用计数(您也可以在视图中使用它来获取视图组件被调用的次数)
public static class ViewComponentExtensions
{
public static int InvocationCount(this HttpContext context, string viewComponentName)
{
var count = context.Items[GetHttpContextItemsName(viewComponentName)];
return count == null ? 0 : (int)count;
}
internal static int IncreaseInvocationCount(this HttpContext context, string viewComponentName)
{
var count = context.InvocationCount(viewComponentName);
context.Items[GetHttpContextItemsName(viewComponentName)] = ++count;
return count;
}
private static string GetHttpContextItemsName(string viewComponentName)
{
return string.Format("InvocationCount-{0}", viewComponentName);
}
}
最后一部分是创建一个新的 IViewComponentInvokerFactory
替换 default one,因此它创建了一个新的自定义视图组件调用程序的实例,而不是默认的调用程序。您还需要在 Startup.cs:
public class MyViewComponentInvokerFactory : IViewComponentInvokerFactory
{
private readonly IServiceProvider _serviceProvider;
private readonly ITypeActivatorCache _typeActivatorCache;
private readonly IViewComponentActivator _viewComponentActivator;
public MyViewComponentInvokerFactory(IServiceProvider serviceProvider, ITypeActivatorCache typeActivatorCache, IViewComponentActivator viewComponentActivator)
{
_serviceProvider = serviceProvider;
_typeActivatorCache = typeActivatorCache;
_viewComponentActivator = viewComponentActivator;
}
public IViewComponentInvoker CreateInstance(ViewComponentDescriptor viewComponentDescriptor, object[] args)
{
return new LimitedPerRequestViewComponentInvoker(
new DefaultViewComponentInvoker(_serviceProvider, _typeActivatorCache, _viewComponentActivator));
}
}
//Configure the ViewComponentInvokerFactory in Startup.ConfigureServices
services.AddTransient<IViewComponentInvokerFactory, MyViewComponentInvokerFactory>();
所有这些都准备就绪后,您可以使用视图组件 3 次,您将看到它如何只渲染两次:
@Component.Invoke("MyComponent")
<span>Invoked ViewComponent <span>@Context.InvocationCount("MyComponent")</span> times</span>
出于以下几个原因,我更喜欢此解决方案:
- 它基于新的mvc框架提供的钩子。
- 除了添加设置调用限制的属性外,不需要更改您的视图组件。
- 异步调用视图组件时有效