在本文中,我为建立的自定义的DfaGraphWriter实现奠基了基础。DfaGraphWriter是公然的,因此您可以如上一篇文章中所示在应用程序中使用它,但它使用的所有类均已标记为internal。这使得建立自己的版本成为问题。要解决此问题,我使用了一个开源的反射库ImpromptuInterface,使建立自定义的DfaGraphWriter实现加倍容易。

作者:依乐祝
原文地址:https://andrewlock.net/creating-a-custom-dfagraphwriter-using-impromptuinterface-and reflection/
译文地址:https://www.cnblogs.com/yilezhu/p/13336066.html

我们将从查看现有的DfaGraphWriter最先,以领会其使用的internal类以及导致我们的问题。然后,我们来看一下使用一些自定义接口和ImpromptuInterface库来允许我们挪用这些类。在下一篇文章中,我们将研究若何使用自定义界面建立的自定义版本DfaGraphWriter

探索现有的 DfaGraphWriter

DfaGraphWriter类是存在于ASP.NET Core中的一个“pubternal”文件夹中的。它已注册为单例,并使用注入的IServiceProvider来剖析DfaMatcherBuilder

 public class DfaGraphWriter
{
    private readonly IServiceProvider _services;
    public DfaGraphWriter(IServiceProvider services)
    {
        _services = services;
    }

    public void Write(EndpointDataSource dataSource, TextWriter writer)
    {
        // retrieve the required DfaMatcherBuilder
        var builder = _services.GetRequiredService<DfaMatcherBuilder>();

        // loop through the endpoints in the dataSource, and add them to the builder
        var endpoints = dataSource.Endpoints;
        for (var i = 0; i < endpoints.Count; i++)
        {
            if (endpoints[i] is RouteEndpoint endpoint && (endpoint.Metadata.GetMetadata<ISuppressMatchingMetadata>()?.SuppressMatching ?? false) == false)
            {
                builder.AddEndpoint(endpoint);
            }
        }

        // Build the DfaTree. 
        // This is what we use to create the endpoint graph
        var tree = builder.BuildDfaTree(includeLabel: true);

        // Add the header
        writer.WriteLine("digraph DFA {");

        // Visit each node in the graph to create the output
        tree.Visit(WriteNode);

        //Close the graph
        writer.WriteLine("}");

        // Recursively walks the tree, writing it to the TextWriter
        void WriteNode(DfaNode node)
        {
            // Removed for brevity - we'll explore it in the next post
        }
    }
}

上面的代码显示了图形编写者Write方式的所有操作,终结如下:

  • 获取一个 DfaMatcherBuilder
  • 写入所有的端点EndpointDataSourceDfaMatcherBuilder
  • 挪用DfaMatcherBuilderBuildDfaTree。这将建立一个DfaNode的 图。
  • 接见DfaNode树中的每一个,并将其写入TextWriter输出。我们将在下一篇文章中探讨这种方式。

建立我们自己的自定义编写器的目的是通过控制若何将差别的节点写入输出来定制最后一步,因此我们可以建立更多的描述性的图形,如我先前所示:

我们的问题是两个重点类,DfaMatcherBuilderDfaNode,是internal以是我们不能容易实例化它们,或者使用它们的写入方式。这给出了两个选择:

  • 重新实现这些internal类,包罗它们依赖的其他任何internal类。
  • 使用反射在现有类上建立和挪用方式。

这些都不是很好的选择,然则鉴于端点图不是性能要害的器械,我决议使用反射将是最简朴的。为了使事情变得加倍简朴,我使用了开源库ImpromptuInterface

ImpromptuInterface使反射更容易

ImpromptuInterface是一个库它使挪用动态工具或挪用存储在工具引用中的底层工具上的方式变得加倍容易。它本质上增加了简朴的duck/structural类型,允许您为工具使用stronlgy类型化接口。它使用Dynamic Language Runtime和Reflection.Emit来实现。

例如,让我们获取我们要使用的现有DfaMatcherBuilder类。纵然我们不能直接引用它,我们仍然可以从DI容器中获取此类的实例,如下所示:

// get the DfaMatcherBuilder type - internal, so needs reflection :(
Type matcherBuilder = typeof(IEndpointSelectorPolicy).Assembly
    .GetType("Microsoft.AspNetCore.Routing.Matching.DfaMatcherBuilder");

object rawBuilder = _services.GetRequiredService(matcherBuilder);

rawBuilder是一个object引用,但它包罗了一个DfaMatcherBuilder的实例。我们不能直接在挪用它的方式,然则我们可以通过直接构建MethodInfo和直接挪用invoke来使用反射来挪用它们。。

ImpromptuInterface通过提供一个可以直接挪用方式的静态接口,使该历程加倍容易。例如,对于DfaMatcherBuilder,我们只需要挪用两个方式AddEndpointBuildDfaTree。原始类如下所示:

internal class DfaMatcherBuilder : MatcherBuilder
{
    public override void AddEndpoint(RouteEndpoint endpoint) { /* body */ }
    public DfaNode BuildDfaTree(bool includeLabel = false)
}

我们可以建立一个露出这些方式的接口:

public interface IDfaMatcherBuilder
{
    void AddEndpoint(RouteEndpoint endpoint);
    object BuildDfaTree(bool includeLabel = false);
}

然后,我们可以使用ImpromptuInterface ActLike<>方式建立实现了IDfaMatcherBuilder的署理工具。此署理包装rawbuilder工具,因此当您在接口上挪用方式时,它将在底层挪用DfaMatcherBuilder中的等效的方式:

在代码中,如下所示:

// An instance of DfaMatcherBuilder in an object reference
object rawBuilder = _services.GetRequiredService(matcherBuilder);

// wrap the instance in the ImpromptuInterface interface
IDfaMatcherBuilder builder = rawBuilder.ActLike<IDfaMatcherBuilder>();

// we can now call methods on the builder directly, e.g. 
object rawTree =  builder.BuildDfaTree();

原始DfaMatcherBuilder.BuildDfaTree()方式和接口版本之间有一个主要区别:原始方式返回一个DfaNode,但这是另一个internal类,因此我们无法在接口中引用它。

相反,我们为DfaNode类建立另一个ImpromptuInterface,露出我们将需要的属性(在接下来的文章中你就会明了为什么我们需要他们):

public interface IDfaNode
{
    public string Label { get; set; }
    public List<Endpoint> Matches { get; }
    public IDictionary Literals { get; } // actually a Dictionary<string, DfaNode>
    public object Parameters { get; } // actually a DfaNode
    public object CatchAll { get; } // actually a DfaNode
    public IDictionary PolicyEdges { get; } // actually a Dictionary<object, DfaNode>
}

在下一篇文章中,我们将在WriteNode的方式中使用这些属性,然则有一些复杂性。在原始DfaNode类中,ParametersCatchAll属性返回DfaNode工具。在我们IDfaNode版本的属性中,我们必须返回object。我们无法引用DfaNode(由于是internal)而且我们不能返回IDfaNode,由于DfaNode 它没有实现IDfaNode,因此您不能将object引用隐式转换为IDfaNode。你必须使用ImpromptuInterface显式地添加一个实现了接口的署理,。

例如:

// Wrap the instance in the ImpromptuInterface interface
IDfaMatcherBuilder builder = rawBuilder.ActLike<IDfaMatcherBuilder>();

// We can now call methods on the builder directly, e.g. 
object rawTree =  builder.BuildDfaTree();
// Use ImpromptuInterface to add an IDfaNode wrapper
IDfaNode tree = rawTree.ActLike<IDfaNode>();

// We can now call methods and properties on the node...
object rawParameters = tree.Parameters;
// ...but they need to be wrapped using ImpromptuInterface too
IDfaNode parameters = rawParameters.ActLike<IDfaNode>();

返回Dictionary类型的属性另有另一个问题:LiteralsPolicyEdges。现实返回的类型分别为Dictionary<string, DfaNode>Dictionary<object, DfaNode>,然则我们需要使用一个包罗该DfaNode类型的类型。不幸的是,这意味着我们不得不退回到.NET 1.1 IDictionary接口!

您不能将一个Dictionary<string, DfaNode>强制转换为IDictionary<string, object>,由于这样做将是不安全的协方差形式。

IDictionary是一个非泛型接口,因此keyvalue仅作为object公然。对于string键,您可以直接举行转换,对于,DfaNode我们可以使用ImpromptuInterface为我们建立署理包装器:

// Enumerate the key-value pairs as DictinoaryEntrys
foreach (DictionaryEntry dictEntry in node.Literals)
{
    // Cast the key value to a string directly
    var key = (string)dictEntry.Key;
    // Use ImpromptuInterface to add a wrapper
    IDfaNode value = dictEntry.Value.ActLike<IDfaNode>();
}

现在,我们已经拥有了通过实现WriteNode来建立自定义DfaWriter实现所需的一切工具,然则这篇文章已经有点长了,以是我们将在下一篇文章中探讨若何实现这一点!

摘要

在本文中,我探讨了DfaWriter在ASP.NET Core 中的实现以及它使用的两个internal类:DfaMatcherBuilderDfaNode。这些类是内部类的事实使得建立我们自己的DfaWriter实现异常棘手。为了清洁地实现它,我们将不得不重新实现这两种类型以及它们所依赖的所有类。

作为替换,我使用ImpromptuInterface库建立了一个包装器署理,该署理实现与被包装的工具拥有类似的方式。这使用反射来挪用包装属性上的方式,但允许我们使用强类型接口。在下一篇文章中,我将展示若何使用这些包装器建立一个定制的DfaWriter来举行端点图的自定义。

,

欧博亚洲手机版下载

欢迎进入欧博亚洲手机版下载(Allbet Game):www.aLLbetgame.us,欧博官网是欧博集团的官方网站。欧博官网开放Allbet注册、Allbe代理、Allbet电脑客户端、Allbet手机版下载等业务。

Allbet Gaming声明:该文看法仅代表作者自己,与阳光在线无关。转载请注明:allbet手机版下载:使用ImpromptuInterface反射库利便的建立自定义DfaGraphWriter
发布评论

分享到:

今日洛阳新闻专题:四个实例告诉你 为什么买车不能光凭主观印象
1 条回复
  1. 欧博allbet网址
    欧博allbet网址
    (2020-08-23 00:02:17) 1#

    欧博亚洲网址欢迎进入欧博亚洲网址(Allbet Game):www.aLLbetgame.us,欧博官网是欧博集团的官方网站。欧博官网开放Allbet注册、Allbe代理、Allbet电脑客户端、Allbet手机版下载等业务。总觉得你可以更好

发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。