How do I render a ASP.NET MVC Razor View to a string in 2023? [duplicate] - c#

This question already has answers here:
Send asp.net mvc action result inside email
(5 answers)
Closed 4 days ago.
I have a razor view and I want to store the compiled version in a string and send a mail containing it
I've tried this :
string body = Engine.Razor.RunCompile(template,"templateKey",null,model);
but I received this error:
Could not load type
'System.Security.Principal.WindowsImpersonationContext

Maybe you can look here https://www.learnrazorpages.com/advanced/render-partial-to-string and see how to get razor view to string.
After you get razor view as html string you can use that string as body for email.

Also for sending emails you may use xsl templates and bing xml data wıth template and create your html content
private string getEMailBodyAccountForgetPassword(User User)
{
string recieverName = User.Name;
string validationKey = User.ValidationKey;
string applicationDisplayName = applicationName;
string resetLink = $"{host}/account/resetpassword/start/{validationKey}";
string template = emailTemplateRootPath + "\\Account\\ForgetPassword\\ForgetPassword.xsl";
string xmlData = "<email>";
xmlData += $"<recievername>{recieverName}</recievername>";
xmlData += $"<link>{resetLink}</link>";
xmlData += $"<applicationdisplayname>{applicationDisplayName}</applicationdisplayname>";
xmlData += "</email>";
string body = Fabrikafa.Common.Functions.BindXslData(xmlData, template);
return body;
}
Here is the bind method:
public static string BindXslData(string XmlData, string XslFile)
{
string empty = string.Empty;
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml(XmlData);
XslCompiledTransform xslCompiledTransform = new XslCompiledTransform();
xslCompiledTransform.Load(XslFile);
XPathNavigator input = xmlDocument.CreateNavigator();
StringBuilder stringBuilder = new StringBuilder();
XmlWriter xmlWriter = XmlWriter.Create(stringBuilder);
xslCompiledTransform.Transform(input, xmlWriter);
xmlWriter.Close();
return stringBuilder.ToString();
}
Here is the xsl template:
<?xml version="1.0" encoding="utf-16"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="msxsl">
<xsl:output method="html" indent="yes"/>
<xsl:template match="#* | node() | /">
<html>
<head>
<link href="https://fonts.googleapis.com/css?family=PT+Sans" rel="stylesheet"/>
<style>
body {
font-family: 'PT Sans', sans-serif;
}
</style>
</head>
<body>
<div>
<img src="https://eu2.contabostorage.com/80ec950384cb46a8bf00f0e75660cedf:fabrikafa.content/Logo/apps/Yoneticim.EmailProviders-Logo-sm.png" /><br/>
<h2>
<xsl:value-of select="//email/applicationdisplayname"/> - Password Reset
</h2>
Dear <xsl:value-of select="//email/recievername"/>,<br/>
<br/>
To reset your password, please click link below.<br/>
<br/><br/>
<a href="https://{//email/link}" target="_blank">
https://<xsl:value-of select="//email/link"/>
</a>
<br/>
<br/>
<br/>
<b>
<xsl:value-of select="//email/applicationdisplayname"/> Team
</b>
</div>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

Related

c# xslt transform on large xml string causes aspx timeout

I am working on an older project using .NET framework 4.5. There is an aspx page with an OnSelectedIndexChanged event that completes via a server side call. The server side call uses a stored proc that returns xml and then uses a transformation to convert the xml into html. The issue is that when the xml is large (currently testing with 10k records in xml format from the stored proc though the expectation is 100k records in production), then the aspx page times out. I am not sure what the best way to fix this issue is since the web.config settings for executionTimeout is set very high and debug is set to false.
Web.Config
<httpRuntime maxRequestLength="102400" executionTimeout="20000"/>
ASPX Page
<asp:DropDownList ID="ddlViews" runat="server" AutoPostBack="true" style="height:25px;min-width:150px;"
OnSelectedIndexChanged="ddlViews_SelectedIndexChanged">
</asp:DropDownList>
.
.
.
<asp:UpdatePanel ID="updPnl" runat="server">
<Triggers>
<asp:AsyncPostBackTrigger ControlID="ddlViews" EventName="SelectedIndexChanged" />
</Triggers>
<ContentTemplate>
<asp:Literal ID="litData" runat="server"></asp:Literal>
</ContentTemplate>
</asp:UpdatePanel>
ASPX Code Behind
protected void ddlViews_SelectedIndexChanged(object sender, EventArgs e)
{
if (ddlViews.Items.Count > 0 && ddlViews.SelectedValue != "-1")
{
System.Threading.Thread.Sleep(3000);
ShowView();
}
}
private void ShowView()
{
if (ddlViews.Items.Count > 0)
{
string inputXml = GetXml();
strData = Utils.ApplyXslt(inputXml);
litData.Text = strData;
}
}
public static string ApplyXslt(string xml)
{
try
{
string xsltUri = GetXslPath();
XsltArgumentList xslArg = new XsltArgumentList();
xslArg.AddExtensionObject("urn:string-plus", new XslStringPlus());
var xdoc = new XmlDocument();
xdoc.LoadXml(xml);
var xslt = new System.Xml.Xsl.XslCompiledTransform();
using (var mStream = new System.IO.MemoryStream())
{
xslt.Load(xsltUri);
xslt.Transform(xdoc, xslArg, mStream);
mStream.Position = 3;
var sr = new System.IO.StreamReader(mStream);
return sr.ReadToEnd();
}
}
catch (Exception e){
throw new Exception(String.Format("Error Applying Xslt from path '{0}'to xml value '{1}'", xsltUri, xml) + " | " + e.StackTrace);
}
}
The Transform is taking close to 3 minutes to complete for 10k records. It would be great to be able to drastically reduce that time if possible. The page is timing out after 1.5 minutes and I cannot find anything that works to increase that.
XSLT
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xslplus="urn:string-plus">
<xsl:output method="html" />
<xsl:template match="/">
<table id="tblLoanQuery" class="tableStyle">
<tr>
<xsl:for-each select="ROOT/Fields/child::*">
<xsl:variable name="h" select="." />
<th><xsl:value-of select="xslplus:FormatLabel($h)" /></th>
</xsl:for-each>
</tr>
<xsl:for-each select="ROOT/LoanNumbers/child::*">
<xsl:variable name="loanId" select="." />
<tr>
<xsl:for-each select="//ROOT/Fields/Field">
<xsl:variable name="fullFieldName" select="." />
<xsl:variable name="field" select="substring-after($fullFieldName,'.')" />
<xsl:variable name="FieldValue" select="//ROOT/ROWS/row[#lqKey=$loanId]/#*[name(.)= xslplus:ConvertSpace($field)]/." />
<td>
<xsl:value-of select="xslplus:FormatValue($FieldValue)"/>
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
In terms of XSLT it appears that //ROOT/ROWS/row[#lqKey=$loanId] suggests you want to use a key <xsl:key name="row-ref" match="ROOT/ROWS/row" use="#lqKey"/> and use key('row-ref', $loanId) instead of //ROOT/ROWS/row[#lqKey=$loanId]. The use of a key might improve run-time performance.

Generate HTML based on user input (XML/XSLT)

How this program works is, it allows the user to input a street name that exists from the XML file (there are a lot more data from XML, but here is just a couple). Once the user enters a street name, it will run the C# code and use the XSLT to create the layout then open the result in an HTML (internet explorer).
XML:
<allstops>
<stop number="2504" name="Main & Bainard EB">
<location>
<latitude>42.91033567</latitude>
<longitude>-81.29671483</longitude>
</location>
<routes>28</routes>
</stop>
<stop number="20" name="Adelaide & Ada NB">
<location>
<latitude>42.9742886</latitude>
<longitude>-81.2252341</longitude>
</location>
<routes>16</routes>
</stop><stop number="2448" name="Homeview & Golfview Cres S Leg NB">
<location>
<latitude>42.9459748</latitude>
<longitude>-81.2503736</longitude>
</location>
<routes>26</routes>
</stop>
<stop number="2448" name="Homeview & Golfview Cres S Leg NB">
<location>
<latitude>42.9459748</latitude>
<longitude>-81.2503736</longitude>
</location>
<routes>26</routes>
</stop>
</allstops>
XSLT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="html" indent="yes"/>
<xsl:param name="name" />
<xsl:template match="/">
<html>
<body>
<h1>
<font face="Verdana">
LTC Stops on <xsl:value-of select="$name"/>
</font>
</h1>
<h2>
<font face="Verdana">
<xsl:value-of select="count(//stop)"/> stops found
</font>
</h2>
<table style="width:720px" border="3">
<tr>
<th>
<font face="Verdana" size="4">Stop #</font>
</th>
<th>
<font face="Verdana" size="4">Stop Name</font>
</th>
<th>
<font face="Verdana" size="4">Latitude</font>
</th>
<th>
<font face="Verdana" size="4">Longitude</font>
</th>
<th>
<font face="Verdana" size="4">Routes</font>
</th>
</tr>
<xsl:apply-templates select ="//stop">
<xsl:sort select="#number" data-type="number" order="ascending" />
</xsl:apply-templates>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="stop">
<xsl:element name="tr">
<xsl:element name="td">
<xsl:value-of select="#number"/>
</xsl:element>
<xsl:element name="td">
<xsl:value-of select="#name"/>
</xsl:element>
<xsl:element name="td">
<xsl:value-of select=".//latitude"/>
</xsl:element>
<xsl:element name="td">
<xsl:value-of select=".//longitude"/>
</xsl:element>
<xsl:element name="td">
<xsl:value-of select=".//routes"/>
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
C#:
using System;
using System.Xml.Xsl; // Xslt types
using System.IO; // Directory class
namespace GetLTCStops
{
class Program
{
/*
* The following constants assume all the files are located
* in the solution folder which is three folders up from the
* program's runtime folder.
*/
private const string XML_FILE = #"..\..\..\Ltcstops.xml";
private const string XSLT_FILE = #"..\..\..\Ltcstops.xslt";
private const string XSLT_FILE_TWO = #"..\..\..\Ltcstops1.xslt";
private const string HTML_FILE = #"..\..\..\Ltcstops.html";
static void Main(string[] args)
{
// Display a title
Console.WriteLine("London Transit Comission Bus Stop Report");
Console.WriteLine("----------------------------------------");
Console.WriteLine("\nProvide a street name or partial street name exactly as it appears in the XML file.");
string streetName;
do
{
// Get the name of a character from the usuer
Console.Write("\nEnter Street: ");
streetName = Console.ReadLine().ToUpper().Trim();
if (streetName.Length == 0)
{
// Create a new XslTransform object and load the style sheet
XslCompiledTransform xslt = new XslCompiledTransform();
xslt.Load(XSLT_FILE_TWO);
// Set-up an XsltArgumentList object to pass to the
// XSLT file's 'character' parameter
XsltArgumentList xsltArgList = new XsltArgumentList();
// Execute the transformation
FileStream outFile = File.Create(HTML_FILE);
xslt.Transform(XML_FILE, xsltArgList, outFile);
outFile.Close();
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo.FileName = "iexplore";
proc.StartInfo.Arguments = Directory.GetCurrentDirectory().ToString() + "\\" + HTML_FILE;
proc.Start();
}
}
while (streetName.Length == 0);
try
{
// Create a new XslTransform object and load the style sheet
XslCompiledTransform xslt = new XslCompiledTransform();
xslt.Load(XSLT_FILE);
// Set-up an XsltArgumentList object to pass to the
// XSLT file's 'name' parameter
XsltArgumentList xsltArgList = new XsltArgumentList();
xsltArgList.AddParam("name", "", streetName);
// Execute the transformation
FileStream outFile = File.Create(HTML_FILE);
xslt.Transform(XML_FILE, xsltArgList, outFile);
outFile.Close();
// Display the transformed XML file in Internet Explorer
// The rutime folder used by the Internet Explorer (IE) program is
// different from the one used by our C# program. So we're going to give
// IE the absolute path to the XML file by using the GetCurrentDirectory() method.
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo.FileName = "iexplore";
proc.StartInfo.Arguments = Directory.GetCurrentDirectory().ToString() + "\\" + HTML_FILE;
proc.Start();
}
catch (Exception ex)
{
Console.WriteLine("Error: {0}", ex.Message);
}
} // end Main method
} // end class
} // end namespace
The problem I am having is, I can enter any of the street that exist in the XML, but it doesn't narrow down the search bringing only the street name I typed. I am wondering if I am missing a line of code or I coded it incorrectly?
You need to use the parameter to filter nodes so use e.g.
<xsl:param name="name" />
<xsl:key name="filter" match="stop" use="#name"/>
<xsl:variable name="filtered-stops" select="key('filter', $name)"/>
and then use <xsl:value-of select="count($filtered-stops)"/> instead of <xsl:value-of select="count(//stop)"/> and <xsl:apply-templates select ="$filtered-stops"> instead of <xsl:apply-templates select ="//stop">.

XsltProcessor in WinRT does not work but the same class in a Windows 10 Universal App does

The code below works for all examples I've tried in a Windows 10 Universal app. But in a Windows 8.1 app it does not output any static html content from the xsl template except the xml document declaration and the inner xml.
Any ideas on why it behaves like this in WinRT?
private static string TransformToString(string xml, string xsl)
{
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(xml);
var xslDocument = new XmlDocument();
xslDocument.LoadXml(xsl);
var processor = new XsltProcessor(xslDocument);
//string transformation = processor.TransformToString(xmlDocument.LastChild);
string transformation = processor.TransformToString(xmlDocument);
return transformation;
}
The example xml:
<library xmlns='http://www.microsoft.com'>
<book>
<chapter></chapter>
<chapter>
<section>
<paragraph a="b">1</paragraph>
<paragraph a="b">2</paragraph>
</section>
</chapter>
</book>
</library>
Example xsl:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:m="http://www.microsoft.com">
<xsl:template match="/">
<html><body><table>
<tr>
<td>Attribute</td>
<td>Value</td>
</tr>
<xsl:for-each select="m:library/m:book/m:chapter/m:section/m:paragraph">
<tr>
<td><xsl:value-of select="#a"/></td>
<td><xsl:value-of select="."/></td>
</tr>
</xsl:for-each>
</table></body></html>
</xsl:template>
Result
<?xml version="1.0" encoding="utf-8"?>12
The problem was calling TransformToString with xmlDocument.LastChild instead of xmlDocument.

create xml with attributes programmatically

I've read around and I couldn't find any good example of how to do this. The file I'm trying to create programmatically looks like this:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<html>
<head>
<title> title here </title>
</head>
<body>
<xsl:apply-templates />
</body>
</html>
</xsl:template>
<xsl:template match="data">
<table width="400" border="1">
<tr bgcolor="#a0acbc">
<td></td>
<td></td>
</tr>
<xsl:for-each select="row">
<tr>
<td>
<xsl:value-of select="" />
</td>
<td>
<xsl:value-of select="" />
</td>
</tr>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
I've seen some examples but I don't know how to make it look exactly like this, with the "stylesheet xmlns:xls=...." and with the attributes on the table and tr.
Could someone help me with this or post me some good example of this?
You'll need to use namespaces (that is the "xsl:" in the names of the elements.)
I'm not going to do the whole thing for you, but this should help point you in the right direction:
You'll need to prefix the element names and attributes with namespaces like so:
using System.IO;
using System.Xml;
using System.Xml.Linq;
namespace XSLCreator
{
class Program
{
static void Main(string[] args)
{
XNamespace xsl = XNamespace.Get("http://www.w3.org/1999/XSL/Transform");
var doc = new XDocument(
new XElement(xsl + "stylesheet",
new XAttribute(XNamespace.Xmlns + "xsl", xsl),
new XAttribute("version", "1.0")
)
);
var sw = new StreamWriter("test.xml");
XmlWriter xw = new XmlTextWriter(sw);
doc.WriteTo(xw);
xw.Close();
sw.Close();
}
}
}
You'll get an xml doc that looks like this:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" />

Setting XSL substitution parameters within c# code

I am trying to set the width variables <WIDTH> and <WIDTH1> within XSL which I am retriveing from the web.config within c# as below:
string Width1 = System.Configuration.ConfigurationSettings.AppSettings.Get("Width1");
string Width2 = System.Configuration.ConfigurationSettings.AppSettings.Get("Width2");
cslx.Xslt=#"<?xml version='1.0' encoding='UTF-8'?>
<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:output method='html'/>
<xsl:template match='/'>
<link rel='stylesheet' type='text/css' href='/StyleSheets/test.css'/>
<xsl:apply-templates select='/Data/Test/TestItems/TestItem'/>
</xsl:template>
<xsl:template match='TestItem'>
<xsl:when='boolean($Link1Items)or boolean($Link2Items) or boolean($Link3Items)'>
<table width='<WIDTH1>' class='tablestyle '>
</xsl:when>
<xsl:otherwise>
<table width='<WIDTH2>' class='tablestyle '>
</xsl:otherwise>
</table>
</xsl:template>
</xsl:stylesheet>
";
// update subsitution parameters
cslx.Xslt = cslx.Xslt.Replace("<WIDTH1>", Width1);
cslx.Xslt = cslx.Xslt.Replace("<WIDTH2>", Width2);
But the HTML is not generated and an error is thrown regarding the table tag which is not closed.
I know the table tag must go inside each of the xsl:when and xsl:otherwise tags but I want to avoid this.
My problem is there is a lot XSL code between the tags and I want to avoid code duplication! Is there any other way I can acheive this?
Many Thanks,
Use XSLT parameters to pass parameters to your stylesheet, not
string replacement.
Your XSLT is not a well formed XML document. To
manipulate attributes, you have to use your xsl:whens inside an
xsl:attribute element. In your case your code should be like this:
string Width1 = System.Configuration.ConfigurationSettings.AppSettings.Get("Width1");
string Width2 = System.Configuration.ConfigurationSettings.AppSettings.Get("Width2");
cslx.Xslt=#"<?xml version='1.0' encoding='UTF-8'?>
<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:output method='html'/>
<xsl:param name='width1' />
<xsl:param name='width2' />
<xsl:template match='/'>
<link rel='stylesheet' type='text/css' href='/StyleSheets/test.css'/>
<xsl:apply-templates select='/Data/Test/TestItems/TestItem'/>
</xsl:template>
<xsl:template match='TestItem'>
<table class='tablestyle'>
<xsl:attribute name='width'>
<xsl:choose>
<xsl:when test='boolean($Link1Items)or boolean($Link2Items) or boolean($Link3Items)'><xsl:value-of select='$width1' /></xsl:when>
<xsl:otherwise><xsl:value-of select='$width2' /></xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</table>
</xsl:template>
</xsl:stylesheet>
";
var xslt = new XslCompiledTransform();
xslt.Load(new XmlTextReader(new StringReader(cslx.Xslt)));
var args = new XsltArgumentList();
args.AddParam("width1", "", Width1);
args.AddParam("width2", "", Width2);
// whenever you want to transform
var writer = new XmlWriter("output.xml");
xslt.Transform(document, args, writer);
Your XSL is not well-formed XML , e.g.:
<xsl:otherwise>
<table width='<WIDTH2>' class='tablestyle '>
</xsl:otherwise>
</table>
The first thing you should do is concentrate on getting the XSLT correct. Note that you must have xsd:choice elements wrapping the xsl:otherwise, etc. There are a lot of other errors (e.g. xsl:when=... should be xsl:when test="...). Take it in steps:
learn how to write XML
Learn XSLT
Only then put it into C#

Categories