I tried to add an existing project to a solution that is placed under the source control of GotDotNet Workspaces. This finally results in nearly destroying my solution file, I can't save it under the original name. I am not able to check in or out using the visual studio workspace plug in any more. Opening the solution file again doesn't work. No Fun.
I thought it would be nice to integrate an ASP.NET like template language inside my Greenator-AddIn. Instead of reinventing the wheel, I decided to use the m3rlin.engine Assembly. m3rlin is a XML-centric templating code generator by Joseph Cooney. It generates code by hosting the ASP.NET runtime.
In my last post, I described an issue about creating an appdomain; which is needed to host the asp.net runtime. This problem was solved in signing the assembly and publishing the assembly in the GAC.
Today I examined another really strange behaviour inside of Visual Studio Add-Ins. M3rlin.engine calls HttpRuntime.AspInstallDirectory(), and that call gets me a System.ArgumentNullExeption (Parameter name str). First I thought that I might not be allowed to call it inside of a com interop. I decided to examine this in a separate add in. I generated a new visual studio add in (“AddInTest”) and placed the following code inside the Connect class.:
public
void Exec(string commandName, EnvDTE.vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled)
{
handled = false;
if(executeOption == EnvDTE.vsCommandExecOption.vsCommandExecOptionDoDefault)
{
if(commandName == "AddInTest.Connect.AddInTest")
{
try
{
string x = System.Web.HttpRuntime.AspInstallDirectory;
MessageBox.Show (x);
}
catch (SystemException ex)
{
MessageBox.Show (ex.GetType().ToString()+": "+ ex.Message);
}
handled = true;
return;
}
}
}
I started the project and clicked the „AddInTest“ button and was surprised to get the wanted result: “C:\Windows\Microsoft.Net\Framework\v1.1.4322”. Opening a solution and executing the button again is still ok.
But starting VS-Studio, first opening a solution and then executing the button raises the System.ArgumentNullExeption. Even more confusing: it depends on the opened solution. Some of them will reproduce the error. others will give me the right result. I can't get why.
M3rlin.engine needs the value of HttpRuntime.AspInstallDirectory to set the “hostingInstallDir” of the created appdomain and run the ASP.NET host. I moved the function call inside of the new appdomain (AppDomain.CurrentDomain.SetData(".hostingInstallDir", HttpRuntime.AspInstallDirectory). Now every thing runs fine.
The VisualStudio Add-In "MyTestPlugIn" is installed by default at %Program Files%\MyTestPlugIn. I have to create an AppDomain inside the Add-In and to create an instance of a Worker Class. This will result in an InvalidCastException.
This code is called from the exec command inside the connect class:
try
{
AppDomainTest.Worker Worker = AppDomainTest.Factory.CreateWorker ();
Worker.DoIt();
}
catch (SystemException ex)
{
MessageBox.Show (ex.GetType().ToString()+": "+ ex.Message);
}
The appdomain factory and worker class look like this:
using System;
using System.Runtime.Remoting;
using System.Reflection;
namespace AppDomainTest
{
public class Worker:MarshalByRefObject
{
public Worker(){}
public void DoIt()
{
System.Windows.Forms.MessageBox.Show ("Hello World");
}
}
public class Factory
{
public static Worker CreateWorker()
{
Assembly currentAssembly =Assembly.GetExecutingAssembly();
AppDomain ad = AppDomain.CreateDomain("WorkerTest");
ObjectHandle oh = ad.CreateInstanceFrom
(currentAssembly.Location, typeof(Worker).FullName );
return (Worker) oh.Unwrap(); //InvalidCastException
}
}
}
Note:
The call will work without raising an exception if called from a winform or console. It seems that the add-in assembly can not be found by visual studio to decide if it is the same assembly and type. As a matter of fact copying the add-in assembly inside the Visual Studio\common7\ide directory solves this problem.
But I don't want to place my add-in dlls at two different locations and certainly not under the Visual Studio Path.
Playing around with AppDomain.AppendPrivatePath, added an AppDomainSetup setting the ApplicationBase and PrivateBinPath but this problem remains unsolved.
Greenator V0.1 published at http://workspaces.gotdotnet.com/greenator!
First steps in using Greenator are descriped in a article.
Before talking about application code generation, let’s have a closer look at the generator techniques we are all familiar with; let’s examine html generators.
In the old days, at the beginning of the internet time age, some geeks discovered very fast the power of generators. The first generators were written in C or Perl and got famous under the term CGI.
Later some template language based generators like ASP and PHP appeared. A lot of people started to use them. The ice for html generation was broken.
Later was recognized that it wasn’t really a good idea to mix code and design at one place. That’s one point what ASP.NET is about. Code and Design are now separated into two files. In addition an even higher abstraction level was introduced by using the OO features of the .NET Framework. We are now able to add objects like tables, rows and cells to controls without using a template. Due to this fact it is possible to create the output for different targets like Internet Browsers and mobile devices.
Let’ summarize this in a fact sheet and compare it to active code generation:
|
|
CGI |
ASP/PHP… |
ASP.NET/code behind |
|
name proposal |
script |
template language |
object model |
|
Code looks like |
write ("<table>"); write (" <tr>"); write (" <td>"); write (" "+content); write (" </td>"); write (" </tr>"); write ("</table>"); |
<table> <tr> <td> <%=Content%> </td> </tr> </table> |
Table table = new Table(); TableRow row = new TableRow(); TableCell cell = new TableCell(); cell.Controls.Add (new LiteralControl(content)); row.Cells.Add (cell); table.Rows.Add (row); this.Controls.Add (table); |
|
viable for html creation |
yes |
yes |
yes |
|
ease of maintain source |
easy |
easy |
easy |
|
ease of pasting in sample code |
difficult |
easy |
nearly impossible, aside from controls.add(new literalcontrol(“…samplecode..”)) |
|
presentiment of output |
yes |
yes |
no |
|
abstraction level |
low |
low |
high |
|
other benefits |
total flexibility |
easy to read while developing |
output to multiple targets |
|
application range |
closed application |
template based html generation |
user controls (compiled) |
|
|
Symmetries to application code generation/ using these to generate code instead of html |
|
CGIA |
tier generator |
tier generator |
code munger |
|
KaDGen |
brute force |
not mentioned * |
codeDOM |
|
|
|
|
|
|
primary domain (IMHO) |
wizard |
template based code generation |
on the fly code generation, powerfull in conjunction with parser, code generation controls? |
* Using template languages in general is not mentioned in Code Generation in Microsoft .NET. Kathleen targets only XSLT, which is in fact a template language. But it is totally unfair to compare XSLT with “brute force” and codeDOM. That’s like explaining our modern road traffic like this: On the roads you will find trucks, busses and Porsches. And not telling that there are Ford, Mercedes, Toyota and many other cars too. By the way, I like her book very much. Kathleen has done an excellent work. I am just reading her second chapter, extracting Metadata.
At the end of the last century the corporate homepage of my employer celebrated its fourth birthday. The site was hosted by a provider who didn’t support any dynamic features like CGI. Most of the pages have been static and have been updated once in two month. There was only one exception; a calendar site that should always be actual. All changes have been transmitted to our external web designer. He adjusted the appropriate HTML page and sent it to our provider.
I was asked to simplify that procedure. At this time I have never written a web page before. I knew the basics and started my first web adventure. I examined the current calendar page and guess what, it was a simple html table included in a customized head and footer. I moved the calendar data to an access database and wrote a small VB6 form. The data was edited in a grid and a click on a button produced the html file and uploaded it to our homepage with ftp. The html creating function looked something like that:
Public Function MakeHTML(rs As recordset) as string
Dim html As String
html = html + "<html>"
html = html + " <body>"
html = html + " <table>"
While Not rs.EOF
html = html + " <tr><td>"
html = html + rs!Date + " " + rs!event
html = html + " </td></tr>"
rs.MoveNext
Wend
html = html + " </table>"
html = html + " </body>"
html = html + "</html>"
MakeHTML=html
End function
This calendar has been so popular that a subset of this data should have been published to a second internet domain. This page had a different design. Moreover the design of our home page changed frequently too. I changed my approach. I installed PHP locally and batched it from my application to produce the html pages. I can’t remember the proper PHP syntax, but the templates looked similar to that:
<html>
<body>
<table>
<? php while (not rs->eof): ?>
<tr>
<td>
<?php echo rs->fields(“date”) +” “+ rs->fields(“event”); % >
</td>
</tr>
<?php
rs->MoveNext;
end while; ?>
</table>
</body>
</html>
In my memory the creation of these html files are my really first steps in "code" generation. Ok, the output is not a programming language, just html. We use it daily in our asp.net, asp, php, jsp...pages. Nothing special.
By the way, until now the calendar is still present in internet. The dates are nowadays maintained in a Windows Sharepoint Service list inside our intranet. XSLT-Stylesheets transform the list on the fly to our customers at our home page.
I like XSLT. I use it on several occasions; mostly in asp.net context or sharepoint portal server. I am quit familiar with the XSLT syntax.
Why start with code generation? It is no "hen or egg" question. The code you want to generate has been written several times by you. .Net has powerful libraries; you complemented them with your own. You refactored your code. But you couldn't avoid writing boring code again and again. You see the repeating pattern and starts to extract the metadata.
All times I did this; I felt it naturally to write down this metadata in form of XML. No problem to build up hierarchies and transform the XML so it is a real abstraction of your domain. I invent my own tags and I am every time enthusiastic about the result.
As I mentioned in a post yesterday, my first generator generated ascx User Contols. Because I knew XSLT, it was the self-evident idea to use XSLT to transform XML to a HTML-like syntax.
Some weeks ago I had to wrap a set of web services with an object model. Writing the second class in this project I’ve caught the pattern and developed the abstraction as XML. Now I was looking for a code generator that targets C# and searched the web. First hit has been CodeSmith, I installed it and I was really disappointed. I recognized the well known ASP.NET like way, but I was shocked to see that CodeSmith force me to give up my XML representation and to transform it to a .NET based property set. ( to Eric: I studied “PurchaseOrderXML.cst”, that is not what I dream of, sorry). I can imagine requirements where CodeSmith will be the tool of my choice; if have to extract the Meta data out of a database or other sources.
I remembered the XP wisdom “Do the Simplest Thing that Could Possibly Work". I know XSLT, it works. I know how to implement my generator using IE and notepad. That is simple. It doesn’t handicaps me in representing the metadata in its elemental form. Sole constraint: XSLT is different from C# or VB, so you will have a longer learning curve.
It would be easier (not simpler) to have the generator integrated in my IDE: Visual Studio. That is why I started Greenator. Greenator has yet three different generators built in: XSLT, “Simple” and the possibility to configure an external generator like CodeSmith. In a later post I will show you my favourite, “Simple”.
Today I got the book Code Generation in Microsoft .NET. I was really surprised, I have read the interviews with her at Code Generation Network but I was totally impressed of getting such a huge tome.
In chapter one Kathleen Dollard distinguish between three main types of code generators; "brute force", CodeDOM and XSLT. In a comparison table she evaluates the ease of pasting in sample code: XSLT easy, "brute force" difficult and CodeDOM effectively impossible.
I would agree totally but I think that one technique is totally missing; an ASP.NET like approach like CodeSmith, M3rlin, Cube or MyGeneration. CodeSmith and Cube uses "brute force" as engine behind, Cube loads the ASP.NET engine. Pasting in sample code into the templates is easy; the principle is powerful and familiar to all .NET users.
I prefer the way of XSLT, mainly because you are not "overlaying the syntax of the output code with the code that runs the template". But I will not ignore ASP.LIKE techniques.
I can’t wait to read on.
VS .NET has a built in interface for integrating Code Generation called custom tool. Two Custom Tools (also called Generators) come with VS.NET, MSDataSetGenerator for strongly-typed DataSets, and MSDiscoCodeGenerator for WebService proxy classes.
Once you have set the property custom tool the generator starts and generates a subordinate project item with the generated source. Every time you change and save your base definition file, the source file is generated again.
I like the transparent way how the generation works. In fact, at first sight it is a miracle how the strongly typed dataset is generated from a Schema.
It is possible to write your own custom tools. All you need is to implement one function:
public override byte[] GenerateCode(string file, string contents)
{
string code = "<<generated code>>";
return System.Text.Encoding.ASCII.GetBytes(code);
}
On a second sight, this approach limits the scope for active code generation. Code generation should be under your control, you should e.g. set the templates to be used for the code generation. It is not possible to set a further argument inside the properties of the definition file.
Some code generators that implement custom tool cheat; they add the reference of the template inside the model definition. That is not orthogonal.
Furthermore it is not possible to generate two code files out of one model.
Greenator is written as Visual Studio Add-In and for this reason Greenator is not limited by the design of custom tool. But Greenator supplements custom tools behaviour, it watches all templates and models and generates the code file or even files transparent on the fly every time it must.
I used consciously code generation one year ago. Nearly every day I was asked to implement and publish another contact form at our company’s homepage: Address fields, email address, custom validations…Actual a common task, but none of these forms looked like another, all of them asked for at least one different field. It was a stupid and boring. The approach was simple too: The form definition has been reduced to a xml based model. I used XSLT successful to transform these models to .ascx user controls.
The model files have been maintained by some power users. They did not know much about XML, but they have been able to alter existing model files to their needs. My function was reduced to validate the assigned model, to run the transformation and to publish the output to a web server. The time was reduced from about one hour per form to few minutes. As a side effect oversights diasppeared.
Did you ever have a "Deja vu" in your daily programming live? Do you have too the feeling of repeating again and again similar lines of code? Do you used to copy and paste your source code?
No? You must be lucky. Perhaps you are using a language like ruby, which is dynamic enough to let be do what you want without leaving the language.
Yes? But you worked hard, you have powerfull libraries. You have refactored your code again and again, but this feeling never moved away?
Now its time for Code Generation!