An academic query about T4 templates - t4

Ok,
I'm thinking a little ahead here in my current project, so I apologize for how vague this question is going to be. Is it possible in one project of a solution to have a T4 template that references one of the other assemblies in the solution and inspects the exported types of the other assembly and uses their metadata to build up other code? I don't need to reference the types in that assembly directory, I just need to be able to get a list of all of them that derive from a particular base class and some metadata about them. I'm thinking of doing this specifically for some objects that are built up from a common base class and dumped to the database by NHibernate as an easy way to generate DTO classes from them for throwing at the client during AJAX calls. I'm not looking for an absolutely perfect solution, just one that gets a lot of the easy cases out of the way.
Again, I don't really have a specific example. I'm a few days out from running into this problem head-on, but it occurred to me that this option might cover a lot of the cases I might run into.
Thoughts?

Yep, this should be fine - you can use the <## assembly #> directive and also use $(SolutionDir) and other VS macros to get a starting point to navigate to your other project's output. Then you can use reflection to read the metadata you're interested in.

I was trying to achieve something similar and it works fine. In the example .tt file below, the name of my own assembly in the same solution that I am referring to is SomeLibrary. The class I am reflecting on is an interface called SomeLibrary.SomeInterface. There are ways to make this more generic, but I did not want to make the sample too complicated. Also, please do not pay attention to the overall result of this template, this is just to give you an impression on how it would work:
<## template language="C#" #>
<## output extension=".cs" #>
<## assembly name="$(SolutionDir)$(SolutionName)\bin\$(ConfigurationName)\$(SolutionName).dll" #>
<## import namespace="System.Reflection" #>
<#
Type someIType = typeof(SomeLibrary.SomeInterface);
#>
namespace SomeLibrary
{
class <#=someIType.Name#>Impl: <#=someIType.Name#>
{
<#=someIType.Name#> encapsulatedIntf;
public <#=someIType.Name#>Impl(<#=someIType.Name#> anIntf)
{
encapsulatedIntf = anIntf;
}
// Methods to be implemented:
<#
MethodInfo[] methods = someIType.GetMethods();
foreach (MethodInfo m in methods)
{
#>
// <#=m#>;
<#
}
#>
}
}
In my case, this results in
namespace SomeLibrary
{
class SomeInterfaceImpl: SomeInterface
{
SomeInterface encapsulatedIntf;
public SomeInterfaceImpl(SomeInterface anIntf)
{
encapsulatedIntf = anIntf;
}
// Methods to be implemented:
// Int32 someMethod(Int32);
// System.String someOtherMethod();
}
}

Related

Typescript enum, used from implementation and interfaces

I have the following, in CommandEnum.ts:
export enum CommandEnum {
createProject,
renameProject,
hablaBabla
}
in a module which I am able to reference from implementation code, using
import {CommandEnum} from '../server/contracts/CommandEnum'
let x = CommmandEnum.hablaBabla
The enum file is compiled into a javascript function with export logic, in CommandEnum.js.
This now works fine, but I want to reference this enum in my interfaces as well, I try:
/// <reference path="../contracts/CommandEnum.ts" />
namespace ValueTypes {
export interface Command {
type : CommandEnum;
referenceId : string;
}
}
Now, this reference does not import the CommandEnum type, but some of the other combinations of modules / namespace / export default I have tried does. I can get the reference syntax to work, but not the module syntax and the other way around - but not both.
Is this actually possible? Using an enum from a pure definitions interface file seems like a very common scenario. But when the interface is implemented the enum must be available in "function form" and these two models does not seem to combine?
I had the same problem with classes, which I wanted to namespace, .Net-style - which I had to give up. Classes, however, are not referenced in my interfaces - enums are.
I work with node.js and compile to individual files, not a single concated output.
This now works fine, but I want to reference this enum in my interfaces as well
You can move stuff from a module into the global namespace use declare global
E.g. myEnumGlobalDeclare.ts
import {MyEnum as MyEnumModule} from "./myEnum";
declare global {
declare var MyEnum: typeof MyEnumModule;
}
E.g. myEnumGlobalDefine.ts
import {MyEnum as MyEnumModule} from "./myEnum";
MyEnum = MyEnumModule;
Or something similar ^. Of course this means your runtime should support global augmentation e.g. in nodejs you need to use globals and in browsers window.
More
I definitely do not recommend going down this path. Instead create a global types.ts module and just use that everywhere. E.g. alm has this file : https://github.com/alm-tools/alm/blob/master/src/common/types.ts

Use types (interfaces) from other commonjs modules in Visual Studio using TypeScript without having to import that module?

I'm trying to simply use types (interfaces) from other commonjs modules. I'm wondering if there is a way to do this without having to import the module each time I need just the types (no implementation)?
Also to the same point. How would I use commonjs declared module types in projects without compiling with "--module commonjs"? (just to use the types, I'm aware you could do var x = require('x') if you don't care about type safety, but that defeats the purpose of TypeScript)
This seems like a valid use case as one could desire to create a library free from the actual implementation... But as I currently see it, you have to import x = require('x') on the actual implementation, even if I don't need it and already have the definition file.
Is there a way to tell the compiler that it "just exists," similar to how declare works with variables?
Example:
Suppose I have file.ts that I'm not compiling with any module setting (none):
/// <reference path="ExternalCommonJSModule.d.ts" />
export class A {
public foo(bar: ITypeFromExternalCommonJSModule): number {
return bar.x * 2;
}
}
And suppose ExternalCommonJSModule.d.ts looks like this:
declare module "ExternalCommonJSModule" {
export interface ITypeFromExternalCommonJSModule {
x: number;
}
}
(note, this doesn't compile because file.ts isn't compiled with --module commonjs and it doesn't import the implementation of the .d.ts)
I'm wondering if there is a way to do this without having to import the module each time I need just the types (no implementation)
You can put the interfaces in a global module e.g. globals.d.ts and then you would be able to use them throughout the project without importing.
Note: as soon as you global you are severely limiting the shareability of your code and opening yourself to the diamond version dependency problem.

How do you change the output of a T4 template from a base class?

I want to create a tool as a base class to a T4 template for generating code at design time within Visual Studio .NET. I want my TextTransformation derived base class to have the final say as to what gets output into the generated file. The problem is that using .NET reflector I cannot find a method that I can override (or declare as new) or any event to hook into to change the contents of the GenerationEnvironment property before it gets used by the T4 framework.
Here is an example of the base class:
public class MyTool
: TextTransformation
{
public override void Initialize()
{
// Initialize My Tool
base.Initialize();
}
// Override the text that is generated from the T4 template and modify it
// (strip out some well known .NET attributes) here.
}
And it would be inherited from a T4 template like this:
<## template debug="false" hostspecific="true" language="C#" inherits="MyTool" #>
<## assembly name="System.Core" #>
<## assembly name="MyTool.dll" #>
<## assembly name="Microsoft.VisualStudio.TextTemplating.10.0" #>
<## import namespace="System.Linq" #>
<## import namespace="System.Text" #>
<## import namespace="System.Collections.Generic" #>
<## import namespace="MyTool" #>
<## output extension=".cs" #>
[Ender]
public interface INinjaEnder
{
INinja Build();
}
[Required]
public interface INinjaNamed<TRemainder>
{
TRemainder Named(string name);
}
public interface INinjaCostume<TRemainder>
{
TRemainder WithCostume(Costume costume);
}
I can hit breakpoints in the Initialize() method or the Dispose() method (when overridden) in MyTool, but there doesn't seem to be anything in between I can use for modifying the output. Some options I am considering:
Using the Dispose() method to create a separate file to output with the changed contents and just ignoring the output of the T4 template altogether. Unfortunately, generating a file with the same name in the Dispose() method doesn't work because it gets overwritten by the original text.
Forcing the end user to add a method call at the end of every template so I can run what I need to at the right time.
Override the WriteLine() and/or Write() methods (using new) and use them to strip out the text I don't want in the final file. This will likely entail RegEx matches on the output of the GenerationEnvironment so I can see if the code is in there and is completely generated before stripping it out.
I also thought of subclassing StringBuilder to override the ToString() method (the GenerationEnvironment returns a StringBuilder), but the StringBuilder class is sealed.
I should mention that I am targeting all editions (including express editions) of Visual Studio 2010, 2012, and 2013. I noticed that there were some pretty big differences in T4 between 2010 and 2012, so I need to come up with a solution that works in both environments.
Some Context
My reasons for going down this path are:
T4 is supported by express editions, custom tools (IVsSingleFileGenerator) and designers are not. A large part of my target audience (open source developers) will likely be using an express edition.
I want the user to specify the input as code (interfaces and attributes through the T4 template) so they don't have a steep learning curve to use my tool by having to input something more abstract such as XML, diagrams, or a designer.
I don't want the user to have to set a reference to my DLL (and thus have to ship the DLL with their code) in order to use my tool. Using T4 I can strip the attributes out of the final code after the generator is done using them, so only my tool needs to have a reference to the attributes.
T4 supports dependency injection through property injection of the base class, so my users will be able to supply their own extensions to my tool.
I can support both VB and C# T4 templates without creating too much duplicate code (since most of it will be in my DLL).
However, I will accept architectural advice if there is a better choice available than a T4 base class.

Using inheritance and parameters at the same time in t4

I am using t4 (text templates) to generate emails and would like a common base class for my templates. To do this, I create an email base template and have all my email templates inherit from it. Like so:
The base template:
<## template language="C#" #>
The derived template:
<## template language="C#" inherits="BaseTemplate" #>
<## parameter name="Param" type="System.String" #>
Template! Param=<#= Param #>
Note the parameter in the derived template. When this is present, it causes the template to have an Initialize method. And, because the the derived template derives from a base template, the declaration of the Initialize method in the derived template uses the keyword "override". However, there is no Initialize method on the base template. This causes an error:
'Template.Initialize()': no suitable method found to override
I can work around this by declaring a dummy parameter in the base template:
<## template language="C#" #>
<## parameter name="DummyParam" type="System.String" #>
Which causes an Initialize method to be generated in the base template, from which the derived template may override.
My questions is, am I missing something? Having to add a dummy parameter to pacify the compiler make it seem like I'm doing something wrong.

Change modified classes generated by T4

I used T4 to generate some entity classes , but I forgot to make them Serializable. So is there any solution to use something like T4 to add a Serializable attribute to all my classes ?
If you've already modified your generated classes, I think you might find it easier to do with Visual Studio's global replace with a fancy regex to find the classes you need to change. (If that's not possible, it's not hard to write a quick console application to process the files).
Using T4 you can control which files are overwritten, for instance using the Output.PreserveExistingFile which comes with T4 Toolbox.
<#
var t = new SampleTemplate();
t.Output.File = "Sample.cs";
t.Output.PreserveExistingFile = true;
t.Render();
#>
And then you can delete the specific files you want recreated. But however you determine which files to overwrite, any changes to those files that you've made since last regenerating will be lost. One recommendation is to build your templates as partial classes so that you can put all manual modifications in a separate file (but that doesn't really help you if you've already modified your generated classes).
Are those generated classes partial classes? If so, use another T4 template in order to generate a partial class definition decorated with the Serializable Attribute.
Otherwise you could use the Visual Studio CodeModel in order to identify all classes that need this implementation inside another T4 template and then let this T4 template add the code fragments necessary.
If you are using tangible's T4 Editor, it comes with a free Template Gallery and as far as I know there is a template called "Add NotifyPropertyChanged" which does pretty much what you are looking for: discovering code classes inside a Solution and making them implement a given interface. You might adapt that one easily and get your desired functionality.
Hope that helps.

Resources