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.

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
}

Error Invoking AppDomain Proxy

I’m having one heck of an AppDomain bug. I have a situation where I am creating an AppDomain and then creating an instance and unwrapping it and invoking a method on that proxy instance. The first time I invoke this instance I get successful results and if I have a breakpoint inside of the proxy it will break on it, however the second time I invoke the method it will fail by yielding an incorrect return value.

Also, strangely enough, if I ever try to step INTO the method call with the debugger visual studio will suddenly shutdown. Examining the error code in the debugger doesn’t really reveal anything valuable either. Here is a link to the code in question:

ESharpCompiler.cs

Upgrading to NBusiness 2.1

I finally spent some time last night updating this blog site. I still have a few things that I want to do but I got some of the big things out of the way. The first big one was upgrading to NBusiness v2.1, actually it was pretty easy to do but I wasn’t sure how it would work out.

For anyone out there running 2.0.1 and thinking about upgrading to 2.1 I’ll try to list some issues you are bound to encounter. The first one is the change from using the static CreateNew() method to using constructors. so if you have code like this:

Blog blog = Blog.CreateNew();

You will need to change it so that it looks like this:

Blog blog = new Blog();

Much better if you ask me! Also the old create new methods will still be generated but it will give you an obsolete error message so you can find and change them easily. You may also have to change references to NBusiness in your website, i had to alter a web.config file that had version information in it. Other than that I had to run the sql stored procedures it created on my new site to get it to work which is fine.

Also there is one big problem I encountered and that is that the scripts will generate sibling relationship tables and scripts and keys at the bottom of “complete.sql” not at the top with the rest of the tables! Which means if you try to run just the scripts be sure to search for ALL of the table drop/create scripts because I accidentally dropped my BlogsTopics tables when upgrading. Which turned out to be ok because I wanted to redo them anyway (I had too many useless topics) but it could have been really bad in a bigger site.

I’ll probably make NBusiness v2.1 available as the primary download on codeplex later today (you can get it right now from the planned release section).

The biggest new feature of NBusiness 2.1 is the ability to use some generated objects to create dynamic sql. So when you go to create custom fetch methods its much faster than the current system. The other features are listed on the download page.

Dynamic SQL for NBusiness

I have been working on adding the ability to do dynamic SQL for NBusiness business objects. The point of this basically to allow a developer to quickly add new Fetch factories. Currently if you want to add a new fetch factory you need to do a few steps.

·         Create the factory method

·         Create the Criteria class

·         Add a handler to the Fetching event

·         Add a case to the fetching handler for your criteria object

·         Then call the Database Provider

·         Create the necessary stored procedure

Since needing to create new Fetch factories is a pretty common task I wanted to add a way to make it easier. NBusiness will generate all of the most common Factories and stored procedures for you (such as factories for fetching all entities or fetching by id’s or relationships) but if you want to be able to fetch by some other column type you’re stuck with creating your custom factories. For example if you wanted to search for all Customers with the first name of “justin” you’d need to create a FetchByFirstName(string name) factory.

So now rather than going through all of the steps above you can simple create your Fetch factory using dynamic SQL and pass that into the Database Provider instead. There is an object model for creating this dynamic SQL and NBusiness default templates will generate for you all of the Table and Column objects corresponding to your entities.

So for example if you have a Customer entity and you want to fetch by first name you might create a factory method that looks like this:

public static TopicCollection FetchByFirstName(string name)

{

    IQuery query = Topic.Queryable.Table.Where(

        Topic.Queryable.Name == “@name”);

       

    return new TopicCollection(

        query,

        new Parameter(“@name”, name));

}

NOTE: exact syntax may change; this is just an example of how I have it now.

Also, when we upgrade to Visual Studio 2008 these objects will be LINQ queryable. Meaning if you query your entities within a LINQ statement it will generate dynamic SQL for you to fetch the data for your entities (yes, like DLINQ).

Different Database providers will be able to implement their own Query classes so that SQL can be generated for different types of databases. For now I will just implement it for SqlServer 2005, in fact I’m probably going to push back the release of the MySql provider stuff just because as far as I know there isn’t really a demand for it at this point and it’s more work than it’s worth given that fact.