Kon*_*ski 5 asp.net-mvc dependency-injection autofac asp.net-mvc-5
我想将属性渲染器实现为处理程序。我在应用程序中使用 Autofac 作为 DI 容器。如何在不使用全局可访问容器(服务位置)的情况下获取在 HtmlHelper 扩展中实现 IPropertyHandler 的对象?是否可以在 Autofac 中注册自己的 HtmlHelper?也许MVC框架提供了另一种方式?
public static class HtmlHelperExtensions {
public static MvcHtmlString Editor(this HtmlHelper html, object model) {
return new Renderer(new List<IPropertyHandler>() /*Where to get these objects?*/ ).Render(html, model);
}
}
public class Renderer {
private readonly ICollection<IPropertyHandler> _propertyRenderers;
public Renderer(ICollection<IPropertyHandler> propertyRenderers) {
_propertyRenderers = propertyRenderers;
}
public MvcHtmlString Render(HtmlHelper html, object model) {
var result = "";
foreach(var prop in model.GetType().GetProperties()) {
var renderers = _propertyRenderers.OrderBy(b => b.Order);
//impl
}
return new MvcHtmlString(result);
}
}
Run Code Online (Sandbox Code Playgroud)
AFAIK,MVC 5 没有提供一种方法来做到这一点。但这并不意味着您不能建立自己的解决方案。
MVC Core 现在使用对DI 友好的视图组件,因此您不必跳过这么多圈套。
根据Mark Seemann的文章DI Friendly Framework,您可以为您的 HTML 助手创建一个工厂接口,该接口可用于实例化它的依赖项。
首先有一个默认工厂,它提供逻辑默认行为(无论是什么)。
public interface IRendererFactory
{
IRenderer Create();
void Release(IRenderer renderer);
}
public class DefaultRendererFactory : IRendererFactory
{
public virtual IRenderer Create()
{
return new Renderer(new IPropertyHandler[] { new DefaultPropertyHandler1(), DefaultPropertyHandler2() });
}
public virtual void Release(IRenderer renderer)
{
if (renderer is IDisposable disposable)
{
disposable.Dispose();
}
}
}
Run Code Online (Sandbox Code Playgroud)
您可能希望使这个默认工厂更智能,或者甚至使用 fluent builder 来提供其依赖项,如另一篇文章DI Friendly Library那样,它在不使用 DI 容器的情况下更加灵活。
然后我们对 使用抽象Renderer,IRenderer因此它可以轻松交换和/或通过 DI 提供。
public interface IRenderer
{
MvcHtmlString Render(HtmlHelper html, object model);
}
public class Renderer : IRenderer
{
private readonly ICollection<IPropertyHandler> _propertyRenderers;
public Renderer(ICollection<IPropertyHandler> propertyRenderers)
{
_propertyRenderers = propertyRenderers;
}
public MvcHtmlString Render(HtmlHelper html, object model)
{
var result = "";
foreach(var prop in model.GetType().GetProperties())
{
var renderers = _propertyRenderers.OrderBy(b => b.Order);
//impl
}
return new MvcHtmlString(result);
}
}
Run Code Online (Sandbox Code Playgroud)
接下来,我们提供一个钩子来注册工厂。由于 HTML helper 是一个静态扩展方法,唯一的选择是创建一个带有静态属性或方法的静态字段来设置它。如果需要在工厂中使用装饰器模式,那么制作吸气剂总是很好的做法。
public interface IRendererFactory
{
IRenderer Create();
void Release(IRenderer renderer);
}
public static class HtmlHelperExtensions {
private static IRendererFactory rendererFactory = new DefaultRendererFactory();
public static IRendererFactory RendererFactory
{
get => rendererFactory;
set => rendererFactory = value;
}
public static MvcHtmlString Editor(this HtmlHelper html, object model) {
var renderer = rendererFactory.Create();
try
{
return renderer.Render(html, model);
}
finally
{
rendererFactory.Release(renderer);
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果这对应用程序更有意义,您可以提供一些逻辑位置来静态注册所有工厂。但是基本上每个 HTML 助手都需要一个工厂来遵守 SRP。如果您尝试概括,您基本上会回到服务定位器。
现在所有的部分都已就位,这就是您将 Autofac 放入等式的方法。您将需要一个自定义IRendererFactory,您将使其成为特定于 Autofac 的合成根的一部分。
public class AutofacRendererFactory : IRendererFactory
{
private readonly Autofac.IContainer container;
public AutofacRendererFactory(Autofac.IContainer container)
{
this.container = container ?? new ArgumentNullException(nameof(container));
}
public IRenderer Create()
{
return this.container.Resolve(typeof(IRenderer));
}
public void Release(IRenderer renderer)
{
// allow autofac to release dependencies using lifetime management
}
}
Run Code Online (Sandbox Code Playgroud)
接下来,您需要将类型映射IRenderer及其依赖项添加到 Autofac。
最后但并非最不重要的是,您需要在创建 Autofac 容器后在应用程序启动中添加一行,以在应用程序需要时解析渲染器。
// Register all of your types with the builder
// ...
// ...
Autofac.IContainer container = builder.Build();
HtmlHelperExtensions.RendererFactory = new AutofacRendererFactory(container);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2898 次 |
| 最近记录: |