T4 parameter directive using .Net Core - c#

I have an issue in "CallContext.LogicalGetData" method using .net Core when i try to send parameter to runtime text template t4 (net core)
Below tt file :
<## template language="C#" #>
<## assembly name="System.Core" #>
<## import namespace="System.Linq" #>
<## import namespace="System.Text" #>
<## import namespace="System.Collections.Generic" #>
<## parameter name="firstName" type="System.String" #>
<## parameter name="lastName" type="System.String" #>
and Cs call method :
var pt1 = new ParamTemplate1();
pt1.Session = new Dictionary<string, object>();
pt1.Session["firstName"] = "David";
pt1.Session["lastName"] = "Giard";
pt1.Initialize();
var outputText1 = pt1.TransformText();
Console.WriteLine(outputText1);
Hello <#=firstName #> <#=lastName #>!
the problem is du to " System.Runtime.Remoting" library is not supported in .net core
Any ideas or workaround ??
Thanks.

Sorry for the late reply, but if you still want to use the t4 templates, you can replace the parameters directives with properties at the end of the t4 document:
<#+
public string FirstName { get; set; }
public string LastName { get; set; }
#>
And then call it:
var pt1 = new ParamTemplate1
{
FirstName = "David",
LastName = "Giard"
};
var outputText1 = pt1.TransformText();
Console.WriteLine(outputText1);

Related

Can not use DisplayAttribute in T4 template

In my custom T4 template I have to read DisplayAttribute of a type. But I'm keep getting different errors after struggling with several solutions.
Here is part of my .tt file :
<## template debug="true" hostspecific="true" language="C#" compilerOptions="/langversion:10" #>
<## assembly name="System" #>
<## assembly name="System.Runtime" #>
<## assembly name="System.Core" #>
<## assembly name="System.ComponentModel.Annotations" #>
<## import namespace="System" #>
<## import namespace="System.Reflection" #>
<## import namespace="System.Runtime" #>
<## import namespace="System.Linq" #>
<## import namespace="System.Text" #>
<## import namespace="System.Collections.Generic" #>
<## import namespace="System.ComponentModel.DataAnnotations" #>
<## output extension=".cs" #>
<#
static string GetEnumValueDisplayName(Enum value)
{
Type type = value.GetType();
MemberInfo[] memInfo = type.GetMember(value.ToString());
DisplayAttribute? dispalyAttribute = null;
if (memInfo != null && memInfo.Length > 0)
{
object[] attrs = memInfo[0].GetCustomAttributes(typeof(DisplayAttribute), false);
if (attrs != null && attrs.Length > 0)
dispalyAttribute = attrs[0] as DisplayAttribute;
}
return dispalyAttribute?.Name ?? "";
}
#>
<#
foreach(myEnum en in Enum.GetValues(typeof(MyEnum)))
{#>
public static class <#=#en.ToString()#> { public const string Value = "<#=#en.ToString()#>";public const string Text = "<#=#GetEnumValueDisplayName(en)#>";}
<#}#>
And here is my compile time error :
Compiling transformation: The type name 'DisplayAttribute' could not
be found in the namespace 'System.ComponentModel.DataAnnotations'.
This type has been forwarded to assembly
'System.ComponentModel.DataAnnotations, Version=4.0.0.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35' Consider adding a
reference to that assembly.
I am using .net 6 with Visual Studio 2022(17.0.4).
I am using .net 6
That would be a problem. The VS T4 engine only supports .NET Framework at this point, which means that it cannot load any .NET Core libraries during the transformation. We have started working on a .NET Core version of the engine but there is no ETA on when this would become available. Any updates would be communicated through this Suggestion Ticket.
Remove <## assembly name="System.ComponentModel.Annotations" #> and add using System.ComponentModel.DataAnnotations; to the regular using statements.

how to make T4 text template database driven

I am experimenting with code generation atm, I've writen a small template that does what I need it to do, but I need to hardcode the properties myself, I want to extract class and property names from a database where the tables are the classes and index fields are the properties. Idea is to write a filter class based on table indexes. The code I have now assumes that I have 1 table and 3 fields to use as filter criteria.
Currently don't have any code to generate the compiled version as VS does this internally.
Here is what I have so far:
<## template debug="false" hostspecific="false" language="C#" #>
<## assembly name="System.Core" #>
<## import namespace="System.Linq" #>
<## import namespace="System.Text" #>
<## import namespace="System.Collections.Generic" #>
<## parameter name="namespacename" type="System.String" #>
<## parameter name="filterobjectname" type="System.String" #>
<## parameter name="property1" type="System.String" #>
<## parameter name="property2" type="System.String" #>
<## parameter name="property3" type="System.String" #>
<## output extension=".cs" #>
using AzzWork.Business;
using Ventura.Business.Entities;
<# string _namespacename; if(this.namespacename==null){_namespacename = "Ventura.Business";}else{_namespacename = this.namespacename;} #>
<# string _classname; if(this.filterobjectname==null){_classname = "BusinessObject";}else{_classname = this.filterobjectname;}#>
<# string _property1; if(this.property1==null){_property1 = "LastName";}else{_property1 = property1;}#>
<# string _property1c = _property1.Substring(0, 1).ToLower() + _property1.Substring(1);#>
<# string _property2; if(this.property2==null){_property2 = "Email";}else{_property2 = property2;}#>
<# string _property2c = _property2.Substring(0, 1).ToLower() + _property2.Substring(1);#>
<# string _property3; if(this.property3==null){_property3 = "FirstName";}else{_property3 = property3;}#>
<# string _property3c = _property3.Substring(0, 1).ToLower() + _property3.Substring(1);#>
<# string seperator1; if((_property1!=null && _property2!=null) || (_property1!=null && _property2==null && _property3!=null)){seperator1=", ";}else{seperator1="";}#>
<# string seperator2; if(_property2!=null && _property3!=null){seperator2=", ";}else{seperator2="";} #>
namespace <#= _namespacename #>
{
public static class <#= _classname #>FilterFactory{
internal static AzzFilterCollection<<#= _classname #>> GetFilterFor<#= _classname #>(<#if(_property1!=null){#>string <#=_property1c #>Part<#}#><#=seperator1#><#if(_property2!=null){#>string <#=_property2c #>Part<#}#><#=seperator2#><#if(_property3!=null){#>string <#=_property3c #>Part<#}#>)
=> new AzzFilterCollection<<#=_classname#>>()
<#if(_property1!=null){ #>
.AddFilterFor<#= _property1 #>(<#=_property1c #>Part)
<#}#>
<#if(_property2!=null){ #>
.AddFilterFor<#= _property2 #>(<#=_property2c #>Part)
<#}#>
<#if(_property3!=null){ #>
.AddFilterFor<#= _property3 #>(<#=_property3c #>Part)<#}#>;
<#if (_property1!=null){#>
internal static AzzFilterCollection<<#=_classname #>> AddFilterFor<#= _property1 #>(this AzzFilterCollection<<#= _classname#>> filterCollection, string <#= _property1c #>Part)
{
if (<#= _property1c #>Part == null)
return filterCollection;
var filter = new AzzFilter<<#=_classname#>>(<#=_classname#>.ColumnNames.<#=_property1#>, <#=_property1c#>Part);
return filterCollection.AddFilter(filter);
}
<#}#>
<#if (_property2!=null){#>internal static AzzFilterCollection<<#=_classname #>> AddFilterFor<#= _property2 #>(this AzzFilterCollection<<#= _classname#>> filterCollection, string <#= _property2c #>Part)
{
if (<#= _property2c #>Part == null)
return filterCollection;
var filter = new AzzFilter<<#=_classname#>>(<#=_classname#>.ColumnNames.<#=_property2#>, <#=_property2c#>Part);
return filterCollection.AddFilter(filter);
}
<#}#>
<#if (_property3!=null){#>
internal static AzzFilterCollection<<#=_classname #>> AddFilterFor<#= _property3 #>(this AzzFilterCollection<<#= _classname#>> filterCollection, string <#= _property3c #>Part)
{
if (<#= _property3c #>Part == null)
return filterCollection;
var filter = new AzzFilter<<#=_classname#>>(<#=_classname#>.ColumnNames.<#=_property3#>, <#=_property3c#>Part);
return filterCollection.AddFilter(filter);
}
<#}#>
}
}
I guess you're looking at something for SQL.
Does this helps you?
This, This (same),
Also this

Getting specific data from html

I want to get specific data from html. Im using c# and HtmlAgilityPack
Here's the HTML sample:
<p class="heading"><span>Greeting!</span>
<p class='verse'>Hi!<br> //
Hello!</p><p class='verse'>Hello!<br> // i want to get this g
Hi!</p> //
<p class="writers"><strong>WE</strong><br/>
Here my code in c#:
StringBuilder pureText = new StringBuilder();
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(Lyrics);
var s = doc.DocumentNode.Descendants("p");
try
{
foreach (HtmlNode childNode in s)
{
pureText.Append(childNode.InnerText);
}
}
catch
{ }
UPDATE:
StringBuilder pureText = new StringBuilder();
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(URL);
var s = doc.DocumentNode.SelectNodes("//p[#class='verse']"); // error
try
{
foreach (HtmlNode childNode in s)
{
pureText.Append(childNode.InnerText);
}
}
catch
{ }
ERROR:
'HtmlAgilityPack.HtmlNode' does not contain a definition for 'SelectNodes' and no extension method 'SelectNodes' accepting a first argument of type 'HtmlAgilityPack.HtmlNode' could be found (are you missing a using directive or an assembly reference?)
You can try with XPath query syntax to select all <p> having class='verse', like this :
var s = doc.DocumentNode.SelectNodes("//p[#class='verse']");
Then do the same foreach as you already have.
UPDATE I :
I don't know why the code above throwing error for you. It has been tested in my PC and should work fine. Anyway if you accept workaround, the same query can be achieved without XPath this way :
var s = doc.DocumentNode.Descendants("p").Where(o => o.Attributes["class"] != null && o.Attributes["class"].Value == "verse");
This solution is longer since we need to check if a node has class attibutes or not, before checking the attributes' value. Otherwise, we'll get Null Reference Exception if there any <p> without class attributes.

Parsing specific part of XML file

I have a .gpx XML file with the following sample:
<trk>
<name>Test</name>
<trkseg>
<trkpt lon="-84.89032996818423" lat="32.75810896418989">
<ele>225.0</ele>
<time>2011-04-02T11:57:48.000Z</time>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>0</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
</trkseg>
</trk>
I'm using Linq to XML to parse this but I'm having a difficult time parsing the extensions section. Here's the code I'm using:
var gpxDoc = LoadFromStream(document);
var gpx = GetGpxNameSpace();
var gpxtpx = XNamespace.Get("gpxtpx");
var tracks = from track in gpxDoc.Descendants(gpx + "trk")
select new
{
Name = DefaultStringValue(track, gpx, "name"),
Description = DefaultStringValue(track, gpx, "desc"),
Segments = (from trkSegment in track.Descendants(gpx + "trkseg")
select new
{
TrackSegment = trkSegment,
Points = (from trackpoint in trkSegment.Descendants(gpx + "trkpt")
select new
{
Lat = Double(trackpoint.Attribute("lat").Value),
Lng = Double(trackpoint.Attribute("lon").Value),
Ele = DefaultDoubleValue(trackpoint, gpx, "ele"),
Time = DefaultDateTimeValue(trackpoint, gpx, "time"),
Extensions = (
from ext in trackpoint.Descendants(gpx + "extensions").Descendants(gpxtpx + "TrackPointExtension")
select new
{
Cad = DefaultIntValue(ext, gpxtpx, "cad")
}).SingleOrDefault()
})
})
};
Here's the relevant helper code:
private static double? DefaultIntValue(XContainer element, XNamespace ns, string elementName)
{
var xElement = element.Element(ns + elementName);
return xElement != null ? Convert.ToInt32(xElement.Value) : (int?)null;
}
private XNamespace GetGpxNameSpace()
{
var gpx = XNamespace.Get("http://www.topografix.com/GPX/1/1");
return gpx;
}
The actual error I'm getting is
The following error occurred: Object reference not set to an instance of an object.
and it bombs on this code:
Extensions = (from ext in trackpoint.Descendants(gpx + "extensions").Descendants(gpxtpx + "TrackPointExtension")
select new
{
Cad = DefaultIntValue(ext, gpxtpx, "cad")
}).SingleOrDefault();
I just don't know how to fix it.
Since you never declare the namespace (xmlns:gpxtpx="http://www.topografix.com/GPX/1/1") it is never going to match. The xml fragment you provided is not well formed due to the lack of the namespace.
If the fragment posted is snipped from a larger document, consider switching to XML API's rather than string manipulation. If that is the entirety of the XML you receive from an outside system, add it to a root node which you can declare the schema in:
<root xmlns:gpxtpx="http://www.topografix.com/GPX/1/1">
<!-- put your xml fragment here -->
</root>

How to create a method that encapsulates the T4 template text section?

Instead of this .tt:
<## template debug="false" hostspecific="true" language="C#" #>
<## import namespace="System.IO" #>
<## output extension=".txt" #>
<## assembly name="System"#>
<# message = "hello world" ; #>
blah blah blah etc. very complex example with embedded expression like
<#=message#>
I'd like to have an output function that would return the output blah blah etc.:
<## template debug="false" hostspecific="true" language="C#" #>
<## import namespace="System.IO" #>
<## output extension=".txt" #>
<## assembly name="System"#>
<#output();#>
<#+ output() { #>
blah blah blah etc. very complex example with embedded expression like
<#=message#>
<#}
#>
Of course the syntax above is not correct. How to do this ?
This is an alternative solution not using class feature blocks <#+ ... #>.
Using a lambda expression inside usual statement blocks <# ... #> allows defining a local function as follows:
<## template language="C#" #>
<## output extension=".txt" #>
<# Action output = () => { #>
loooooooong text <#= "message" #>
<# }; #>
<# output(); #>
This template produces the output below:
loooooooong text message
Actually you're very close with what you've got there.
I find it helps to remember that the template is essentially a C#/VB class under the hood, so when you use a <#+ #> block, you're really just adding a member to the class.
Once you've started using the <#+ #> notation, you have to keep using it, as you're still adding stuff to the class at the member level, not adding the the TransformText() method which regular <# #> tags do.
The correct syntax would be
<#+ public void output() { #>
blah blah blah etc. very complex example with embedded expression like <#=message#>
<#+ }
#>

Categories