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

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#>
<#+ }
#>

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.

T4 parameter directive using .Net Core

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);

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

Pass parameter to C# function in XSLT

I have a XML and XSLT like this:
XML
<process id="1">
<task id="task1">
</task>
<task id="task2">
</task>
...
</process>
XSLT
<msxsl:script language="C#" implements-prefix="user">
<![CDATA[
public string GetStartTask(int ProcessID)
{
return StartTask(ProcessID);
}
]]>
</msxsl:script>
...
<xsl:if test="#id = user:GetStartTask(<xsl:value-of select="/../#id"/>)">
...
</xsl:if>
...
(StartTask is method that return string.)
In code above I try to pass process id to GetStartTask function by this:
<xsl:value-of select="/../#id"/>
But this code is incorrect. I read many post but I don't find syntax for this issue.
It would be very helpful if someone could explain solution for this issue.
I think you meant to write:
<xsl:if test="#id = user:GetStartTask(../#id)">
This is assuming you're in the context of task and you want to compare the value of the current task's idwith the value returned by the GetStartTask() function with the parent process id as the argument.
If your current context is <task>, you should try
<xsl:value-of select="../#id" />
as an XSLT expression.

Using variables in C# tags in html, using asp.net

I was wondering if it was possible to define and use a variable in your html using C# tags.
So for instance
<% String var = "simpleString"; %>
<link rel="stylesheet" type="text/css" href="style.css?v=<%Response.Write(var);%>" />
I have been trying to get something like this to work. When I write it like
<% String var = "pineapples"; Response.Write(var);%>
it works, but when I write them in separate tags it doesn't seem to read it as a variable.
var is a keyword in c# used to implicitly type a variable.
String is explicitly typed.
You have used both in your example:
String var = "simpleString";
You could change it to:
String var1 = "simpleString";
using your desired variable name above.
<% String var1 = "simpleString"; %>
<link rel="stylesheet" type="text/css" href="style.css?v=<%=var1%>" />
Also note that from mark-up you can use <%= %> to output rather than Response.Write.

Categories