国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - ASP.NET教程 - 基于.net standard 的動態編譯實現代碼

基于.net standard 的動態編譯實現代碼

2020-05-29 15:43Tourist ASP.NET教程

這篇文章主要介紹了基于.net standard 的動態編譯實現代碼,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下

在上篇文章[基于.net core 微服務的另類實現]結尾處,提到了如何方便自動的生成微服務的客戶端代理,使對于調用方透明,同時將枯燥的東西使用框架集成,以提高使用便捷性。在嘗試了基于 Emit 中間語言后,最終決定使用生成代碼片段然后動態編譯的模式實現。

1.背景:

其一在前文中,我們通過框架實現了微服務面向使用者的透明調用,但是需要為每個服務寫一個客戶端代理,顯得異常繁瑣,其二項目中前端站點使用了傳統的.Net Framework 框架,后端微服務我們使用了.Net Core 框架改造,短時間將前端站點調整成 .Net Core 框架亦不現實,為了能同時支持這兩種框架。如何 .Net Standard 框架來自動創建微服務的客戶端代理成為我們必須解決的問題。

2.問題轉化

我們在回頭簡單看一下我們現在期望的微服務客戶端代理長的樣子:

基于.net standard 的動態編譯實現代碼

        通過上面分析,我們只需要將服務接口中的每個方法,判斷是否有返回值,如果有返回值調用Invoke<ReturnType>方法,沒有返回值調用InvokeWithoutReturn方法,然后依次將接口名,方法名以及方法的參數按順序傳入即可。各位如果是熟悉Java的同學,這個問題很容易解決,使用動態代理創建一個這樣的匿名類即可,但在.net 的世界里,動態代理的實現確顯得異常麻煩。
       首先想到是通過中間語言 IL 的 Emit 實現,但無奈這個使用起來實在是太不友好了, 幾經折騰最終還是選擇放棄了,后又想到其實可以通過動態生成這個代碼片段,動態編譯后加載到系統程序集中,應該就可以了。于是在這個方向的指引下,我們嘗試著去一步步實現這個問題。

3.解決方案

如何生成這個代碼片段? 通過上面的分析,我們知道只需要將接口反射獲取其中的公共方法,并將接口的每個方法簽名原樣復制,在根據接口方法是否有返回值分別調用RemoteServiceProxy基類中相關方法即可,不過需要特殊注意的泛型方法翻譯,以下是生成這個代碼片段的參考實現.

尋找出為服務接口程序集文件,并處理每個文件

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private static StringBuilder CreateApiProxyCode()
{
 var path = GetBinPath();
 var dir = new DirectoryInfo(path);
 //獲取項目中微服務接口文件
 var files = dir.GetFiles("XZL*.Api.dll");
 var codeStringBuilder = new StringBuilder(1024);
 //添加必要的using
 codeStringBuilder
 .AppendLine("using System;")
 .AppendLine("using System.Collections.Generic;")
 .AppendLine("using System.Text;")
 .AppendLine("using XZL.Infrastructure.ApiService;")
 .AppendLine("using XZL.Infrastructure.Defines;")
 .AppendLine("using XZL.Model;")
 .AppendLine("namespace XZL.ApiClientProxy")
 .AppendLine("{"); //namespace begin
 //處理每個文件中的接口信息
 foreach (var file in files)
 {
 CreateApiProxyCodeFromFile(codeStringBuilder, file);
 }
 codeStringBuilder.AppendLine("}"); //namespace end
 return codeStringBuilder;
}

處理每個文件中的接口類型,并將每個程序集的依賴程序集找出來,方便后面動態編譯

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
private static void CreateApiProxyCodeFromFile(StringBuilder fileCodeBuilder, FileInfo file)
 {
 try
 {
 Assembly apiAssembly = Assembly.Load(file.Name.Substring(0, file.Name.Length - 4));
 var types = apiAssembly
 .GetTypes()
 .Where(c => c.IsInterface && c.IsPublic)
 .ToList();
 var apiSvcType = typeof(IApiService);
 bool isNeed = false;
 foreach (Type type in types)
 {
 //找出期望的接口類型
 if (!apiSvcType.IsAssignableFrom(type))
 {
 continue;
 }
 //找出接口的所有方法
 var methods = type.GetMethods(BindingFlags.Public
 | BindingFlags.FlattenHierarchy
 | BindingFlags.Instance);
 if (!methods.Any())
 {
 continue;
 }
 //定義代理類名,以及實現接口和繼承RemoteServiceProxy
 fileCodeBuilder.AppendLine($"public class {type.FullName.Replace(".", "_")}Proxy :" +
  $"RemoteServiceProxy, {type.FullName}")
 .AppendLine("{"); //class begin
 //處理每個方法
 foreach (var mth in methods)
 {
 CreateApiProxyCodeFromMethod(fileCodeBuilder, type, mth);
 }
 fileCodeBuilder.AppendLine("}"); //class end
 isNeed = true;
 }
 if (isNeed)
 {
 var apiRefAsms = apiAssembly.GetReferencedAssemblies();
 refAssemblyList.Add(apiAssembly.GetName());
 refAssemblyList.AddRange(apiRefAsms);
 }
 }
 catch
 {
 }
 }

處理接口中的每個方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
private static void CreateApiProxyCodeFromMethod(
 StringBuilder fileCodeBuilder,
 Type type,
 MethodInfo mth)
{
 var isMthReturn = !mth.ReturnType.Equals(typeof(void));
 fileCodeBuilder.Append("public ");
 //添加返回值
 if (isMthReturn)
 {
 fileCodeBuilder.Append(GetFriendlyTypeName(mth.ReturnType)).Append(" ");
 }
 else
 {
 fileCodeBuilder.Append(" void ");
 }
 //方法參數開始
 fileCodeBuilder.Append(mth.Name).Append("(");
 var mthParams = mth.GetParameters();
 if (mthParams.Any())
 {
 var mthparaList = new List<string>();
 foreach (var p in mthParams)
 {
 mthparaList.Add(GetFriendlyTypeName(p.ParameterType) + " " + p.Name);
 }
 fileCodeBuilder.Append(string.Join(",", mthparaList));
 }
 //方法參數結束
 fileCodeBuilder.Append(")");
 //方法體開始
 fileCodeBuilder.AppendLine("{");
 if (isMthReturn)
 {
 //返回值
 fileCodeBuilder.Append("return Invoke<")
 .Append(GetFriendlyTypeName(mth.ReturnType))
 .Append(">");
 }
 else
 {
 fileCodeBuilder.Append(" InvokeWithoutReturn");
 }
 //拼接接口名及方法名
 fileCodeBuilder.Append($"(\"{type.FullName}\",\"{mth.Name}\"");
 //方法本身參數
 if (mthParams.Any())
 {
 fileCodeBuilder.Append(",").Append(string.Join(",", mthParams.Select(t => t.Name)));
 }
 fileCodeBuilder.Append(");");
 //方法體結束
 fileCodeBuilder.AppendLine("}");
}

獲取泛型類型字符串

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private static string GetFriendlyTypeName(Type type)
{
 if (!type.IsGenericType)
 {
 return type.FullName;
 }
 string friendlyName = type.Name;
 int iBacktick = friendlyName.IndexOf('`');
 if (iBacktick > 0)
 {
 friendlyName = friendlyName.Remove(iBacktick);
 }
 friendlyName += "<";
 Type[] typeParameters = type.GetGenericArguments();
 for (int i = 0; i < typeParameters.Length; ++i)
 {
 string typeParamName = GetFriendlyTypeName(typeParameters[i]);
 friendlyName += (i == 0 ? typeParamName : "," + typeParamName);
 }
 friendlyName += ">";
 return friendlyName;
}

如何添加依賴

既然是要編譯源碼,那么源碼中的依賴必不可少,在上一步中我們已經將每個程序集的依賴一并找出,接下來我們將這些依賴全部整理出來

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//緩存程序集依賴
 var references = new List<MetadataReference>();
 var refAsmFiles = new List<string>();
 //系統依賴
 var sysRefLocation = typeof(Enumerable).GetTypeInfo().Assembly.Location;
 refAsmFiles.Add(sysRefLocation);
 //refAsmFiles原本緩存的程序集依賴
 refAsmFiles.Add(typeof(object).GetTypeInfo().Assembly.Location);
 refAsmFiles.AddRange(refAssemblyList.Select(t => Assembly.Load(t).Location).Distinct().ToList());
 //傳統.NetFramework 需要添加mscorlib.dll
 var coreDir = Directory.GetParent(sysRefLocation);
 var mscorlibFile = coreDir.FullName + Path.DirectorySeparatorChar + "mscorlib.dll";
 if (File.Exists(mscorlibFile))
 {
 references.Add(MetadataReference.CreateFromFile(mscorlibFile));
 }
 var apiAsms = refAsmFiles.Select(t => MetadataReference.CreateFromFile(t)).ToList();
 references.AddRange(apiAsms);
 //當前程序集依賴
 var thisAssembly = Assembly.GetEntryAssembly();
 if (thisAssembly != null)
 {
 var referencedAssemblies = thisAssembly.GetReferencedAssemblies();
 foreach (var referencedAssembly in referencedAssemblies)
 {
 var loadedAssembly = Assembly.Load(referencedAssembly);
 references.Add(MetadataReference.CreateFromFile(loadedAssembly.Location));
 }
 }

編譯

有了代碼片段, 也有了編譯程序集依賴, 接下來就是最重要的編譯了.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//定義編譯后文件名
var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Proxy");
if (!Directory.Exists(path))
{
 Directory.CreateDirectory(path);
}
var apiRemoteProxyDllFile = Path.Combine(path,
 apiRemoteAsmName + DateTime.Now.ToString("yyyyMMddHHmmssfff") + ".dll");
var tree = SyntaxFactory.ParseSyntaxTree(codeBuilder.ToString());
var compilation = CSharpCompilation.Create(apiRemoteAsmName)
 .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))
 .AddReferences(references)
 .AddSyntaxTrees(tree);
//執行編譯
EmitResult compilationResult = compilation.Emit(apiRemoteProxyDllFile);
if (compilationResult.Success)
{
 // Load the assembly
 apiRemoteAsm = Assembly.LoadFrom(apiRemoteProxyDllFile);
}
else
{
 foreach (Diagnostic codeIssue in compilationResult.Diagnostics)
 {
 string issue = $"ID: {codeIssue.Id}, Message: {codeIssue.GetMessage()}," +
 $" Location: { codeIssue.Location.GetLineSpan()}, " +
 $"Severity: { codeIssue.Severity}";
 AppRuntimes.Instance.Loger.Error("自動編譯代碼出現異常," + issue);
 }
}

結語

在經過以上處理后,雖算不上完美,但順利的實現了我們期望的樣子,在之前的GetService中,當發現屬于遠程服務的時候,只需要類似如下形式返回代理對象即可。同時為增加調用更加順暢,我們將此編譯的時機定在了發生在程序啟動的時候,ps 當然或許還有一些其他更合適的時機.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static ConcurrentDictionary<string, Object> svcInstance = new ConcurrentDictionary<string, object>();
var typeName = "XZL.ApiClientProxy." + typeof(TService).FullName.Replace(".", "_") + "Proxy";
object obj = null;
if (svcInstance.TryGetValue(typeName, out obj) && obj != null)
{
 return (TService)obj;
}
try
{
 obj = (TService)apiRemoteAsm.CreateInstance(typeName);
 svcInstance.TryAdd(typeName, obj);
}
catch
{
 throw new ICVIPException($"未找到 {typeof(TService).FullName} 的有效代理");
}
return (TService)obj;

總結

以上所述是小編給大家介紹的基于.net standard 的動態編譯實現代碼,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對服務器之家網站的支持!

原文鏈接:https://www.cnblogs.com/xie-zhonglai/p/dynamic_compilation_netstandard.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: www.涩涩视频 | 青青久久北条麻妃 | 中文字幕在线观看 | 探花在线观看 | 亚洲成人精品在线观看 | 欧美亚洲视频 | 亚洲一区二区精品视频 | 国产激情在线 | 欧美久久久久 | 欧美黄色小视频 | 在线国产一区 | 中文字幕精品视频 | 狠狠操狠狠操 | 欧美在线视频不卡 | 香蕉影院在线观看 | 欧洲一区| 亚洲精品乱码久久久久膏 | 久久九| 国产在线一区二区三区 | 黄色国产片 | 激情综合五月 | 日韩欧美一区二区视频 | 欧美国产伦久久久久久 | 亚洲欧美另类久久久精品2019 | 欧美日韩国产精品 | 99精品视频在线免费观看 | 欧洲精品在线观看 | 亚洲黄网在线观看 | 久久99精品久久久久婷婷暖91 | 欧美在线网站 | 欧美激情专区 | 久久久久久av | 精品一区久久 | 国产精品视频区 | 在线观看成人 | 国产操片 | 欧美亚洲日本 | 欧美亚洲一区二区三区 | 日本a在线| 欧美成人专区 | 日本久久久 |