Spark view engine

If you’re interested in the new Microsoft MVC framework but you’re a little skeptical about going back to a classic ASP Tag Soup rendering system, you should check out the Spark View Engine.

It has a very interesting way of allowing you to write your view with some simple string replacements and also specialized tags that appear to be actual HTML. The general idea is basically the same but visual appeal is quite a bit better.

Here is an example:

<var names=“new [] {‘alpha’, ‘beta’, ‘gamma’}”/>

<for each=“var name in names”>

  <test if=“name == ‘beta'”>

    <p>beta is my favorite.</p>

    <else/>

    <p>${name} is okay too I suppose.

  </test>

</for>

 In this case your “foreach” loop appears to actually be part of the html. The code inside of the each attribute is compiled as C#.  The system it uses is pretty straightforward and easy to understand which gives me a lot of confidence in it already.

I have been having problems getting NVelocity to work properly so I am going to try to use Spark as the default NBusiness code generation tool. We’ll see how intuitive it is to have this pseudo-html markup inside of code but I suspect this tool will work much better for NBusiness.

ESharp Validation Rule Breakthrough

After a late night of hacking I have finally got an end to end transformation of E# validation rules into CSLA code that compiles. I am using the NVelocity code generator I created to do this, here is the example entity I have defined:

using NBusiness.Frameworks.Csla.Templates;

using Csla.Validation.CommonRules;

 

family Test

{

      entity A as EditableRoot

      {

            field auto id int aid;

            field nullable string data;

            field nullable double value;

           

            validate data StringMaxLength { MaxLength : 10 };

      }

}

 

After running this through the compiler here is the code that it generated:

using System;

using Csla;

using Csla.Validation;

using System.Collections.Generic;

 

namespace Test

{

      [Serializable]

      public partial class A : BusinessBase<A>

      {

            #region Properties

            private static PropertyInfo<int> aidProperty = RegisterProperty<int>(

                  typeof(A),

                  new PropertyInfo<int>(“aid”));

            /// <summary>

            /// aid property

            /// </summary>

            public int aid

            {

                  get { return GetProperty<int>(aidProperty); }

            }

            private static PropertyInfo<string> dataProperty = RegisterProperty<string>(

                  typeof(A),

                  new PropertyInfo<string>(“data”));

            /// <summary>

            /// data property

            /// </summary>

            public string data

            {

                  get { return GetProperty<string>(dataProperty); }

                  set { SetProperty<string>(dataProperty, value); }

            }

            private static PropertyInfo<System.Nullable<double>> valueProperty = RegisterProperty<System.Nullable<double>>(

                  typeof(A),

                  new PropertyInfo<System.Nullable<double>>(“value”));

            /// <summary>

            /// value property

            /// </summary>

            public System.Nullable<double> value

            {

                  get { return GetProperty<System.Nullable<double>>(valueProperty); }

                  set { SetProperty<System.Nullable<double>>(valueProperty, value); }

            }

            #endregion

 

            #region Relationships

            #endregion

           

            #region Validation

            protected override void AddBusinessRules()

            {

                  Dictionary<string, object> dataArgs = new Dictionary<string, object>();

                  dataArgs.Add(“MaxLength”, 10);

                  ValidationRules.AddRule(

                        Csla.Validation.CommonRules.StringMaxLength,

                        new DecoratedRuleArgs(dataProperty, dataArgs));

            }

            #endregion

      }

}

(You can see the power of a DSL simply by looking at how many more lines of code it takes to represent the same thing in a lower level language!)

It has taken me a loooong time to get to this point and I think it’s all downhill from here. Trying to find out how to discover validation / access / authorization rules from arbitrary business object frameworks turns out to be a terribly difficult thing to do. There is lots of room for improvement but I think I have the basics down for now. This should allow me to generate code for CSLA and NBusiness frameworks at least and perhaps a couple of others without too much work.

Next up is largely a process of cleaning up code (I have been hacking on things a lot recently), fixing up unit tests and fleshing out CSLA templates for each major stereotype (I love that word in a software context!). What a relief!

NVelocity Code Generation Tool for NBusiness

I’ve really been struggling with how to improve creating templates for NBusiness 3 so that it is much easier than it is now. I have been delayed by the prospect of actually creating any more templates with the CodeDom. It is just too painful and ugly.

On one hand I really want templates to be classes because this allows me to search for them easily, they are either in the same assembly or in a referenced assembly. It also allows me to easily gather meta-data about templates (attributes) and actually run the templates. It also allows for the template developer to generate code however they way, it’s very powerful. There also does not need to be some special path that you store templates in and there does not need to be a relative path specified in the entity. It’s a very light-weight and powerful way to do templates.
Unfortunately the problem with this approach is that your templates are probably going to end up being done with the CodeDom, which as you probably know is a royal pain in the butt. If you try to use a template engine such as NVelocity then you’re stuck taking care of the path issues yourself, which is ok but is also sort of annoying.
Well last night I finally figured out a great solution to this problem. I have decided to include an NBusinessTemplateCodeGenerator Custom Tool to the visual studio integration project. This custom tool will generate for you a template class, based on a template file it is applied to in your solution. Then your entities can reference that template and will use NVelocity to do the generation. I plan on making it extensible somehow so other template tools (such as codesmith?) could be used. Here are some screenshots of a working prototype.
So in this example my simple NVelocity template has the following code:
using System;
 
namespace $entity.Namespace
{
      public partial class $entity.Name
      {
#foreach($field in $entity.Fields)
            public $field.TypeName $field.Name { get; set; }
#end
      }
}
The .nbt (NBusiness Template) file will create a Template class such as this:
[NBusiness.Templates.CodeTemplateAttribute()]
public class TestTemplate : NBusiness.Templates.TemplateBase {
    protected override NBusiness.CodeDom.Compiler.EntityFile Compile(NBusiness.CodeDom.Entity entity, NBusiness.CodeDom.Compiler.EntityCompileParameters parameters) {
        NBusiness.Templates.ICodeGeneratorEngine engine = NBusiness.Templates.CodeGeneratorEngine.GetEngine();
        string template = System.Text.Encoding.UTF8.GetString(System.Convert.FromBase64String(@”IyoNCiAgICBTaW1wbGUgRW50aXR5SW5mbyBDb…you get the idea”));
        byte[] generated = engine.Generate(entity, template, parameters);
        return new NBusiness.CodeDom.Compiler.EntityFile(entity.Name, generated);
    }
}
…Which is ugly, which is why this tool was created. It will convert your template code into a base64 string which will be converted back into code before getting passed into the NVelocity engine and merged with the incoming entity. This class will now be accessible by entities in the same project or referencing projects.
Your entity file might look like this:
using ClassLibrary1;
 
family Test
{
      entity A as TestTemplate
      {
            field auto id int aid;
            field nullable string data;
            field nullable double value;
      }
}
Which when run through the TestTemplate will produce code that looks like this:
using System;
 
namespace Test
{
      public partial class A
      {
            public int aid { get; set; }
            public string data { get; set; }
            public System.Nullable<double> value { get; set; }
      }
}
So all you have to do to get NVelocity working with NBusiness entities and models will be to add a .nbt file to your project which (if done through the Add New Item dialog) will have the Custom Tool NBusinessTemplateCodeGenerator applied to it. Then author away!
I will be using this system to author the core templates that come with NBusiness from now on.

NBusiness 3 Updates

I’ve been working on NBusiness a little bit over the weekend. I’ve decided to create a Curried API using extension methods for the CodeDom to speed along template creation. Here is an example of what I have so far.

CodeMethodReferenceExpression onPropertyChanged =
 new CodeMethodReferenceExpression(null, “OnPropertyChanged”);
 
CodeMemberProperty memberProperty = new CodeMemberProperty();
memberProperty.Name = field.Name.ToPascalCase();
memberProperty.Type = new CodeTypeReference(field.Type);
 
memberProperty.Get()
.Return(memberField.Reference());
memberProperty.Set()
.If(             
    memberField.Reference().NotEqualTo(memberProperty.SetValue()))
.Then(
   memberField.Reference().Assign(
      memberProperty.SetValue()),
      onPropertyChanged.Invoke(memberProperty.Name));
Trust me, as ugly as the above seems it’s still a million times better than doing it all the way it was originally written. Hopefully this will mature and get better over time as well. Here is an example of the code it produces.
private int Aid {
 get {
   return _aid;
}
 set {
   if ((_aid != value)) {
      _aid = value;
      OnPropertyChanged(“Aid”);
    }
}
}
Secondly I have been polishing and simplifying the model framework that comes with NBusiness. I have been working to create an abstract DataAccess API that can easily produce any type of query. Here is an example of how you might insert a new model with the next version of NBusiness.
MockModel model = new MockModel();
model.Name = “testinsert”;
model.Save();
This is as normal and will end up calling into MockModel’s “InsertModel” method which may be implemented like so
protected override void InsertModel(IQuery query)
{
query.Insert().Into(
    MockModel.Database.Table,
    MockModel.Database.Name).Values(“@Name”);
query.Go();
query.Parameter(“@Id”).Direction = ParameterValueDirection.Output;
query.Parameter(“@Name”).Value = Name;
query.Load += loader =>
{
   Id = (int)query.Parameter(“@Id”).Value;
};
}
The DataBase.Table and DataBase.Name members are code generated, static ITable and IColumn fields based on your E# model definition. The query object will be passed up to parent objects and down to child objects and will be passed into an abstract provider which will use it to generate SQL and call your database. The IQuery object actually comes from your IDatabaseProvider and may abstract it to work with different databases. My theory at this point is that you could actually parse the query object anyway you like in the IDatabaseProvider and with a little manual work you could even get it working across a webservice.
Here is my MockDatabaseProvider for my unit tests. The provider interface is much simpler with this new technique.
class MockDatabaseProvider : IDatabaseProvider
{
#region IDatabaseProvider Members
 
 public IQuery Query()
{
    return new SqlQuery();
}
 
 public ITable Table(string name)
{
   return new SqlTable(name);
}
 
 public void Execute(IQuery query)
{
   MethodCall.Calls.Add(new MethodCall(
      “Execute”,
      new { Name = “query”, Value = query }));
 
   SqlQuery sqlQuery = query as SqlQuery;
           
    // Simulate setting the identity parameter after an insert
    IParameterValue id = sqlQuery.Parameters.FirstOrDefault(
      v => v.Name == “@Id”);
   
    if (id!=null && id.Direction == ParameterValueDirection.Output)
    {
      id.Value = 100;
    }
 
    sqlQuery.OnLoad(new MockLoader());
}
 
#endregion
}

Twin Cities Code Camp Spring 2008

I will be giving another talk at the next Twin Cities Code Camp about Visual Studio integration. The event is on April 5th and is free so you should definitely come down and make me sweat with questions that I cannot answer!

http://www.twincitiescodecamp.com/

I plan on talking about visual studio integration in general but I will hopefully show off the current state of the NBusiness 3 plugin as well.