XSLT3 Options for .NET Core in 2019 - .net-core

Has anyone got XSLT3 transforms working in .NET Core 2.x+ in 2019?
Seems that the request to MS for XSLT2/3 support hasn't moved forwards, and the Saxon people have other priorities, especially given the IKVM closedown.
Are there any other alternatives for in-process XSLT transformation? At the moment, it seems my only choice is to wrap something up via an external service or some undesirable (for us) COM-style approach that would involve lots of marshalling of data, hurting performance.

Unfortunately IKVM has never supported .NET Core, so the .NET version of Saxon cannot be made to work in that environment. In Saxonica we've been exploring alternative avenues for .NET support, but we haven't found anything remotely promising. (Anyone fancy doing a Kotlin implementation for .NET?)
I don't know what's possible using XMLPrime or Exselt, both of which target .NET.
2021 Update
Saxonica now ships SaxonCS on .NET 5, this product is built by converting the Java code of SaxonJ to C# source code using a custom transpiler.

There is one way how to use Saxon on .NET Core: via Transform.exe running as a process.
You can use code similar to this:
/// <summary>Transform XML inputFile using xsltFile and parameters. Save the result to the outputFile.</summary>
public void Transform(string inputFile, string outputFile, string xsltFile, NameValueCollection parameters)
{
//Search for the instalation path on the system
string path = GetInstalPath(#"Software\Saxonica\SaxonHE-N\Settings", "InstallPath");
string exePath = Path.Combine(path, "bin", "Transform.exe");
string parametersCmd = null;
//Set indicidual parameters
foreach (string parameter in parameters)
{
parametersCmd += String.Format("{0}={1} ", parameter, parameters[parameter]);
}
//set arguments for Transform.exe
string arguments = string.Format("-s:\"{1}\" -xsl:\"{0}\" -o:\"{3}\" {2}", xsltFile, inputFile, parametersCmd, outputFile);
//https://stackoverflow.com/questions/5377423/hide-console-window-from-process-start-c-sharp
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = exePath;
startInfo.Arguments = arguments;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
int waitingTime = 5 * 60 * 1000; //5 minutes; time in milliseconds
Process processTemp = new Process();
processTemp.StartInfo = startInfo;
processTemp.EnableRaisingEvents = true;
try
{
processTemp.Start();
processTemp.WaitForExit(waitingTime);
}
catch (Exception e)
{
throw;
}
}
static string GetInstalPath(string comName, string key)
{
RegistryKey comKey = Registry.CurrentUser.OpenSubKey(comName);
if (comKey == null)
return null;
string clsid = (string)comKey.GetValue(key);
return clsid;
}

SaxonCS EE has been released and works with .NET 5 and .NET 6 (RC/preview) and that way allows using XSLT 3, XPath 3.1 and XQuery 3.1 with .NET Core. It is only available under a commercial license however, but you can test it with a trial license, download from Saxonica is at https://www.saxonica.com/download/dotnet.xml, also on NuGet as https://www.nuget.org/packages/SaxonCS/.
In the meantime IKVM has been updated (https://www.nuget.org/packages/IKVM.Maven.Sdk) and is capable of producing .NET 3.1, .NET 5 and .NET 6 (aka .NET core) compatible cross-compilations. Using that I have managed to cross-compile Saxon HE 11.4 Java to .NET 6 and have published two command line apps/dotnet tools on NuGet to run XSLT 3.0 or XQuery 3.1:
XSLT 3.0: https://www.nuget.org/packages/SaxonHE11NetXslt/
XQuery 3.0: https://www.nuget.org/packages/SaxonHE11NetXQuery/
I have furthermore created an extension library to ease the use of the Java s9api from .NET code, it is on NuGet at https://www.nuget.org/packages/SaxonHE11s9apiExtensions/, the GitHub repository is at https://github.com/martin-honnen/SaxonHE11s9apiExtensions.
A simple example to run some XSLT 3.0 code with .NET 6, using the IKVM cross compiled Saxon HE 11, would be:
using net.sf.saxon.s9api;
using net.liberty_development.SaxonHE11s9apiExtensions;
//using System.Reflection;
// force loading of updated xmlresolver (no longer necessary with Saxon HE 11.5)
//ikvm.runtime.Startup.addBootClassPathAssembly(Assembly.Load("org.xmlresolver.xmlresolver"));
//ikvm.runtime.Startup.addBootClassPathAssembly(Assembly.Load("org.xmlresolver.xmlresolver_data"));
var processor = new Processor(false);
Console.WriteLine($"{processor.getSaxonEdition()} {processor.getSaxonProductVersion()}");
var xslt30Transformer = processor.newXsltCompiler().Compile(new Uri("https://github.com/martin-honnen/martin-honnen.github.io/raw/master/xslt/processorTestHTML5Xslt3InitialTempl.xsl")).load30();
xslt30Transformer.callTemplate(null, processor.NewSerializer(Console.Out));
A samples project showing various examples of XPath 3.1, XQuery 3.1 and XSLT 3.0 usage is at https://github.com/martin-honnen/SaxonHE11IKVMNet6SaxonCSSamplesAdapted.

Related

Converting ImageMoniker to WPF BitmapSource in VS2022

I'm developing an extension for Visual Studio that includes a XAML view inside a VS window. I want the extension to look and feel like the native UI. The extension is currently running and working fine in VS2017 and VS2019 using the following code to transform a moniker to a WPF BitmapSource that can be used directly from XAML:
public static BitmapSource GetIconForImageMoniker(ImageMoniker? imageMoniker, int sizeX, int sizeY)
{
if (imageMoniker == null)
{
return null;
}
IVsImageService2 vsIconService = ServiceProvider.GlobalProvider.GetService(typeof(SVsImageService)) as IVsImageService2;
if (vsIconService == null)
{
return null;
}
ImageAttributes imageAttributes = new ImageAttributes
{
Flags = (uint)_ImageAttributesFlags.IAF_RequiredFlags,
ImageType = (uint)_UIImageType.IT_Bitmap,
Format = (uint)_UIDataFormat.DF_WPF,
LogicalHeight = sizeY,
LogicalWidth = sizeX,
StructSize = Marshal.SizeOf(typeof(ImageAttributes))
};
IVsUIObject result = vsIconService.GetImage(imageMoniker.Value, imageAttributes);
object data;
result.get_Data(out data);
BitmapSource glyph = data as BitmapSource;
if (glyph != null)
{
glyph.Freeze();
}
return glyph;
}
This method is a direct copy-paste from the WpfUtil class available in multiple of Mads Kristensen's extensions.
As already mentioned, this works fine in VS2017 and VS2019. Now I want this running in VS2022 as well. The extension shows up inside VS2022 but the icons are no longer shown. The problem is that this returns null in VS2022 but not in the previous versions:
ServiceProvider.GlobalProvider.GetService(typeof(SVsImageService)) as IVsImageService2;
Does anyone know how to make this work in VS2022 as well?
This is caused by changes to the interop libraries in VS2022. Namely, they've all been consolidated to a single library (you can see the details here).
This does break compatibility with targeting prior versions of Visual Studio. There is a guide for migrating extensions to VS2022, but to summarize, the guidance is to:
Refactor the source code into a Shared Project.
Create a new VSIX projects targeting VS2022 and the VSIX project you have now would remain for targeting prior versions.

C# 8.0 and .net 3.0

using (var db = new Northwind())
{
Console.WriteLine("Categories and how many products they have:");
IQueryable<Category> cats = db.Categories.Include(c => c.Products);
foreach (Category c in cats)
{
WriteLine($"{c.CategoryName} has {c.Products.Count} products.");
}
}
I am reading c # 8.0 .net core 3.0 by Mark J. Price, but I went to Working with Databases Using Entity Framework Core and I got an error during the query. The connection to the database is connected successfully.
The error is generated when I output cats (System.ArgumentException: 'Format of the initialization string does not conform to specification starting at index 0.'). I cannot solve this problem, please help me.

Auto Fill Web Forms in .net-Core

I am new to .net core.
How can I auto fill forms and submit in dotnet core ?
Please find following sample URLs I want to try
https://mparivahan.in/uyt/?pur_cd=102
Value - 1 = "MH1R"
Value - 2 = "5656"
https://www.filegstrstnow.com/searchGSTTaxpayer
sample Value = "24AADCS0852Q1Z2"
With Regards
I guess you want to automate operations in browser. For this purpose you need a browser automation framework which can be used in you .NET Core 2.0 code. Something like Selenium WebDriver. In this case you code will look like this:
[Test]
public void TestWithFirefoxDriver()
{
using (var driver = new FirefoxDriver())
{
driver.Navigate().GoToUrl(#"https://parivahan.gov.in/rcdlstatus/?pur_cd=102");
driver.FindElement(By.Id("form_rcdl:tf_reg_no1")).Send("GJ01RR");
driver.FindElement(By.Id("form_rcdl:tf_reg_no2")).Send("5656");
driver.FindElement(By.Id("form_rcdl:j_idt36")).Click();
var wait = new WebDriverWait(driver, TimeSpan.FromMinutes(1));
// Find element with the result to retrieve value, and so on..
}
}
Note: I didn't check the code above in runtime, it is just for demonstration purposes.
To run Selenium automation code without opening the browser you could use PhantomJS driver instead of drivers for real browsers like FirefoxDriver. Change this line:
using (var driver = new FirefoxDriver())
to:
using (var driver = new PhantomJSDriver())

aspnet_compiler in Azure starup task

Does anyone know if its possible to call aspnet_compiler from an azure role startup task to force a precompilation inplace. (And if so as a foregroudn/background or simple task?)
Perhaps something like:
%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_precompile.exe -v / -p "F:\siteroot\0"
Or are there any better ways to accomplish this?
Yes, that should work once you figure out the right path to the compiler although I haven't tried this specific approach.
An alternative I've tried is to use ClientBuildManager.PrecompileApplication as described in this answer. I tried calling that from inside OnStart() but you can compile C# code as a .NET assembly and use that from PowerShell or just call .NET primitives from PowerShell as described here and that way call it from the startup task.
A start-up task is possible, but one problem with that is that the siteroot path is hardcoded and that can change. Instead add the following to the RoleEntryPoint OnStart method:
using (var serverManager = new ServerManager())
{
string siteName = RoleEnvironment.CurrentRoleInstance.Id + "_" + "Web";
var siteId = serverManager.Sites[siteName].Id;
var appVirtualDir = $"/LM/W3SVC/{siteId}/ROOT"; // Do not end this with a trailing /
var clientBuildManager = new ClientBuildManager(appVirtualDir, null, null,
new ClientBuildManagerParameter
{
PrecompilationFlags = PrecompilationFlags.Default,
});
clientBuildManager.PrecompileApplication();
}

Use WMI to create IIS Application Directory with C#

We have a web application that is installed on Windows 2003 and Windows 2008 systems. In the past, our install code used ADSI to create a couple of application directories in IIS, but this requires the IIS 6 management components to be installed in Windows 2008. I have been trying to use WMI to create the application directories so we can support both operating systems.
I have been trying this code
public static void AddVirtualFolder(string serverName, string websiteId, string name, string path)
{
ManagementScope scope = new ManagementScope(string.Format(#"\\{0}\root\MicrosoftIISV2", serverName));
scope.Connect();
string siteName = string.Format("W3SVC/{0}/Root/{1}", websiteId, name);
ManagementClass mc = new ManagementClass(scope, new ManagementPath("IIsWebVirtualDirSetting"), null);
ManagementObject oWebVirtDir = mc.CreateInstance();
oWebVirtDir.Properties["Name"].Value = siteName;
oWebVirtDir.Properties["Path"].Value = path;
oWebVirtDir.Properties["AuthFlags"].Value = 5; // Integrated Windows Auth.
oWebVirtDir.Properties["EnableDefaultDoc"].Value = true;
// date, time, size, extension, longdate ;
oWebVirtDir.Properties["DirBrowseFlags"].Value = 0x4000003E;
oWebVirtDir.Properties["AccessFlags"].Value = 513; // read script
oWebVirtDir.Put();
ManagementObject mo = new ManagementObject(scope, new System.Management.ManagementPath("IIsWebVirtualDir='" + siteName + "'"), null);
ManagementBaseObject inputParameters = mo.GetMethodParameters("AppCreate2");
inputParameters["AppMode"] = 2;
mo.InvokeMethod("AppCreate2", inputParameters, null);
mo = new ManagementObject(scope, new System.Management.ManagementPath("IIsWebVirtualDirSetting='" + siteName + "'"), null);
mo.Properties["AppFriendlyName"].Value = name;
mo.Put();
}
}
However, I get path not found errors on known directories. If anybody has some references I can use, I would greatly appreciate it. Any other suggestions on how to go about this are also welcome.
Using the code above, you will still need the IIS6 compatibility bits on Windows 2008/IIS7. The reason for this is that the calls to set properties such as DirBrowseFlags, AccessFlags and so on are IIS 6 metabase properties that are not supported in IIS7 without the IIS6 management components.
For IIS7 I'd recommend programming directly against the Microsoft.Web.Administration namespace, but if you really need to use WMI then see this article:
Managing Sites with IIS 7.0's WMI Provider (IIS.NET)

Resources