.Net Core、1C、動的コンパイル、スクリプトAPI

ハブラチ人への良い一日! 今日、私は偉大で力強いRuslishであなたを苦しめ続けます。 これは記事の続きです。



» 開発→アンマネージコードからの.Netクラスのクロスプラットフォーム使用。 またはLinuxのIDispatchの類似物

» 開発→ネイティブVKを介した1Cでの.Netクラスのクロスプラットフォーム使用。 またはLinuxでCOMを置き換える

» ネイティブVKを介した1Cでの.Netクラスのクロスプラットフォーム使用。 またはLinux IIでCOMを置き換える

» .Net Native VKによる1Cの非同期プログラミング

1C、Linux、Excel、Word、OpenXML、ADO、およびNet Core



現在、.Net Core 2の動的コンパイルオプション



1.これは、Microsoft.CodeAnalysis.CSharp.CSharpCompilation CodeDomの類似物です。

2. Roslyn Scripting API。 ここの



var compilation = Microsoft.CodeAnalysis.CSharp.CSharpCompilation.Create("a") .WithOptions(new Microsoft.CodeAnalysis.CSharp.CSharpCompilationOptions(Microsoft.CodeAnalysis.OutputKind.DynamicallyLinkedLibrary)) .AddReferences( Microsoft.CodeAnalysis.MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location)) .AddSyntaxTrees(Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.ParseText( @" using System; public class C { public C(){} public string M() { return ""Hello Roslyn.""; } }")); var fileName = @"d:\NetStandart\TestCoreNetApp\src\TestCoreNetApp\bin\Debug\netcoreapp1.0\a.dll"; compilation.Emit(fileName); var a = System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromAssemblyPath(fileName); Type  = a.GetType("C"); var obj = Activator.CreateInstance(); var res = .GetMethod("M").Invoke(obj, null); Console.WriteLine(res.ToString());
      
      





このアプローチは、ライブラリを更新する必要がある場合に適しています。 ただし、これにより、すべての結果を伴うDLLが作成されます。



私がもっと好きな2番目の方法。 デリゲートを取得する例を見てみましょう。



 string words = "        "; string pattern = @"\w+"; var scr = Microsoft.CodeAnalysis.Scripting.ScriptOptions.Default; var mscorlib = Assembly.Load(System.Runtime.Loader.AssemblyLoadContext.GetAssemblyName(@"c:\Users\Smirnov_SA\.nuget\packages\Microsoft.NETCore.Portable.Compatibility\1.0.1\ref\netcore50\mscorlib.dll")); scr =scr.WithReferences(mscorlib, typeof(MulticastDelegate).GetTypeInfo().Assembly, typeof(System.Runtime.CompilerServices.IStrongBox).GetTypeInfo().Assembly, typeof(MatchEvaluator).GetTypeInfo().Assembly, typeof(Regex).GetTypeInfo().Assembly) .WithImports("System", "System.Text.RegularExpressions"); string  = @"return (MatchEvaluator)((match) => { string x = match.Value; // If the first char is lower case... if (char.IsLower(x[0])) { // Capitalize it. return char.ToUpper(x[0]) + x.Substring(1, x.Length - 1); } return x; });"; var result = Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript.EvaluateAsync(, scr).Result; MatchEvaluator evaluator = (MatchEvaluator)result; Console.WriteLine(Regex.Replace(words, pattern, evaluator));
      
      





以下へのリンクを必ず含めてください:

mscorlib.dll

System.Private.CoreLib.ni.dll

System.Runtime.dll


次のライブラリがコンパイルに使用されます。

「Microsoft.CodeAnalysis.CSharp」:「2.0.0-beta3」、

「Microsoft.CodeAnalysis.CSharp.Scripting」:「2.0.0-beta3」、

「Microsoft.CodeAnalysis.Scripting.Common」:「2.0.0-beta3」、

Microsoft.CodeAnalysis.Scripting


動的コンパイル。 かつて彼は車のスペアパーツを扱っていました。 そして、たくさんのサプライヤーと顧客がいます。 さらに、数百万のポジションの価格。 そして、それぞれが独自の形式でデータを提供します。 各クライアントは、ディレクトリに書き込むコードを作成し、RunまたはCalculateで使用する方が簡単でした。 確かに、百万番目の価格で作業する場合、このアプローチは遅くなりました。



どういうわけか、DLLを作成し、COMで作業する必要がありました。



動的コンパイルを使用すると、コードを保存し、条件に応じて適用できます。 条件によって動的に形成されたものを含め、コンパイルされたデリゲートを再利用のためにキャッシュできます。

コンパイル速度は非常に高速です(5秒を除く)。



1Cに移りましょう。 したがって、2番目の1Cアルゴリズムは次のようになります



  = "return (MatchEvaluator)((match) => |{ | string x = match.Value; |// If the first char is lower case... |if (char.IsLower(x[0])) |{ |// Capitalize it. |return char.ToUpper(x[0]) + x.Substring(1, x.Length - 1); |} |return x; | |});";  = "        ";  = "\w+"; // MatchEvaluator evaluator = (MatchEvaluator)(); ScriptOptions=("Microsoft.CodeAnalysis.Scripting.ScriptOptions","Microsoft.CodeAnalysis.Scripting"); CSharpScript=("Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript","Microsoft.CodeAnalysis.CSharp.Scripting"); scr = (ScriptOptions.Default); mscorlib = (.("mscorlib.dll",)); Private_CoreLib=(.("System.Private.CoreLib.ni",)); System_Runtime=(.("System.Runtime",)); RegularExpressions=(.("System.Text.RegularExpressions",)); Regex=(RegularExpressions.GetType("System.Text.RegularExpressions.Regex")); scr =(scr.WithReferences(mscorlib.(), Private_CoreLib.(), System_Runtime.(), RegularExpressions.())); scr =(scr.WithImports("System", "System.Text.RegularExpressions")); evaluator = ((CSharpScript.EvaluateAsync(, scr.())).Result); (Regex.Replace(, , evaluator.()));
      
      





一般的に、特別なことは何もありません。 アセンブリを取得し、それらへのリンクをコンパイルして呼び出しました。



さらに複雑な例に移りましょう。 そのため、前回の記事では、ExcelとWordを読む例を使用してDocumentFormat.OpenXmlを使用する例を示しました。



b関数を介してオブジェクトに文字列をキャストすることで速度に問題があり、1C自体からの呼び出し速度はネイティブコードの5倍遅くなります。



したがって、.Netのほとんどのコードを削除します。 大規模な.Netにも同様のオプションがあります

1Cの.Net。 HTTPClient、AngleSharpの例を使用します。 CSSセレクターを使用したJala承認を含むAngleSharpライブラリーを使用したサイトの便利な解析。 動的コンパイル (記事の最後の例)。



クラスを作成し、レイアウトにコピーします。 クラスの本質は、セルデータを読み取り、行番号でグループ化することです。



 public class  { public string ; public string ; public int ; public string ; } public class ExcelReader { static Regex  = new Regex("[A-Za-z]+"); OpenXmlElementList ; void (string ,  ) { . = ; var match = .Match(); var  = match.Value; var  = int.Parse(.Substring(.Length)); . = ; . = ; } void (List<> , Cell cell) { var  = cell.CellReference.InnerText; var text = cell.CellValue?.Text; var DataType = cell.DataType; string res = text; if (DataType != null && DataType == CellValues.SharedString) { int ssid = int.Parse(text); res = [ssid].InnerText; } if (res == null) return; var result = new (); (, result); result. = res; .Add(result); } public List<> ReadExcel(string fileName) { List<>  = new List<>(); using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { using (SpreadsheetDocument doc = SpreadsheetDocument.Open(fs, false)) { var workbookPart = doc.WorkbookPart; //    //     var pt = workbookPart.GetPartsOfType<SharedStringTablePart>(); var sstpart = pt.First(); var sst = sstpart.SharedStringTable;  = sst.ChildElements; var workbook = workbookPart.Workbook; //    var sheet = workbook.Descendants<Sheet>().First(); var worksheetPart = (DocumentFormat.OpenXml.Packaging.WorksheetPart)workbookPart.GetPartById(sheet.Id); var worksheet = worksheetPart.Worksheet; var cells = worksheet.Descendants<Cell>(); // One way: go through each cell in the sheet foreach (Cell cell in cells) { (, cell); } } } return ; } static string (List<> ) { var  = ""; var  = 0; foreach (var  in ) { var  = .; var  = .Length; if ( > ) {  = ;  = ; } else if ( ==  && string.Compare(, , true) > 0)  = ; } return ; } public static object (string fileName) { var res = new ExcelReader(); var  = res.ReadExcel(fileName); var  = .GroupBy( => .).Select( => new {  = .Key,  = .ToArray() }).OrderBy( => .); var  = (); return new {  = .ToList(),  =  }; } } return new Func<string, object>(ExcelReader.);
      
      





読み取りクラスについて説明し、ファイルパスを取得して匿名クラスを返すデリゲートへのリンクを返しました。 とにかく、1Cではリフレクションを通して彼と協力します。 ここでは2つのクラスについて説明していることに注意してください。



次に、このコードを1Cから呼び出します。



 ScriptOptions=("Microsoft.CodeAnalysis.Scripting.ScriptOptions","Microsoft.CodeAnalysis.Scripting"); CSharpScript=("Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript","Microsoft.CodeAnalysis.CSharp.Scripting"); scr = (ScriptOptions.Default); mscorlib = (.("mscorlib.dll",)); Private_CoreLib=(.("System.Private.CoreLib.ni",)); System_Runtime=(.("System.Runtime",)); RegularExpressions=(.("System.Text.RegularExpressions",)); OpenXml=(.("DocumentFormat.OpenXml.dll")); Linq=(.("System.Linq", )); FileSystem=(.("System.IO.FileSystem", )); Regex=(RegularExpressions.GetType("System.Text.RegularExpressions.Regex")); scr =(scr.WithReferences(mscorlib.(), Private_CoreLib.(), System_Runtime.(), RegularExpressions.(),OpenXml.(),Linq.(),FileSystem.())); scr =(scr.WithImports("System", "System.Collections.Generic", "System.Linq", "System.IO", "DocumentFormat.OpenXml", "DocumentFormat.OpenXml.Packaging", "DocumentFormat.OpenXml.Spreadsheet", "System.Text.RegularExpressions")); =("").();  = ((CSharpScript.EvaluateAsync(, scr.())).Result); = (.DynamicInvoke()); (.); = ; =.; (.,); =.; =(.); =(.(.())); =1; //       .MoveNext()  = (.Current); =.;  <  =+1; .(); ; =+1; =.(); //     =(.); =(.(.()));  .MoveNext()  = (.Current); =.; =.; //        64  1  26   //         =.(); .(.(),); ; ;
      
      





現在、Excelの処理速度は大幅に向上しており、コンパイルコストはファイルの読み取りコストに見合っています。



Wordを読むプロセスを見てみましょう。 さらに苦労せずに、私はここで完成したクラスを取りました 。 特にすべてが英語であるため。



 //     //https://code.msdn.microsoft.com/office/CSOpenXmlGetPlainText-554918c3/sourcecode?fileId=71592&pathId=851860130 public class GetWordPlainText : IDisposable { // Specify whether the instance is disposed. private bool disposed = false; // The word package private WordprocessingDocument package = null; /// <summary> /// Get the file name /// </summary> private string FileName = string.Empty; /// <summary> /// Initialize the WordPlainTextManager instance /// </summary> /// <param name="filepath"></param> public GetWordPlainText(string filepath) { this.FileName = filepath; if (string.IsNullOrEmpty(filepath) || !File.Exists(filepath)) { throw new Exception("The file is invalid. Please select an existing file again"); } this.package = WordprocessingDocument.Open(filepath, true); } /// <summary> /// Read Word Document /// </summary> /// <returns>Plain Text in document </returns> public string ReadWordDocument() { StringBuilder sb = new StringBuilder(); OpenXmlElement element = package.MainDocumentPart.Document.Body; if (element == null) { return string.Empty; } sb.Append(GetPlainText(element)); return sb.ToString(); } /// <summary> /// Read Plain Text in all XmlElements of word document /// </summary> /// <param name="element">XmlElement in document</param> /// <returns>Plain Text in XmlElement</returns> public string GetPlainText(OpenXmlElement element) { StringBuilder PlainTextInWord = new StringBuilder(); foreach (OpenXmlElement section in element.Elements()) { switch (section.LocalName) { // Text case "t": PlainTextInWord.Append(section.InnerText); break; case "cr": // Carriage return case "br": // Page break PlainTextInWord.Append(Environment.NewLine); break; // Tab case "tab": PlainTextInWord.Append("\t"); break; // Paragraph case "p": PlainTextInWord.Append(GetPlainText(section)); PlainTextInWord.AppendLine(Environment.NewLine); break; default: PlainTextInWord.Append(GetPlainText(section)); break; } } return PlainTextInWord.ToString(); } #region IDisposable interface public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { // Protect from being called multiple times. if (disposed) { return; } if (disposing) { // Clean up all managed resources. if (this.package != null) { this.package.Dispose(); } } disposed = true; } #endregion public static string GetText(string FileName) { using (var pt = new GetWordPlainText(FileName)) { return pt.ReadWordDocument(); } } } return new Func<string,string>(GetWordPlainText.GetText);
      
      





しかし、Ruslishに戻ります。



 ScriptOptions=("Microsoft.CodeAnalysis.Scripting.ScriptOptions","Microsoft.CodeAnalysis.Scripting"); CSharpScript=("Microsoft.CodeAnalysis.CSharp.Scripting.CSharpScript","Microsoft.CodeAnalysis.CSharp.Scripting"); scr = (ScriptOptions.Default); mscorlib = (.("mscorlib.dll",)); Private_CoreLib=(.("System.Private.CoreLib.ni",)); System_Runtime=(.("System.Runtime",)); RegularExpressions=(.("System.Text.RegularExpressions",)); OpenXml=(.("DocumentFormat.OpenXml.dll")); Linq=(.("System.Linq", )); FileSystem=(.("System.IO.FileSystem", )); Regex=(RegularExpressions.GetType("System.Text.RegularExpressions.Regex")); scr =(scr.WithReferences(mscorlib.(), Private_CoreLib.(), System_Runtime.(),OpenXml.(),FileSystem.())); scr =(scr.WithImports("System", "System.Text", "System.IO", "DocumentFormat.OpenXml", "DocumentFormat.OpenXml.Packaging")); =("").();  = ((CSharpScript.EvaluateAsync(, scr.())).Result);  = .DynamicInvoke(); = ; .(); .();
      
      





主なタスクは、使用されるアセンブリと名前空間への参照を指定することです。



次の記事では、イベントを使用するオブジェクトのラッパーを動的に作成します。

1Cの.NET(C#)。 AddHandlerまたはHandlingExternal Eventsを介して1Cで.Netイベントを使用するためのラッパークラスの動的コンパイル



All Articles