I would like to generate some C# code based on existing code.
More precisely I need some mappers for existing enums as well as converters, unit-tests for them.
It would be a longer discussion why I've generate this code rather than go for a generic approach, but considering that I would like to generate some classes based on some enumeration types, what options would I have?
I am just thinking about visual studio extensions, some templates or maybe resharper addins but so far I haven't done anything like this...
I would appreciate any input of those of you who had previous experience with such a task.
ReSharper supports Live Templates as a means of generating code. You can create whatever code you like in there, with editable or linked hotspots to provide customisation points (e.g. current file name, class name, time, new guids, suggested variable names, etc). You can generate code snippets in existing files, surround existing code, or create new files. ReSharper 8 also introduces support for multi-file templates, creating more than one file at a time.
However, ReSharper's templates don't support things like loops - you can't loop over an XML file and generate a class member for each element, for example. T4 would be a better solution for that.
Reegenerator supports generating code from existing code. You can create a code generator and then attach it to the file that contains the enum type. The generator will be executed everytime the file is saved.
The code generator are given access to the Visual Studio DTE Object, which will give you the code structure through CodeElement classes (CodeClass, CodeAttribute, etc). From there you can either generate a separate partial file through a T4-like template, or simply manipulate the code directly using the DTE EditPoint.
Related
I am writing a basic code generation tool. It's pretty bare-bones and I am only doing it as a learning exercise and for messing around for fun, not for serious use. It prompts you for a Class name and lets you add fields, specifying the name, type, whether it is a Primary Key, Foreign Key, nullable, a Collection type such as an array or Generic List, etc. It creates a DTO class with Properties and if you check that it is a database table class, it also creates a DataAccess class (only things fully fleshed out with actual functional code that interacts with DB are GetByID, Insert, Update, Delete and an optional GetAll), a Business Logic Layer class with only pass through methods that call DataAccess class by default, a XAML and corresponding .cs class for both a details screen as well as a search screen with two-way model binding for all the field controls on the details screen. It also creates the text for the Stored Procedures for all of the methods in the DataAccess class.
Is there a way for me to make the functionality I've described a plug-in for my copy of Visual Studio so I could use it on any of my projects I choose as I develop them from inside VS and have the generated classes automatically added to the solution? I only want to use it for playing around, so I won't be too disappointed if this isn't possible. Can this be done in Visual Studio by an average programmer? I know I can finish my project in terms of code-generation functionality, except I'm more asking if it can be added to VS as a plug-in and what type of skills I would need to acquire to get it accomplished. In particular, is it even possible for it to use the generated SP text to create actual SPs in the database from within a Visual Studio project?
Yes, this would be possible, look at the visual studio SDK: https://learn.microsoft.com/en-us/visualstudio/extensibility/visual-studio-sdk
However it would not be at all trivial.
As of lately I'm using quite some code generation, usually in combination with partial classes. Basically the setup is as follows:
Partial class containing generated code. Some of this code will call partial methods. The code is re-generated a lot of time. The code generator is in some cases a custom tool.
Partial methods are manually implemented in a separate file.
The problem is that when I'm using Intellisense features like "generate method", they are for some reason generated in the file containing the generated code. Obviously I don't want that.
My question is: Is it possible to generate some hint that tells Intellisense it shouldn't touch certain 'cs' files (but instead the other partial class)?
Update
In retrospect I should have noted that I'm using a custom tool to generate the code. It's not a EF or a simple transformation; there's quite a bit of logic involved in the code generation. Also, it generates a complete namespace and class structure with partial classes. The 'root namespace' is found by extracting it from the csproj file and then using the folder structure to figure out the absolute namespace (it's similar to how Linq2sql does this).
The answer suggested by xanatos (thanks!) works: intellisense sorts its operation on the name, then alphabetically sorts on the name and then picks the first item in the list. This means that you can generate a zzzz.foo.cs which (albeit a bit ugly) will work just fine. I've just ran some experiments and found out that the feature find all references returns the order that VS appears to use. As it turns out, it works like this:
Say you have a custom tool that works on the filename foo.bar and transforms it into foo.cs. The custom tool will generate the content as string and pass it back to Visual studio (that's just how custom tools work...). The result will be in a file called foo.cs.
Now, I was quite surprised to found that Intellisense does not sort it as foo.cs but rather as foo.bar\foo.cs. In other words: regardless of how you name the 'cs' output in your custom tool, you have to rename the base file foo.bar to something like zoo.bar.
While that might be a workaround, I'm hesistant to accept it as the answer, because I would have to give files in my project strange names (names have meaning...). Also, some of my custom tools have dependencies on their filenames, so that will also get broken...
Therefore, I'm still open for suggestions on how to fix this properly.
From a simple test I've done in VS2013, it seems that Visual Studio 2013 adds the method to the "first" file he finds in the Solution Explorer. So you could simply add a .something.cs to your file-name, like MyClass.generated.cs vs MyClass.cs. Be aware that the VS2013 seems to be using the "full path", with path ordering based on name. So:
Z\MyClass.cs
comes after
MyClass.generated.cs
(and Intellisense will put code in MyClass.generated.cs) even while in the Solution Explorer all the folders are ordered first.
Full example:
A\MyClass.gen3.cs
MyClass.gen2.cs
Z\MyClass.gen1.cs
This should be the order as "seen" by the Intellisense, so it will put the new classes in A\MyClass.gen3.cs.
Assuming you're talking about the EF, I always change the template file (.tt) so the filename of the auto-generated file is [classname].model.cs. This means my partial file, which by convention is called [classname].cs is alphabetically first and always seems to get picked for auto-generation.
All you have to do is find/replace all the:
fileManager.StartNewFile(entity.Name + ".cs");
With:
fileManager.StartNewFile(entity.Name + ".model.cs");
There should be 3.
This has other benefits like auto-generated files are clearly marked in the filename.
I still have no idea why they didn't do this in the first place.
If you're not talking about the EF, the same trick of using the filename to order them should work.
I'm creating a C# file using a T4 template. I would like to reuse of the class features in my template, GetListOfItemsToLoopOver() in the generated C# code.
Is it possible to do this without creating a new assembly?
Turn it around: put your GetListOfItemsToLoopOver() method in a standalone C# source file in a class, and add that source file to your project. You can then also include that source file in your T4 template (using the T4 Include directive).
You will need trivial differences between the T4 version and the non-T4 version, at least relating to the using, namespace, and perhaps also the class bits, but that's easily handled by using #if T4...#endif blocks, and making sure the T4 symbol is defined when running the template.
It's easy to accidentally change your source file in ways that only work for one of the places it gets used, though, so do add a comment that the T4 template should be re-tested when changes are made.
It seems like the documentation around Roslyn is a bit lacking?
I am not able to find good comprehensive documentation.
What I am trying to do essentially is copy the public surface of an existing API (.dll)
into a new assembly (need to create source code .cs files!) and at the same time make a variety of tranformations to the resulting code (think making wrapper classes).
Would really appreciate any help in how I can use Rolsyn to load the initial SyntaxTree from an existing assembly and how to do those basic tranforms (for example exclude internal classes etc)
In the current Roslyn CTP there is a Roslyn.Services.MetadataAsSource namespace which can be used to convert an type's public interface to source code. This is what we implement the F12 "metadata as source" feature with. Now, it generates only a shell of source code which won't actually compile, so you'd have to use further APIs to munge the syntax tree into what you want. Alternatively, you could use the Roslyn.Services.CodeGeneration namespace to generate source from these symbols automatically. I should warn the MetadataAsSource namespace may go away in future versions of the API.
You can import symbols from metadata by creating an otherwise empty compilation with the metadata references you care about added, and then from that compilation browsing the type hierarchy from GlobalNamespace property, or calling Compilation.GetReferencedAssemblySymbol() and then digging through that. This is actually far better than using reflection, since it'll properly express the symbol model from the "C# perspective" instead of the "CLR perspective" -- reflection won't give you information for uses of dynamic, some default parameter values, etc.
It seems like the documentation around Roslyn is a bit lacking? I am not able to find good comprehensive documentation.
Roslyn is at the Community Technology Preview stage, so it's not surprising that its documentation is lacking. You can find some sources at Roslyn API documentation.
What I am trying to do essentially is copy the public surface of an existing API (.dll) into a new assembly (need to create source code .cs files!) and at the same time make a variety of transformations to the resulting code (think making wrapper classes).
Working with assemblies this way is not something Roslyn can do. But it seems for what you want, reflection for reading the assembly combined with Roslyn for writing the new code would work. But you would need to write all the code to translate from the reflection model to Roslyn's model (e.g. Type → TypeDeclarationSyntax, MethodInfo → MethodDeclarationSyntax, etc.).
I want to modify the default code generation strategy, how can I do that?
I simply want to modify the class name from <#=code.Escape(container)#> to Entities and change the default connection string to name=Default.
(I don't want to create a template file for the project, I want to edit it so it will work globally)
I've searched for .tt files, I could only find the ItemTemplates. I don't know what generates the code by default, this is the one I want to edit.
Update: I still don't know how to do this.
You can see what generates the code if you click your EMDX file and check file properties in Visual Studio. Look for Custom Tool property that will tell you the class name of the generator that converts EDMX XML into compilable code.
But regarding model customization, I would still suggest you use T4 that takes your EDMX and generates the same code as original generator. The good thing is that you can then manipulate it 'till you drop dead if you will.
And if you intend to use the T4 on several EMDXs in your project then I suggest you rather create a .ttinclude file and reference it in every .tt file. This way you will reuse existing code and when you'd change it it will be reflected on all generated files.
One more question: What do you mean by globally? Globally for all EDMX files in your project or for all EDMX files on your machine or all EDMX files on your project team or what? Define globally.
Additional edit
Since you've defined global as all projects on a particular machine this is what I'd do.
First of all: using T4 allows you to adjust EDMX -> code conversion changes per project or better said per solution (all projects in a particular solution). On other projects/solutions on the same machine, you should include the same T4 template reference. So it's not actually global in your sense...
The best thing you could do is to create a custom Visual Studio item template with this T4 template so it'd be much much easier adding this default T4 template to your solutions/projects. That's as global as you can make it by T4.
Maybe you should read this MSDN entry that talks about your kind of customization:
How to: Customize Object-Layer Code Generation (Entity Data Model Designer)
I don't know whether it is even possible to alter the default code generation.
Instead of trying to modify the default code generation, I suppose you could create a .tt that generates a derived class from the ObjectContext. This way you can name it and implement the default constructor as you wish.
Something like:
<#=Accessibility.ForType(container)#> partial class Entities : <#=code.Escape(container)#>
{
public Entities()
: base("name=Default")
{ }
}
The downside to this approach is you will need to deploy this .tt file with every EDMX you create.
However, with Visual Studio's addin architecture you could look into creating a template that creates an EDMX and this .tt file by default. As a replacement for adding a plain "ADO.NET Entity Data Model"
Looking into the EntityModelCodeGenerator (the Custom Tool that is run by the default codegen strategy), it seems that it is registered with the SingleFileGenerator extensibility mechanism, which is a COM component. Some more info here.