Just after I'd gone and mentioned that the T4 editor from Clarius Consulting doesn't give you intellisense, Victor Garcia Aprea announces that they're working on an update which does exactly that. Looks really nice too.
Pete
|
|
||||
|
This Month
Month Archive
Categories
Search
|
Thursday, April 17
by
Pete
on Thu 17 Apr 2008 10:18 BST
Just after I'd gone and mentioned that the T4 editor from Clarius Consulting doesn't give you intellisense, Victor Garcia Aprea announces that they're working on an update which does exactly that. Looks really nice too. Pete Wednesday, April 16
by
Pete
on Wed 16 Apr 2008 10:31 BST
Cut to the chaseOK. So you just want to know how to do it? Modify your template directives to the following.<#@ template inherits="Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation" language="C#v3.5" #> What?..Why?Recently I have been doing a lot of work in Microsoft's Domain Specific Language Tools for Visual Studio. In fact for the past 8 months this has pretty much been my main focus. I've learned a lot of things on the way and it occurred to me that the development of T4 templates used in DSL tools would be made a lot easier with the use of extension methods. To understand why, you need to understand the concepts behind DSL tools and the development process involved in creating a model to define your problem domain and templating to create the desired output. One of the problems that becomes apparent in the latter stages of developing a DSL tools implementation is that templating can become quite messy. In DSL tools you create a model of the API which will allow you to interact with the visual elements and this generates code containing the classes you defined in the pattern necessary for these visual elements to be able to use them. After you are happy with the model you move on to generating your output. Typically this takes the form of T4 templates which will generate C# classes. T4 templating, despite the fact that the engine is now built into Visual Studio 2008, does not get any designer support. It is an ASP-like syntax which compiles at template-transform-time into C# class files used to generate the output into files in your solution. Even using editors like Clarius Consulting's T4 Editor does not give you the intellisense you are used to in ordinary C# (well not yet anyway....see here). What tends to happen is that developers make extensions to the model API classes generated by the DSL tools using partial classes to support common scenarios in templating (don't even go down the path of static helper classes.....eek). This seems a good idea at first but after some time your API classes tend to suffer from explosion of methods and properties. These methods and properties are not helpful in defining your domain and do not make sense in Domain Driven Design concepts, they have no place in the ubiquitous language of the domain, they only make sense to the text transformation process employed in your T4 templates. The solution can be to add common base templates which are included in your task specific templates using an ASP-like include directive. An issue with this is you typically create new class structures to encapsulate the functionality you want to perform. This has an obvious overhead in setup and maintenance, also the code in the include template is compiled into the same class as the task specific template which simply adds to the issues. No wonder then that I was interested in the possibility of using Extension Methods to extend the DSL generated API with templating specific methods. After asking the question in the VSX forum I submitted a Microsoft Connect issue and got the answer of the above undocumented feature in a reply email. Basically this undocumented (AFAIK) feature allows you to add "v3.5" to the language definition in a template directive and it can be used, it seems like a bit of an after-thought but it does work. One thing to note is that you can't specify extension method classes inside nested classes so it won't work in template include file, but you could externalise them to another assembly if required. So now you can add a class like the following to your code. 1 namespace PGCodeWorks.Dsl.Test.TestDsl.TemplatingExtensions { 2 public static class ExampleElementExtensions { 3 public static string LowerCaseName(this ExampleElement element) { 4 return element.Name.ToLower(); 5 } 6 } 7 } And from your template code you can do the following. 14 <#= element.LowerCaseName() #> How nice is that? Should make templating a lot easier in VS2008. Pete
by
Pete
on Wed 16 Apr 2008 03:27 BST
I ran across an issue recently trying to get a custom exception across a WCF boundary. The trouble is, WCF does not like to tell you what the problem was, and for good reason. I was thinking about exceptions the wrong way. In reality we do not want to pass exceptions across a WCF service boundary, instead we want to pass a Fault back to the caller. An exception is a CLR concept, it does not make sense to expose this outside of the CLR, despite the fact that an exception contains potentially dangerous information (like the stack trace) which we do not want to expose. If your service does not handle an exception, it will fault. Hence a lot of the time we implement a try-catch handler and raise a FaultException which gets sent across the boundary.
[OperationContract] [FaultContract(typeof(DivideByZeroException))] public void MyServiceMethod() { try {
// Do some actual stuff } catch(DivideByZeroException ex) { throw new FaultException<DivideByZeroException>(ex, new FaultReason("DivisionByZero")); } } Then on your proxy side you will typically recreate this exception and throw it. This approach sometimes has problems when your exception has some extra data in it. Consider the following custom exception type:
public class MyDivideByZeroException : DivideByZeroException, ISerializable {
protected MyDivideByZeroException(SerializationInfo info, StreamingContext context) : base(info, context) { numerator = (int)info.GetValue("numerator", typeof(int)); denominator = (int)info.GetValue("denominator", typeof(int)); }
private int numerator; public int Numerator { get { return numerator; } set{ numerator = value;} }
public int denominator; public int Denominator { get { return denominator; } set { denominator = value; } }
public MyDivideByZeroException(int numerator, int denominator) : base() { this.numerator = numerator; this.denominator = denominator; }
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("numerator", numerator, typeof(int)); info.AddValue("denominator", denominator, typeof(int)); base.GetObjectData(info, context); } } When you try this with the FaultException you may get an exception like the following: In this case you can create a Fault class. Your fault class is simply a Data Contract serializable type containing the information you require to create your exception on the other side. e.g.
[DataContract] public class DivideByZeroFault {
public DivideByZeroFault(MyDivideByZeroException exception) { this.numerator = exception.Numerator; this.denominator = exception.Denominator; }
public MyDivideByZeroException GetException() { return new MyDivideByZeroException(numerator, denominator); }
[DataMember] private int numerator; public int Numerator { get { return numerator; } set { numerator = value; } }
[DataMember] public int denominator; public int Denominator { get { return denominator; } set { denominator = value; } } }
Therefore your service code in the first example will change to be: [OperationContract] [FaultContract(typeof(DivideByZeroFault))] public void MyServiceMethod() { try {
// Do some actual stuff } catch(MyDivideByZeroException ex) { throw new FaultException<DivideByZeroFault>(new DivideByZeroFault(ex), new FaultReason("DivisionByZero")); } }
Pete |
Recent Blog Entries
Recent Comments
Blogs I read
Useful Links
|
||