使用ImpromptuInterface反射方便的创建自定义DfaGraphWriter
在本文中,我為創(chuàng)建的自定義的DfaGraphWriter實(shí)現(xiàn)奠定了基礎(chǔ)。DfaGraphWriter是公開的,因此您可以如上一篇文章《將終結(jié)點(diǎn)圖添加到你的ASP.NET Core應(yīng)用程序中》中所示在應(yīng)用程序中使用它,但它使用的所有類均已標(biāo)記為internal。這使得創(chuàng)建自己的版本成為問題。要解決此問題,我使用了一個(gè)開源的反射庫ImpromptuInterface,使創(chuàng)建自定義的DfaGraphWriter實(shí)現(xiàn)更加容易。
作者:依樂祝
原文地址:https://andrewlock.net/creating-a-custom-dfagraphwriter-using-impromptuinterface-and reflection/
譯文地址:https://www.cnblogs.com/yilezhu/p/13336066.html
我們將從查看現(xiàn)有的DfaGraphWriter開始,以了解其使用的internal類以及導(dǎo)致我們的問題。然后,我們來看一下使用一些自定義接口和ImpromptuInterface庫來允許我們調(diào)用這些類。在下一篇文章中,我們將研究如何使用自定義界面創(chuàng)建的自定義版本DfaGraphWriter。
探索現(xiàn)有的?DfaGraphWriter
該DfaGraphWriter類是存在于ASP.NET Core中的一個(gè)“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方法的所有操作,總結(jié)如下:
獲取一個(gè)?DfaMatcherBuilder
寫入所有的端點(diǎn)EndpointDataSource到DfaMatcherBuilder。
調(diào)用DfaMatcherBuilder的BuildDfaTree。這將創(chuàng)建一個(gè)DfaNode的 圖。
訪問DfaNode樹中的每一個(gè),并將其寫入TextWriter輸出。我們將在下一篇文章中探討這種方法。
創(chuàng)建我們自己的自定義編寫器的目的是通過控制如何將不同的節(jié)點(diǎn)寫入輸出來定制最后一步,因此我們可以創(chuàng)建更多的描述性的圖形,如我先前所示:
我們的問題是兩個(gè)重點(diǎn)類,DfaMatcherBuilder和DfaNode,是internal所以我們不能輕易實(shí)例化它們,或者使用它們的寫入方法。這給出了兩個(gè)選擇:
重新實(shí)現(xiàn)這些internal類,包括它們依賴的其他任何internal類。
使用反射在現(xiàn)有類上創(chuàng)建和調(diào)用方法。
這些都不是很好的選擇,但是鑒于端點(diǎn)圖不是性能關(guān)鍵的東西,我決定使用反射將是最簡單的。為了使事情變得更加簡單,我使用了開源庫ImpromptuInterface。
ImpromptuInterface使反射更容易
ImpromptuInterface是一個(gè)庫它使調(diào)用動(dòng)態(tài)對象或調(diào)用存儲在對象引用中的底層對象上的方法變得更加容易。它本質(zhì)上增加了簡單的duck/structural類型,允許您為對象使用stronlgy類型化接口。它使用Dynamic Language Runtime和Reflection.Emit來實(shí)現(xiàn)。
例如,讓我們獲取我們要使用的現(xiàn)有DfaMatcherBuilder類。即使我們不能直接引用它,我們?nèi)匀豢梢詮腄I容器中獲取此類的實(shí)例,如下所示:
// 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是一個(gè)object引用,但它包含了一個(gè)DfaMatcherBuilder的實(shí)例。我們不能直接在調(diào)用它的方法,但是我們可以通過直接構(gòu)建MethodInfo和直接調(diào)用invoke來使用反射來調(diào)用它們。。
ImpromptuInterface通過提供一個(gè)可以直接調(diào)用方法的靜態(tài)接口,使該過程更加容易。例如,對于DfaMatcherBuilder,我們只需要調(diào)用兩個(gè)方法AddEndpoint和BuildDfaTree。原始類如下所示:
internal class DfaMatcherBuilder : MatcherBuilder {public override void AddEndpoint(RouteEndpoint endpoint) { /* body */ }public DfaNode BuildDfaTree(bool includeLabel = false) }我們可以創(chuàng)建一個(gè)暴露這些方法的接口:
public interface IDfaMatcherBuilder {void AddEndpoint(RouteEndpoint endpoint);object BuildDfaTree(bool includeLabel = false); }然后,我們可以使用ImpromptuInterface?ActLike<>方法創(chuàng)建實(shí)現(xiàn)了IDfaMatcherBuilder的代理對象。此代理包裝rawbuilder對象,因此當(dāng)您在接口上調(diào)用方法時(shí),它將在底層調(diào)用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()方法和接口版本之間有一個(gè)重要區(qū)別:原始方法返回一個(gè)DfaNode,但這是另一個(gè)internal類,因此我們無法在接口中引用它。
相反,我們?yōu)镈faNode類創(chuàng)建另一個(gè)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的方法中使用這些屬性,但是有一些復(fù)雜性。在原始DfaNode類中,Parameters和CatchAll屬性返回DfaNode對象。在我們IDfaNode版本的屬性中,我們必須返回object。我們無法引用DfaNode(因?yàn)槭莍nternal)并且我們不能返回IDfaNode,因?yàn)镈faNode?它沒有實(shí)現(xiàn)IDfaNode,因此您不能將object引用隱式轉(zhuǎn)換為IDfaNode。你必須使用ImpromptuInterface來顯式地添加一個(gè)實(shí)現(xiàn)了接口的代理,。
例如:
// 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類型的屬性還有另一個(gè)問題:Literals和PolicyEdges。實(shí)際返回的類型分別為Dictionary<string, DfaNode>和Dictionary<object, DfaNode>,但是我們需要使用一個(gè)不包含該DfaNode類型的類型。不幸的是,這意味著我們不得不退回到.NET 1.1 IDictionary接口!
您不能將一個(gè)Dictionary<string, DfaNode>強(qiáng)制轉(zhuǎn)換為IDictionary<string, object>,因?yàn)檫@樣做將是不安全的協(xié)方差形式。
IDictionary是一個(gè)非泛型接口,因此key和value僅作為object公開。對于string鍵,您可以直接進(jìn)行轉(zhuǎn)換,對于,DfaNode我們可以使用ImpromptuInterface為我們創(chuàng)建代理包裝器:
// 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>(); }現(xiàn)在,我們已經(jīng)擁有了通過實(shí)現(xiàn)WriteNode來創(chuàng)建自定義DfaWriter實(shí)現(xiàn)所需的一切對象,但是這篇文章已經(jīng)有點(diǎn)長了,所以我們將在下一篇文章中探討如何實(shí)現(xiàn)這一點(diǎn)!
摘要
在本文中,我探討了DfaWriter在ASP.NET Core 中的實(shí)現(xiàn)以及它使用的兩個(gè)internal類:DfaMatcherBuilder和DfaNode。這些類是內(nèi)部類的事實(shí)使得創(chuàng)建我們自己的DfaWriter實(shí)現(xiàn)非常棘手。為了干凈地實(shí)現(xiàn)它,我們將不得不重新實(shí)現(xiàn)這兩種類型以及它們所依賴的所有類。
作為替代,我使用ImpromptuInterface庫創(chuàng)建了一個(gè)包裝器代理,該代理實(shí)現(xiàn)與被包裝的對象擁有類似的方法。這使用反射來調(diào)用包裝屬性上的方法,但允許我們使用強(qiáng)類型接口。在下一篇文章中,我將展示如何使用這些包裝器創(chuàng)建一個(gè)定制的DfaWriter來進(jìn)行端點(diǎn)圖的自定義。
相關(guān)閱讀:
[譯]使用DOT語言和GraphvizOnline來可視化你的ASP.NETCore3.0終結(jié)點(diǎn)01
將終結(jié)點(diǎn)圖添加到你的ASP.NET Core應(yīng)用程序中
往期精彩回顧
【推薦】.NET Core開發(fā)實(shí)戰(zhàn)視頻課程?★★★
.NET Core實(shí)戰(zhàn)項(xiàng)目之CMS 第一章 入門篇-開篇及總體規(guī)劃
【.NET Core微服務(wù)實(shí)戰(zhàn)-統(tǒng)一身份認(rèn)證】開篇及目錄索引
Redis基本使用及百億數(shù)據(jù)量中的使用技巧分享(附視頻地址及觀看指南)
.NET Core中的一個(gè)接口多種實(shí)現(xiàn)的依賴注入與動(dòng)態(tài)選擇看這篇就夠了
10個(gè)小技巧助您寫出高性能的ASP.NET Core代碼
用abp vNext快速開發(fā)Quartz.NET定時(shí)任務(wù)管理界面
在ASP.NET Core中創(chuàng)建基于Quartz.NET托管服務(wù)輕松實(shí)現(xiàn)作業(yè)調(diào)度
現(xiàn)身說法:實(shí)際業(yè)務(wù)出發(fā)分析百億數(shù)據(jù)量下的多表查詢優(yōu)化
關(guān)于C#異步編程你應(yīng)該了解的幾點(diǎn)建議
C#異步編程看這篇就夠了
給我好看 您看此文用??·?秒,轉(zhuǎn)發(fā)只需1秒呦~ 好看你就點(diǎn)點(diǎn)我總結(jié)
以上是生活随笔為你收集整理的使用ImpromptuInterface反射方便的创建自定义DfaGraphWriter的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何利用Gitlab-CI持续部署到远程
- 下一篇: 微服务认证架构如何演进来的?