KnownTypeAttribute in t4 file - visual-studio-2010

I'm trying to use some reflection in a .tt file, more specifically to determine the KnownTypes on a class. To do this I just use simple reflection, or rather want to use simple reflection, but when I try to:
List<String> GetKnownTypes(EntityType entity)
{
List<String> knownTypes = new List<String>();
System.Reflection.MemberInfo info = typeof(EntityType);
object[] attributes = info.GetCustomAttributes(typeof(KnownTypeAttribute), false);
for (int i = 0; i < attributes.Length; i++)
{
KnownTypeAttribute attr = (KnownTypeAttribute)attributes[i];
knownTypes.Add(attr.Type.Name);
}
return knownTypes;
}
I get slapped around the ears with an error:
Error 1 Compiling transformation: The type or namespace name
'KnownTypeAttribute' could not be found (are you missing a using
directive or an assembly reference?)
But, I have a reference to System.Runtime.Serialization. I also import
<## import namespace="System.Runtime.Serialization" #> at the beginning of the tt file.
The target framework is .NET framework 4 (no client profile).
Any thought?

Do you have an <## assembly #> directive to bring in System.Runtime.Serialization? In VS2010, project references don't play any part in assembly resolution in T4.

Related

T4 text template unable to call other code

Open VisualStudio2022 and create a new net6.0 class library.
Create a class to use in the T4 template and create a T4 template and use the class.
Class:
namespace ClassLibraryT4
{
public class Class1
{
public static string DoTheThing() { return "TheThing"; }
}
}
Now build the project so that its dll file exists on disc.
T4:
<## template debug="false" hostspecific="false" language="C#" #>
<## assembly name="$(SolutionDir)ClassLibraryT4\bin\Debug\net6.0\ClassLibraryT4.dll" #>
<## import namespace="ClassLibraryT4" #>
<## output extension=".cs" #>
namespace ClassLibraryT4
{
public class TheGeneratedClass
{
private const string _TheThing = "<# Class1.DoTheThing(); #>";
}
}
The T4 now fails to run because
nThe type 'Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Runtime, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
If I add to the T4:
<## assembly name="System.Runtime"#>
Then I now get
Error Running transformation: System.IO.FileNotFoundException: Could not load file or assembly 'System.Runtime, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.
File name: 'System.Runtime, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
at Microsoft.VisualStudio.TextTemplating6765B00A4659E4D1054752E9A2C829A21EECD20197C4EDDD8F5675E0DB91730A0DFF4528F1622E70821097EC90F6A2D0DE05F4739B3E0CD1BCAF45AAA20D419D.GeneratedTextTransformation.TransformText()
at System.Dynamic.UpdateDelegates.UpdateAndExecute1[T0,TRet](CallSite site, T0 arg0)
at Microsoft.VisualStudio.TextTemplating.TransformationRunner.PerformTransformation()
Can T4s work?
It seems to be impossible to use any outisde code; this does work in the T4:
private const string _TheThing = "<#= 5+2 #>";
and so does this:
private const string _TheThing = "<#= Thing() #>";
...
<#+
private static string Thing() {
return "thing";
}
#>
but this also has the _Could not load file or assembly System.Runtime...` problem:
<#+
private static string Thing() {
return Class1o.DoTheThing();
}
#>
Can T4s work?
Yes.
You just need to ensure that all used dll's are for x86 architecture.
This is limitation by Visual Studio as it is a 32-bit app.
The safest way to get this working is to use netstandard2.0 for any assembly to be loaded in a T4 template. If you include plain C# code (via include directive) only, then you may get away with it even if it uses net6.0 APIs after tweaking the assembly directives for a while. However, you will need to tweak it up to the point where it works for both, the Visual Studio and the MSBuild hosts.

LINQ Extensions not available inside CSharpCodeProvider

I have a .NET application that can take a script written in C# and executes it internally. The scripts are parsed by the class listed below and then compiled. I find that whenever I try and use System.Xml.Linq in the C# script that is compiled I get a compile error and I am not sure why.
public static void CreateFunction(string scriptCode, BO.ObjectBO obj)
{
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters options = new CompilerParameters();
options.ReferencedAssemblies.Add("System.Data.dll");
options.ReferencedAssemblies.Add("System.dll");
options.ReferencedAssemblies.Add("System.Xml.dll");
options.ReferencedAssemblies.Add("System.Linq.dll");
options.ReferencedAssemblies.Add("System.Xml.Linq.dll");
options.GenerateExecutable = false;
options.GenerateInMemory = true;
CompilerResults results = provider.CompileAssemblyFromSource(options, scriptCode);
_errors = results.Errors;
if (results.Errors.HasErrors)
{
DataTable errorTable = BO.DataTableBO.ErrorTable();
foreach(CompilerError err in results.Errors)
{
DataRow dr = errorTable.NewRow();
dr["ErrorMessage"] = "Line "+ err.ErrorNumber.ToString() + " " + err.ErrorText;
errorTable.Rows.Add(dr);
}
return;
}
Type binaryFunction = results.CompiledAssembly.GetType("UserFunctions.BinaryFunction");
_methodInfo = binaryFunction.GetMethod("Function");
}
Here is the error message I get when I try and run a script that makes use of LINQ extensions inside the compiler.
'System.Collections.Generic.IEnumerable<System.Xml.Linq.XElement>' does not contain a definition for 'Select' and no extension method 'Select' accepting a first argument of type 'System.Collections.Generic.IEnumerable<System.Xml.Linq.XElement>' could be found (are you missing a using directive or an assembly reference?)
Does anyone see what I may be doing wrong? I am attempting to include System.Linq and System.Xml.Linq yet the compiler does not seem to be able to locate them.
Here is an example C# script I am trying to compile that makes use of LINQ extensions.
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Xml.Linq;
namespace CompilerTest
{
public class BinaryFunction
{
public static void Function()
{
string xmlData = #"<data>
<clients>
<client>
<clientId>1</clientId>
<clientName>Dell</clientName>
</client>
<client>
<clientId>2</clientId>
<clientName>Apple</clientName>
</client>
</clients>
</data>";
XDocument xDoc = XDocument.Parse(xmlData);
List<string> results = xDoc.Descendants("data")
.Descendants("client")
.Select(x => x.Element("clientName").Value)
.ToList<string>();
}
}
}
UPDATE: I confirmed that the following assemblies were in the GAC. System.Xml and System.Xml.Linq. I also added the compiler version to the constructor and I still get the same error.
CSharpCodeProvider(new Dictionary<String, String> { { "CompilerVersion", "v4.6.1" } })
After searching for related errors I found the solution. I needed to add System.Core as a referenced assembly.
options.ReferencedAssemblies.Add("System.Core.dll");
Once I did this then the LINQ assemblies were used and I was able to use LINQ extensions. So to be clear my new code is
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters options = new CompilerParameters();
options.ReferencedAssemblies.Add("System.Data.dll");
options.ReferencedAssemblies.Add("System.dll");
options.ReferencedAssemblies.Add("System.Xml.dll");
options.ReferencedAssemblies.Add("System.Linq.dll");
options.ReferencedAssemblies.Add("System.Xml.Linq.dll");
options.ReferencedAssemblies.Add("System.Core.dll");
I am not sure why the reference to System.Core.dll is needed to be added as I would assume that it was referenced by default when creating a compiler instance but I guess not.

What is the '.tt' extension?

I work with a bunch of something.js.tt JavaScript files using Knockout and a bunch of something-else.tt HTML files.
The infrastructure is mostly a C backend with Perl serving API and we use these .tt files to show the HTML and .js.tt to serve the Knockout.js code. What is .tt?
A TT file is a Visual Studio Text Template, developed by Microsoft.
Text Template Transformation Toolkit, shortly written as T4, uses the .tt file extension for its source files. It is Microsoft's template-based text generation framework included with Visual Studio.
For more info, see the docs.
If you take a look inside the file, you'll probably notice a lot of logic injecting things. This is because this kind of files are used to generate other files.
As explained in the MS page shared by #Recev Yildiz:
In Visual Studio, a T4 text template is a mixture of text blocks and control logic that can generate a text file.
The control logic is written as fragments of program code in Visual C# or Visual Basic. In Visual Studio 2015 Update 2 and later, you can use C# version 6.0 features in T4 templates directives.
The generated file can be text of any kind, such as a web page, or a resource file, or program source code in any language.
There are two kinds of T4 text templates: run time and design time.
Here's an example of a code I've got from a Entity Framework file, from a ASP.NET Web Application (.NET Framework) project (MVC design):
<## template language="C#" debug="false" hostspecific="true"#>
<## include file="EF6.Utility.CS.ttinclude"#><##
output extension=".cs"#><#
const string inputFile = #"DBModel.edmx";
var textTransform = DynamicTextTransformation.Create(this);
var code = new CodeGenerationTools(this);
var ef = new MetadataTools(this);
var typeMapper = new TypeMapper(code, ef, textTransform.Errors);
var loader = new EdmMetadataLoader(textTransform.Host, textTransform.Errors);
var itemCollection = loader.CreateEdmItemCollection(inputFile);
var modelNamespace = loader.GetModelNamespace(inputFile);
var codeStringGenerator = new CodeStringGenerator(code, typeMapper, ef);
var container = itemCollection.OfType<EntityContainer>().FirstOrDefault();
if (container == null)
{
return string.Empty;
}
#>
//------------------------------------------------------------------------------
// <auto-generated>
// <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine1")#>
//
// <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine2")#>
// <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine3")#>
// </auto-generated>
//------------------------------------------------------------------------------
<#
var codeNamespace = code.VsNamespaceSuggestion();
if (!String.IsNullOrEmpty(codeNamespace))
{
#>
namespace <#=code.EscapeNamespace(codeNamespace)#>
{
<#
PushIndent(" ");
}
#>
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
<#
if (container.FunctionImports.Any())
{
#>
using System.Data.Entity.Core.Objects;
using System.Linq;
<#
}
#>
<#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext
{
public <#=code.Escape(container)#>()
: base("name=<#=container.Name#>")
{
<#
if (!loader.IsLazyLoadingEnabled(container))
{
#>
this.Configuration.LazyLoadingEnabled = false;
<#
}
foreach (var entitySet in container.BaseEntitySets.OfType<EntitySet>())
{
// Note: the DbSet members are defined below such that the getter and
// setter always have the same accessibility as the DbSet definition
if (Accessibility.ForReadOnlyProperty(entitySet) != "public")
{
#>
<#=codeStringGenerator.DbSetInitializer(entitySet)#>
<#
}
}
#>
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
<#
foreach (var entitySet in container.BaseEntitySets.OfType<EntitySet>())
{
#>
<#=codeStringGenerator.DbSet(entitySet)#>
<#
}
foreach (var edmFunction in container.FunctionImports)
{
WriteFunctionImport(typeMapper, codeStringGenerator, edmFunction, modelNamespace, includeMergeOption: false);
}
#>
}
The file was way larger than what you see here. And as you can see, it seems to be a really busy code.
This is the context where the file is placed:
TT stands for - Visual Studio Text Template is a software development tool created by the Microsoft.
Further explanation - TT file contains text block and control logic used for generating new files. To write the Text Template file we can use either - Visual C# or Visual Basic Code
It's mainly used for handling Run Time text generation and source code generation both at once. They're like normal text files and can be viewed in any text editor.

Error creating instance of C# class in F# script file

I have the following C# class that I would like to make use of in F#
using System;
using System.Collections.Generic;
using System.Text;
namespace DataWrangler.Structures
{
public enum Type { Trade = 0, Ask = 1, Bid = 2 }
public class TickData
{
public string Security = String.Empty;
public uint SecurityID = 0;
public object SecurityObj = null;
public DateTime TimeStamp = DateTime.MinValue;
public Type Type;
public double Price = 0;
public uint Size = 0;
public Dictionary<string, string> Codes;
}
}
I would like to create an instance of it in F#. The code I am using to do this is in an f# script file
#r #"C:\Users\Chris\Documents\Visual Studio 2012\Projects\WranglerDataStructures\bin\Debug\WranglerDataStructures.dll"
open System
open System.Collections.Generic;
open System.Text;
open DataWrangler.Structures
type tick = TickData // <- mouse over the "tick" gives me a tooltip with the class structure
// it bombs out on this line
let tickDataTest = tick(Security = "test", TimeStamp = DateTime(2013,7,1,0,0,0), Type = Type.Trade, Price = float 123, Size = uint32 10 )
The error I get is:
error FS0193: internal error: Could not load file or assembly 'file:///C:\Users\Chris\Documents\Visual Studio 2012\Projects\WranglerDataStructures\bin\Debug\WranglerDataStructures.dll' or one of its dependencies. An attempt was made to load a program with an incorrect format.
I have checked the file paths and they seem to be correct. I can mouse over the 'type tick' and it gives me the structure of the C# object. So It seems to be finding the C# code. Can anyone tell me what I am doing wrong here? Syntax? Still very new to C# -> F# introp
There are several things to check here:
Make sure that fsi.exe is running in a bit mode that is compatible with your WranglerDataStructures.dll. You run fsi.exe as a 64, or 32 bit process by setting a flag in the Visual Studio Options, under F# Tools -> F# Interactive -> 64-bit F# Interactive. You can usually avoid these types of problems by setting your C# assembly to compile as Any CPU.
Make sure that WranglerDataStructures.dll doesn't depend on other libraries that you are not referencing from F#. Either add the references in F#, or remove them from WranglerDataStructures.dll.
If these steps don't yield success try using the fuslogview.exe tool http://msdn.microsoft.com/en-us/library/e74a18c4.aspx to see exactly what reference is not being loaded.

C#, ANTLR, ECMAScript grammar troubles

I'm trying to parse JavaScript (ECMASCript) with C#.
I found the following instruction on how to create new project:
http://www.antlr.org/wiki/pages/viewpage.action?pageId=557075
So I've downloaded ANTLRWorks, ANTLR v3, unpacked ANTLR, created a VS2010 project (.NET4), added references, checked and generated the grammar.
Then I recieved a lot of compilation error:
The type or namespace name 'AstParserRuleReturnScope' could not be found (are you missing a using directive or an assembly reference?)
The type or namespace name 'GrammarRule' could not be found (are you missing a using directive or an assembly reference?)
Stackoverlowed for them and got a solution: antlr c# errors when integrating into VS2008
So I've downloaded new runtime, overwrite the old one and recompiled the project and got
The name 'HIDDEN' does not exist in the current context d:\Workspace.1\ScriptParser\ScriptParser\TestLexer.cs
Ok, I've changed HIDDEN to Hidden as recommended at in the following conversation: [antlr-interest] How viable is the Csharp3 target? (more specific questions)
Now I'm trying to parse the input. I found a few examples and wrote the following code:
using Antlr.Runtime;
namespace ScriptParser
{
class Program
{
static void Main(string[] args)
{
var stream = new ANTLRStringStream("1+2");
var lexer = new TestLexer(stream);
var tokenStream = new CommonTokenStream(lexer);
var parser = new TestParser(tokenStream);
// what exactly should be here???
}
}
}
My goal is to parser JavaScript file with ANTLR but it seems that it will be the not as easy as I thought...
Update:
As suggested in Why are antlr3 c# parser methods private? I've modified the Test.g grammar by adding the "public" modified before the expr rule:
public expr : mexpr (PLUS^ mexpr)* SEMI!
;
and then regenerated the code, replaced HIDDEN to Hidden (again) and modified the code as follows:
var stream = new ANTLRStringStream("1+2");
var lexer = new TestLexer(stream);
var tokenStream = new CommonTokenStream(lexer);
var parser = new TestParser(tokenStream);
var result = parser.expr();
var tree = (CommonTree)result.Tree;
And not it is crashing on the line
root_0 = (object)adaptor.Nil();
in the following generated code
try { DebugEnterRule(GrammarFileName, "expr");
DebugLocation(7, 0);
try
{
// d:\\Workspace.1\\ScriptParser\\ScriptParser\\Test.g:7:13: ( mexpr ( PLUS ^ mexpr )* SEMI !)
DebugEnterAlt(1);
// d:\\Workspace.1\\ScriptParser\\ScriptParser\\Test.g:7:15: mexpr ( PLUS ^ mexpr )* SEMI !
{
root_0 = (object)adaptor.Nil();
DebugLocation(7, 15);
PushFollow(Follow._mexpr_in_expr31);
with the NullReferenceException message because the adapter is null.
I've resolved it by adding
parser.TreeAdaptor = new CommonTreeAdaptor();
Update 2:
So, finally I've started with my primary task: parse JavaScript.
ANTLR highlights the ECMAScript grammar by Chris Lambrou.
So I've generated lexer/parser and run it with the very simple JavaScript code:
var f = function () { };
and the parsing fails with the following output from tree.ToStringTree():
<error: var q = function () { };>
Your grammar rule says that there should be a semicolon at the end of the expression, but in you main function:
var stream = new ANTLRStringStream("1+2");
is missing a semicolon. Shouldn't it be "1+2;"?

Resources