使用ImpromptuInterface反射方便的创建自定义DfaGraphWriter

在本文中,我为创建的自定义的DfaGraphWriter实现奠定了基础。DfaGraphWriter是公开的,因此您可以如上一篇文章《将终结点图添加到你的ASP.NET Core应用程序中》中所示在应用程序中使用它,但它使用的所有类均已标记为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 DfaMatcherBuildervar builder = _services.GetRequiredService<DfaMatcherBuilder>();// loop through the endpoints in the dataSource, and add them to the buildervar 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 graphvar tree = builder.BuildDfaTree(includeLabel: true);// Add the headerwriter.WriteLine("digraph DFA {");// Visit each node in the graph to create the outputtree.Visit(WriteNode);//Close the graphwriter.WriteLine("}");// Recursively walks the tree, writing it to the TextWritervoid 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 DfaNodepublic object CatchAll { get; } // actually a DfaNodepublic 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 directlyvar key = (string)dictEntry.Key;// Use ImpromptuInterface to add a wrapperIDfaNode value = dictEntry.Value.ActLike<IDfaNode>();
}

现在,我们已经拥有了通过实现WriteNode来创建自定义DfaWriter实现所需的一切对象,但是这篇文章已经有点长了,所以我们将在下一篇文章中探讨如何实现这一点!

摘要

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

作为替代,我使用ImpromptuInterface库创建了一个包装器代理,该代理实现与被包装的对象拥有类似的方法。这使用反射来调用包装属性上的方法,但允许我们使用强类型接口。在下一篇文章中,我将展示如何使用这些包装器创建一个定制的DfaWriter来进行端点图的自定义。

相关阅读:

[译]使用DOT语言和GraphvizOnline来可视化你的ASP.NETCore3.0终结点01

将终结点图添加到你的ASP.NET Core应用程序中

往期精彩回顾

【推荐】.NET Core开发实战视频课程 ★★★

.NET Core实战项目之CMS 第一章 入门篇-开篇及总体规划

【.NET Core微服务实战-统一身份认证】开篇及目录索引

Redis基本使用及百亿数据量中的使用技巧分享(附视频地址及观看指南)

.NET Core中的一个接口多种实现的依赖注入与动态选择看这篇就够了

10个小技巧助您写出高性能的ASP.NET Core代码

用abp vNext快速开发Quartz.NET定时任务管理界面

在ASP.NET Core中创建基于Quartz.NET托管服务轻松实现作业调度

现身说法:实际业务出发分析百亿数据量下的多表查询优化

关于C#异步编程你应该了解的几点建议

C#异步编程看这篇就够了

给我好看 
您看此文用  · 秒,转发只需1秒呦~
好看你就点点我

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/308662.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[RabbitMQ]消息应答概念_消息手动应答代码

消息应答 概念 消费者完成一个任务可能需要一段时间&#xff0c;如果其中一个消费者处理一个长的任务并仅只完成了部分突然它挂掉了&#xff0c;会发生什么情况。RabbitMQ 一旦向消费者传递了一条消息&#xff0c;便立即将该消 息标记为删除。在这种情况下&#xff0c;突然有…

rust火箭基地主楼开启方法_Rust 为什么能成为 Stack Overflow 最受欢迎的语言?

每年&#xff0c;开发者问答网站 Stack Overflow 都会对程序员社区展开年度调查&#xff0c;包括他们最喜爱的技术到工作偏好的所有内容。 在2017 年和2018 年Stack Overflow 年度开发者调查中&#xff0c;Rust语言已经连续两年成为最受欢迎语言Top 1。2018 年 Stack Overflow …

[RabbitMQ]队列持久化

RabbitMQ持久化 概念 如何保障当 RabbitMQ 服务停掉以后消息生产者发送过来的消息不丢失。默认情况下 RabbitMQ 退出或由于某种原因崩溃时&#xff0c;它忽视队列和消息&#xff0c;除非告知它不要这样做。确保消息不会丢失需要做两件事&#xff1a;我们需要将队列和消息都标…

微服务认证架构如何演进来的?

【答疑解惑】| 作者 / Edison Zhou这是恰童鞋骚年的第267篇原创内容之前有同事问为何要用基于JWT令牌的认证架构&#xff0c;然后近期又有童鞋在后台留言问微服务安全认证架构的实践&#xff0c;因此我决定花两篇推文来解答一下。为了答好这个话题&#xff0c;我们先来看看微服…

maskrcnn还可以加网络吗_绿茶加蜂蜜的功效,绿茶可以加蜂蜜吗?

绿茶是我国的主要茶类之一&#xff0c;是一种天然健康的饮料&#xff0c;蜂蜜也是一种营养丰富的滋补食品&#xff0c;有些人不喜欢绿茶的苦味&#xff0c;想放点蜂蜜中和一下&#xff0c;但是不知道能不能这样做。那么绿茶能不能加蜂蜜呢?蜂蜜的主要成分是葡萄糖、果糖&#…

三分钟Docker-镜像、容器实战篇

本文主要内容&#xff1a;Docker 镜像、容器 常用命令整理使用Docker常见命令&#xff0c;搭建Consul集群通过创建自定义镜像&#xff0c;把.NetCore Api运行在Docker中1.镜像、容器命令镜像序号命令描述1docker image build基于Dockerfile创建镜像2docker image history显示镜…

手机键鼠映射软件_吃鸡,我最专业!---盖世小鸡键鼠吃鸡套装评测

Hello大家好&#xff0c;欢迎浏览这篇评测贴。首先很荣幸能够参与本期的评测&#xff0c;毕竟如此炫酷富有科技感的装备是可遇而不可求的&#xff0c;所以不论是得知入选还是收到快递开箱的时候&#xff0c;心情都是无比激动。话不多说&#xff0c;接下来就让我带你走进这个不一…

[Redis6]Redis启动_前台启动和后台启动

前台启动(不推荐) 前台启动&#xff0c;命令行窗口不能关闭&#xff0c;否则服务器停止 redis-server 关闭redis ctrlC : 关闭 后台启动&#xff08;推荐&#xff09; 备份redis.conf cd redis-6.2.6/cp redis.conf /etc/redis.confcd /etc后台启动设置daemonize no改成y…

深入剖析.NETCORE中CORS(跨站资源共享)

前言由于现代互联网的飞速发展&#xff0c;我们在开发现代 Web 应用程序中&#xff0c;经常需要考虑多种类型的客户端访问服务的情况&#xff1b;而这种情况放在15年前几乎是不可想象的&#xff0c;在那个时代&#xff0c;我们更多的是考虑怎么把网页快速友好的嵌套到服务代码中…

ai进入轮廓模式怎么退出_详解AI中扩展、扩展外观、轮廓化描边、创建轮廓

详解AI中扩展、扩展外观、轮廓化描边、创建轮廓在学习AI软件中&#xff0c;有不少同学分不清扩展、扩展外观、轮廓化描边、创建轮廓这四个概念具体的功能区别&#xff0c;今天我们具体聊一下。先说“扩展”&#xff0c;扩展是把复杂物体拆分成最基本的路径。矢量物体在组合&…

[Redis6]Redis相关知识介绍

Redis介绍相关知识 端口6379 6379 是 "MERZ " 九宫格输入法对应的数字。Alessia Merz 是一位意大利舞女、女演员。 Redis 作者 Antirez 早年看电视节目&#xff0c;觉得 Merz 在节目中的一些话愚蠢可笑&#xff0c;Antirez 喜欢造“梗”用于平时和朋友们交流&#x…

【Power Automate】如何自动生成Word与PDF文件[上]

上半年已经悄悄溜走&#xff0c;因为疫情&#xff0c;大家似乎也很习惯于在家办公。作为业务人员&#xff0c;如何汇报自己的工作&#xff0c;让自己更多地学习和掌握数字化办公技巧至关重要。那么今天我们就来看一下在不使用代码的情况下&#xff0c;如何通过Power Automate自…

easyui datagrid 中怎么选中所有页面的数据_学会这5个Excel中常用技巧,可以准时下班去摆摊了...

Excel是大家常用的办公工具之一&#xff0c;虽说上手简单&#xff0c;但是想要精通还是要下一些功夫的。最近有些小伙伴在留言吐槽说Excel数据处理时很方便&#xff0c;但是操作起来还是挺费时间的&#xff0c;其实工作是离不开技巧的&#xff0c;今天要跟大家分享的5个Excel技…

五年了,别再把务虚会开 “虚” 了

这是头哥侃码的第210篇原创上个月&#xff0c;为了配合公司的半年度战略讨论会&#xff0c;我特意留出一个周六的时间&#xff0c;与几位Leader在公司的会议室里开了一次部门半年度务虚会。让我没想到的是&#xff0c;几位小伙伴在这次讨论过程中都表现得非常亢奋&#xff0c;所…

人工智能正在如何改变传统行业

做了这么多年的技术工作&#xff0c;也正好赶上了这一波的人工智能浪潮&#xff0c;有时候我总是不免在想&#xff0c;人工智能如何真正地融入到我们的日常工作和生活中&#xff0c;实现它应有的价值。大家可能不知道&#xff0c;人工智能其实最早在上个世纪五十年代就提出来了…

[Redis6]key键操作

我们先连接redis cd /usr/local/bin/ redis-cliRedis键(key) keys *查看当前库所有key (匹配&#xff1a;keys *1) exists key判断某个key是否存在 type key 查看你的key是什么类型 del key 删除指定的key数据 unlink key 根据value选择非阻塞删除 仅将keys从keyspace元数据…

继续分享 5 个实用的 vs 调试技巧

前言我在上一篇文章????《5 个非常实用的 vs 调试技巧》 中分享了 5 个我认为非常值得了解的 vs 调试技巧&#xff0c;本周继续分享 5 个很基础但同样实用的调试技巧。1. 条件断点作用简介&#xff1a;顾名思义&#xff0c;带条件的断点。满足条件才中断。条件断点非常非常…

[Redis6]常用数据类型_String字符串

Redis字符串(String) 简介 String是Redis最基本的类型&#xff0c;你可以理解成与Memcached一模一样的类型&#xff0c;一个key对应一个value。String类型是二进制安全的。意味着Redis的string可以包含任何数据。比如jpg图片或者序列化的对象。String类型是Redis最基本的数据…

在ASP.NET Core中创建自定义端点可视化图

在上篇文章中&#xff0c;我为构建自定义端点可视化图奠定了基础&#xff0c;正如我在第一篇文章中展示的那样。该图显示了端点路由的不同部分&#xff1a;文字值&#xff0c;参数&#xff0c;动词约束和产生结果的端点&#xff1a;在本文中&#xff0c;我将展示如何通过创建一…

[Redis6]常用数据类型_List列表

List列表 简介 单键多值 Redis 列表是简单的字符串列表&#xff0c;按照插入顺序排序。你可以添加一个元素到列表的头部&#xff08;左边&#xff09;或者尾部&#xff08;右边&#xff09;。 它的底层实际是个双向链表&#xff0c;对两端的操作性能很高&#xff0c;通过索…