Can CodeDom add Source Code Files to a Project? - code-generation

I have been using CodeDom to do some code generation. It works great, but I haven't found a way to include the generated source code files in a project. I started using T4 and the T4Toolbox to generate code because it supports integration with project files.
Does anyone know if CodeDom supports this functionality too? I'd consider taking a second look at CodeDom if it only supported this one feature.
Here is an example of how I make a source code file with CodeDom:
protected void CreateSourceFile(CodeCompileUnit codeCompileUnit,
string fileName,
out string fileNameWithExtension)
{
fileNameWithExtension = string.Format("{0}.{1}",
fileName,
CodeProvider.FileExtension);
var indentedTextWriter =
new IndentedTextWriter(new StreamWriter(fileNameWithExtension,
false),
TabString);
CodeProvider.GenerateCodeFromCompileUnit(codeCompileUnit,
indentedTextWriter,
new CodeGeneratorOptions());
indentedTextWriter.Close();
}
That works fine but it just outputs the file to the hard drive somewhere (probably bin folder).
Here is a second example of some code I use with T4, this one specifies the output as part of the project the template is transformed in:
public class RDFSClassGenerator : Generator
{
private readonly string rootNamespace;
private readonly string ontologyLocation;
public RDFSClassGenerator(
string rootNamespace,
string ontologyLocation)
{
this.rootNamespace = rootNamespace;
this.ontologyLocation = ontologyLocation;
}
protected override void RunCore()
{
XElement ontology = XElement.Load(ontologyLocation);
var service = new RDFSGeneratorService(ontology);
foreach (MetaClass metaClass in service.MetaClasses)
{
var rdfsClassTemplate = new RDFSClassTemplate(rootNamespace, metaClass);
rdfsClassTemplate.Output.File = "Domain/" + metaClass.Name + ".cs";
rdfsClassTemplate.Render();
}
}
}
So the T4 code will output the file into the "Domain" folder of my project. But the CodeGen stuff just outputs the file on disk and doesn't update the project file.
Here is a visual:

Yes, it can. Here is how: http://www.olegsych.com/2009/09/t4-and-codedom-better-together/

Short answer is no, but I could be wrong (ever try to prove a negative?)
Your question was a little confusing as CodeDom isn't exactly equitable with T4. T4 templates are a convenient way of generating code files the same way, for example, asp.net generates HTML files, mixing text and code that gets executed to generate a file that is then interpreted by something else (such as a compiler or a browser). CodeDom is usually used to generate assemblies at runtime rather than files, although you can do it (as you have discovered).
While T4 makes it easy to add files to the solution, you can do this with CodeDom as well. I don't believe it supports interaction with the solution directly, but you can manage this using EnvDTE, or the automation model for Visual Studio.
The problem with this is that the automation model isn't easy to work with. EnvDTE is a wrapper around COM classes, which is always fun to code against. Also, you have to be careful when attempting to get the object. The naive implementation will get the object from the first instance of Visual Studio loaded. You have to poll the Running Object Table to find the current instance. Once you have it, you must deal with searching through the dte for the location you're looking for, deal with source control, locked files, etc etc.
Working with it, you start to learn why T4 was created in the first place.
The question you have to ask yourself is, "Does CodeDom give me enough that T4 doesn't to make up for all its shortcomings?"

Related

Can telosys generation invoke external program prior to writing to target location and based on the return value continue or cancel writing to target

I want to generate c# asp.net core razor pages with many projects: Application, Domain, Infrastructure, Core etc.
I would like to know if Telosys is extensible to allow a custom console program to be called with arguments: , so this program can do some code merges in order to preserve possible additions/changes to the code a developer did since the previous code generation?
C# can have some directives called #region to partition the code and these will be "regenerate only regions" and outside these regions the developer can put his own code. We need to preserver his code.
C# Partial classes and methods can also help.
I want to have capability to merge newly re-generated code (e.g. entity class - I can add some properties or change some property name, type, annotations, tags etc.) into previously generated entity class. This could be done by creating #region .... #endregion where the code can be regenerated into and all the code that is outside these regions should be preserved. See: efg.loresoft.com/en/latest/regeneration So I would like to know if there would be a way to temporarily generate files in a TMPGEN folder and allow some "merge program to run" that will mege new code with previously generated code (with some code added by the developer) previously determining if merge is needed (compare hashes)
This is the code from EntityFrameworkCore.Generator tool that merges existing regions https://github.com/loresoft/EntityFrameworkCore.Generator..EntityFrameworkCore.Generator.Core/Templates/CodeTemplateBase.cs has the protected virtual method:
void MergeOutput(string fullPath, string outputContent) show that fullPath is an existing file and outputContent is the result of parsing the template (new content). RegionParser uses these namespaces: Microsoft.CodeAnalysis.CSharp; and Microsoft.CodeAnalysis.CSharp.Syntax;
so I need to user c# console application to user this preserver region code.
Thanks,
Rad
Yes, Telosys is extensible by using specific Java classes loaded in the template context with $loader
See : Is it possible in a Telosys template to call a function created specifically?
So, you just have to create a Java class to launch any Shell command
For example, see : How to get java getRuntime().exec() to run a command-line program with arguments?

How to do code management for AB Testing

How to do code management for AB testing?
In particular, how to organize the code (baseline and variant) in the project folder?
how to remove code after the experiment?
There is no "one" answer. You can do this in many ways different folders, different classes or do something as simple as if statement in your code.
I think what you are actually asking is how to find all these experiments in the code. A good solution for this is to make the "experiment definition" code with concrete classes so you can easily find it in your IDE and once you delete the experiment definition class your compilation will break and you will easily find all the places in the code that use it.
A good example of this pattern is used by the open source AB test framework called PETRI, which is promoting exactly that. You create an experiment spec (the AB test definition) as a concrete class and use it to conduct the the test.
#RequestMapping(value = "/conductExperimentWithSpecDefinition", method = RequestMethod.GET)
#ResponseBody
public String conductExperimentWithSpecDefinition( #RequestParam("fallback") String fallback) throws ClassNotFoundException {
return laboratory.conductExperiment(TestSpecDefinition.class, fallback, new StringConverter());
}

Why use T4 to modify entities in EF?

What is a valid reason for using the 'ADO.NET EntityObject Generator' with EF? In case you don't know, it generates a T4 file that does the building of the entities from the edmx. You can then change the T4 file to change how the entities are generated.
My question is: other than changing the base class in which entities are derived (also implementing interfaces) and changing the accessibility of entities and/or the context objects and naming conventions, what use does this have? Considering the existing features of EF and partial classes.
I've come up with 2
Grab the table/column descriptions from the database and populate the summary on the entities and properties
Generate light wieght DTO's and do auto mapping.
1 seems like too much work considering it only updates the comments on the models, not the edmx itself (although it could do so)
2 Is this even useful?
We have done a few things with the T4 templates. The first one is to split out the generated code into one class per file, instead of one massive file. This just makes it easier to navigate and browse through the entities.
The second, and more important one, is to automatically generate data annotation validation attributes such as StringLength and Required. This lets us validate our entities with a single function call, and it makes sure that their validation attributes are always in sync with the database (since if we change a column length in the DB, for example, the generated StringLength() attribute is then updated when we do 'Update model from database').
At a previous job, we also added base classes and interfaces to the entities, as you mentioned in your question.
Here's a snippet from our T4 template that checks for required columns and string lengths and adds the necessary validation attributes:
''' <summary>
''' <#=SummaryComment(primitiveProperty)#>
''' </summary><#=LongDescriptionCommentElement(primitiveProperty, 1)#>
<EdmScalarPropertyAttribute(EntityKeyProperty:=<#=code.CreateLiteral(ef.IsKey(primitiveProperty))#>, IsNullable:=<#=code.CreateLiteral(ef.IsNullable(primitiveProperty))#>)>
<DataMemberAttribute()>
<#+
' begin required attribute
If Not ef.IsNullable(primitiveProperty) and not ef.IsKey(primitiveProperty) then
#>
<#+
If ef.ClrType(primitiveProperty.TypeUsage) = GetType(Guid) Then
#>
<GuidRequiredAttribute(ErrorMessage:="<#=FixName(code.Escape(primitiveProperty))#> is required")>
<#+
Else
#>
<RequiredAttribute(ErrorMessage:="<#=FixName(code.Escape(primitiveProperty))#> is required")>
<#+
End If
#>
<#+
End If
If HasMaxLength(primitiveProperty.TypeUsage) then
Dim d = MaxLength(primitiveProperty.TypeUsage)
#>
<StringLengthAttribute(<#=d#>, ErrorMessage:="<#=FixName(code.Escape(primitiveProperty))#> cannot be longer than <#=d#> characters")>
<#+
End If
#>
Well, the reason is that you can do anything with it. So, why don't have it? Of course the usage of T4 is very limited to some special cases, and most of the time it doesn't make sense to use it. On the other hand you can do funky stuff with it. For example you can define some user defined stuff. Let's say you have a Sort column in your table. Then you could define a query function where it automatically sorts every entry if this column exists. Maybe too trivial for an example, but there are tons of other weird architectures where it makes much more sense.
You can also make use of other code generation stuff, without EF. So it's certainly here for completeness and extendability.
T4 template replaces custom tool used in EFv1 to generate code behind file (.designer.cs file with all classes). The main advantage of T4 is that you can change it per project just by modifying text .tt file. In case of custom tool changes were mostly impossible.
With T4 you have complete control over entity classes generated from your EDMX. It is transformation of EDMX into C# or VB.NET files. The default template creates class files with features required by most of developers but if you need anything else you can simply go to your .tt file and add it.
Possibilities for changes are unlimited because EDMX file itself is extensible. On behind EDMX is just XML file and you can include your own custom XML elements in this file (it has some restrictions but it is possible - here is some example for SQL generation in model first approach). Once you have custom elements in EDMX you can use them in T4 template as decision logic for additional features you want to include in generated code. Entity designer is also extensible - you can create custom extensions which will be stored as custom elements in EDMX file. Both EDMX and Entity designer extensions are nicely described in book Entity Framework in Action.
I know this is an old question, but I would have liked to come across this information when I started out.
In our case where we have a win32 Delphi client in our multi-tier solution, I've used the templates (in C#) to generate the DTO classes in .Net, and the win32 counterparts.
This allows us to encapsulate the CRUD functionality on the client using largely auto-generated Delphi code:
procedure Delete;
class function DeleteDto(const _dESPATCHID: integer) : boolean;
class function GetNextID : integer;
class function Get(const _dESPATCHID: integer) : TDtoDESPATCH; overload;
class function Collection(const __filterXml: string): TList<TDtoDESPATCH>;
function Load: boolean; overload;
function Populate(_primaryDict : TDictionary<string, Variant>) : boolean;
function Save : boolean; overload;
Change tracking from the client can also be automated, so each property setter will mark the changed property, to ensure only changed properties are updated.
For example:
procedure TDtoDESPATCH.SetSCT_STATUS(const value : string);
begin
if (self.IsLoaded) and (inherited SCT_STATUS <> value) then
begin
TrackChange('SCT_STATUS');
self.Modified:= True;
end;
inherited SCT_STATUS := value;
end;
On the server side, another template takes care of all the CRUD operations in an auto-generated WCF service that's also exposed as an asmx web service. The Interface, WCF methods and all annotations are generated from the template.
// convert to entity
var _entity = _dto.ToEntity();
if(exists)
{
Global.LogActivity(string.Format("{0} - profile {1}, updating DESPATCH: {2}", racID, profile, _dto.ChangedProperties ));
// Attach the entity to the db
db.DESPATCHes.Attach(_entity);
// Change tracking
ChangeTracking<DESPATCH>(_dto.ModifiedProperties, db, _entity);
}
In a scenario where WIN32 has to be part of the solution, hand coding all this would be a (worse) nightmare.
I've used them quite a bit to generate code that is useful to us at a Datalayer level.
Altering whether they implement additional contracts (interfaces), adding methods, annotations and in the case of a non-layered application (and NOT recommended beyond this) adding property changed notification.
By adding some of these items to the generation at the Datalayer, I've been able to use a template in the Model project to generate the boiler plate code at this level too.
Bottom line, if you can safely get the computer to code for you, then it's well worth while. In our case 80% of basic plumbing work can be code genned - if it's incorrect, it's generally very easy to fix and regenerate.

The best way to handle keys strings

I'm working in a web applications and always i need to use some keys to save some values into object like :
Session["CurrentUser"] = Users.CurrentUser;
I tried to put them into a resource file but it doesn't make sense as i will not need to localize them and the code generated by the resource file looks for the culture every time i get a string from the file.
what is the best way to handle those keys in order to avoid spelling mismatches?
I'm using Visual Studio 2010 and ASP.NET MVC2
You could use constants:
public static class Constants
{
public const string CurrentUserKey = "CurrentUser";
}
and then:
Session[Constants.CurrentUserKey] = Users.CurrentUser;
Also the compiler doesn't care much about spelling or grammar mistakes, as long as the strings match he is OK :-)

CA: Suppress results from generated code not working in VS2010 beta 2

I'm trying to run codeanalysis on an assembly that contains an entity model (edmx file). In project properties I have checked the "Suppress results from generated code" option, but I am still getting a lot of CA errors pertaining to the auto-generated EF code.
Has anyone experienced this? And is there a work-around?
Just put the attribute on your class definition.
But how to do it, since your file can get overridden any time. Use a separate file, since all generated classes are partial classes. Open a separate file, and write something like:
[GeneratedCode("EntityModelCodeGenerator", "4.0.0.0")]
public partial class YourEntitiesContextName : ObjectContext
{
}
This will skip code analysis on your particular generated class. StyleCop for instance is more smart and doesn't touch files that have .designer/.generated part in their name or regions that have generated word in their name.
Well, "Suppress results from generated code" really means "Don't look at types with GeneratedCodeAttribute". EF's code generator hasn't added this, historically (though I've suggested it to the team). But you can add it if you use custom T4.