The ASP.NET Web Pages is a web technology stack built on top of .NET framework. In .NET framework 4, the most impressive new feature is dynamic. Rabbit Framework uses dynamic everywhere, which drastically simplifies the code and testing.

Base on the separation of concerns principle, applications usually follow layered design. There are presentation layer, service layer and data access layer. In ASP.NET MVC, it is suggested to put the validation in a service layer and to implement repository in a data access layer. See this. It seems that ASP.NET MVC calls the data that are pushed into the view as Model. To me the Model is the combination of the service layer and data access layer behind it. The data passed from controller to view are so called Data transfer objects (DTO) as per domain driven design (DDD).

In Rabbit Framework,
  • The new dynamic keyword of C#/.NET 4 is used when referencing classes in other layers.
  • The ExpandoObject class is the type of DTO
  • The DynamicObject class is used to build mock objects

Use dynamic keyword

In a typical layered architecture, classes in presentation layer use classes in service layer. Classes in service layer use classes in data access layer. Whenever a class A uses a class B, A depends on B. A and B are coupled. A won't work without B.

The dependency between a class A and class a B is strong, when A only works with B. The dependency is weak, if A works with an interface, which is implemented by B. The weaker dependency makes A and decoupled.

E.g. This is coupled and strong dependency.
public class Model
{
    public Repository Repository {get; set;}
}

This is decoupled and weak dependency.
public class Model
{
    public IRepository Repository {get; set;}
}
The benefits of decoupling the dependency is that it provides code reusability. We can have different implementation of the interface.
public class SqlRepository : IRepository
{
}

public class JsonRepository : IRepository
{
}
The Service class become reusable to either SQL repository or JSON repository without change. Where in coupled scenario, it is impossible.

The technique to decouple classes is by using the interface. This is well know and widely used in statically typed programming language, such as C++, Java and C# (prior to 4.0). While it weakened the dependency between classes, it also introduced more artifacts to the source code system. There could be a number of interfaces required and need to be managed.

Back to the age of C/C++, you write the functions and classes in .cpp files. You also have to declare the functions and classes in separated .h files. With Java/C#, this is not required. Therefore things get simplified. We can saved our effects on writing the code other than managing the code artifacts .

When C# 4.0 introduced dynamic. The game to fight coupling could change further more. We can get rid of the interface by using dynamic.

E.g. Repository is referenced by dynamic
public class Model
{
    public dynamic Repository {get; set;}
}

The dynamic keyword essentially disables the C# compiler's compile time type checking to make it so called dynamically typed. This does not change the strongly typed nature of C#/.NET. It means if you did not assign correct object, compiler won't complain. But it will cause error at run time.

Without interfaces, we again saved our effects on writing the code other than managing the code artifacts.

The compile time type checking is an advantage of statically typed language. It prevents invalid code. This is a tradeoff. We trade the compile time type check and refactoring with simplicity and extensibility.

To ensure the objects are correctly referenced, unit testing are important. Thankfully, unit test based on dynamic is much simplified.

    [TestMethod]
    public void SaveAs_Should_Validate_New_Id_Exists()
    {
        dynamic data = new ExpandoObject(); //create a DTO
        data.Id = "old-id";
        var repository = new Mock();
        repository.Setup("Exists"new object[] { "new-id" }, true); //tell the model new id exists

        var model = new PageModel();
        model.Repository = repository; //push the mock to model 

        model.SaveAs(data, "new-id"); //trigger the model 

        Assert.IsTrue(model.HasError); //make sure model report error
        repository.Verify(); //make sure reporsitory.Exists is called
    }
The example code above shows that it is easy to push a mock repository to the model. See Unit Test section for more details of mock.

Use ExpandoObject as DTO

Ruby on Rails uses ActiveRecord in Model. ASP.NET MVC uses Entity Framework as repository and the Entity as Model, the M of MVC. Rabbit Framework uses ExpandoObject as DTO.

In Rabbit Framework, controller calls model. Model calls repository. Model is a service façade and unit of work container. Repository provides identity map and in memory cache. The references between each other are dynamic. The DTOs are .NET dynamic objects, the ExpandoObject. This design makes it very flexible, extensible and testable.

Here is an example of how controller passes DTO to view by using ExpandoObject.

In controller,
dynamic model = new ExpandoObject();
model.Message = "hi";
@RenderPage("View", model)
In view,
@Page.Message

This is the same idea as someone probably has tried with ASP.NET MVC.

<%@ Page Title="" Language="C#" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>

ASP.NET MVC has strongly typed view. It also has a ViewData dictionary to pass extra and dynamic data. In MVC 3, the ViewBag property exposes a dynamic object to pass late-bound data from a controller to a view. If the model is dynamic, there is no need to have an extra ViewBag.

More on Dynamic

The Massive project is a good example of using dynamic for persisting data.

The usage of dynamic is not tied to Rabbit Framework. Dynamic applies to all other forms of development.

Last edited Mar 22, 2011 at 3:07 PM by yysun, version 2

Comments

No comments yet.