Project Description

Fluently provides a small, generic set of base classes to easily create a powerful Fluent API around *any* .NET type.

What's the idea?

When writing your class library and API, it will contain one or more classes with properties that can be set.
In order for the user of your code to be able to set the properties in a fluent manner, you would write a wrapper class that encapsulates your class.

Fluently provides a generic, abstract base class that serves as the base for such a wrapper.

The idea is that you, the author of your class library, will create a wrapper class per component class.

Update: it is no longer required to write a custom component builder! There is a new class named DefaultComponentBuilder that enables us to wrap any object with a fluent interface.
For example:

// The new DefaultComponentBuilder class allows API developers to just write their core components without having to write an additional ComponentBuilder implementation.

   // Your domain object
   public class SomeComponent
   {
      public string Name { get; set; }
      public int Age { get; set; }
      public IList<Project> Projects { get; private set; }
      public IList<Hobby> Hobbies { get; private set; }
   }

   // Wrap an instance of your domain object with the DefaultComponentBuilder, allowing users of your domain object to fluently manipulate your domain object
   var someComponent = new DefaultComponentBuilder<SomeComponent>();

   // User code interacting with your domain object
   someComponent
      .Set(x => x.Name = "MyName")
      .Set(x => x.Age = 30)
      .Add(x => x.Projects, new Project())
      .Add(x => x.Hobbies, new[] {  new Hobby(), new Hobby(), new Hobby() });


It's perhaps not as tight as using a custom written component builder, but it enables us to wrap any object around a fluent interface.
As an alternative to deriving from ComponentBuilder<TComponent, TBuilder> you could instead attach extension methods to DefaultComponentBuilder<T> to extend your DSL:

      public static SomeComponentBuilderExtensions
      {
         public static DefaultComponentBuilder<SomeComponent> Add(this DefaultComponentBuilder<SomeComponent> builder, Project project)
         {
            return builder.Add(x => x.Projects, project);
         }

         // Or this:
         public static DefaultComponentBuilder<SomeComponent> Add(this DefaultComponentBuilder<SomeComponent> builder, Action<DefaultBuilder<Project>> projectBuilder)
         {
            return builder.Add(x => x.Projects, projectBuilder);
         }
      }


Users would then be able to talk to your domain object like this:

    var someComponent = new DefaultComponentBuilder<SomeComponent>(); // typically provided by you, the author of the API

    someComponent
      .Add(new Project())
      .Add(project => project.Set(x => x.Name = "Another project");



To ease the writing of wrapper classes, Fluently provides you with an abstract base class, ComponentBuilder<TComponent, TBuilder>.
TComponent is the type of your component, and TBuilder is the type of your own builder that derives from ComponentBuilder<TComponent, TBuilder>.

Example

Let's supppose that we are writing a component called NavigationItem that looks like this:

   public class NavigationItem
   {
      public string Text { get; set; }
      public string Url { get; set; }
      public IList<NavigationItem> Items {get; private set; }

      public NavigationItem()
      {
         Items = new List<NavigationItem>();
      }
   }


Now we want a fluent wrapper around this component:

   public class NavigationItemBuilder : ComponentBuilder<Navigation, NavigationItemBuilder>
   {
      public NavigationItemBuilder Text (string value)
      {
         return SetProperty (component => component.Text = value);
      }

      public NavigationItemBuilder Url (string value)
      {
         return SetProperty (component => component.Url = value);
      }

      public NavigationItemBuilder AddItem (Action<NavigationItemBuilder> itemSetup)
      {
         return AddItem (component => component.Items, CreateAndExecuteBuilder (itemSetup).Component);
      }
   }


As you can see, the ComponentBuilder base class provides some handy methods to allow you to set a property of your component and return the instance of the builder in one line using lambda expressions.
To use the fluent API interface you provide the user with an instance of the NavigationItemBuilder, so that the user can use it as follows:

   navigationItemBuilder.Text("Click Me").Url("http://bing.com");


The ComponentBuilder base class currently provides a SetProperty and an AddItem method, both allowing the deriving class to set a property or add an item to a list of the component in one line.
The ComponentBuilder class also supports creating builders of a different type, which is useful when your component class contains other complex typed properties, for which you want to provide the user with a fluent interface as well.
For example, let's say that we are building a Menu UI component, which looks like this:

   public class Menu
   {
      public string Caption { get; set; }
      public IList<NavigationItem> Items { get; private set; }

      public Menu ()
      {
         Items = new List<NavigationItem>();
      }
   }


Now we want the users of this class to be able to fluently setup its properties as well as add items to the menu and configure them.
The MenuBuilder looks like this:

   public class MenuBuilder : ComponentBuilder<Menu, MenuBuilder>
   {
      public MenuBuilder Caption (string value)
      {
         return SetProperty (x => x.Caption = value);
      }

      public MenuBuilder AddItem (Action<NavigationItemBuilder> itemSetup)
      {
         return AddITem (menu => menu.Items, CreateAndExecuteBuilder (itemSetup).Component);
      }

   }


A user of the Menu and NavigationItem components and builders can now fluently construct a Menu like this:

   var menu = new MenuBuilder(); // Your API should provide an instance of the builder, but for this example we will instantiate it ourselves
   
   menu.Caption("Main Menu")
      .AddItem( item => item.Text ("Home").Url ("/home"))
      .AddItem( item => item.Text ("About Us")
                                        .Url ("/home/aboutus")
                                        .AddItem ( subItem => subItem
                                           .Text ( "History" )
                                           .Url ("/home/aboutus/history"))
      );


The menu (which is of type MenuBuilder) will contain a property named Component, which is of type Menu.
The Menu object will have all of its properties set and items added, and can be used by the user of your API.

ASP.NET MVC Fluent ViewComponents

This library is used in the Skywalker.Web.Mvc.ViewComponents library, which adds a second layer of base classes based on the ComponentBuilder specific for MVC ViewComponents.
Check it out on http://mvcviewcomponents.codeplex.com for more information.

Last edited Aug 31, 2011 at 1:12 AM by sfmskywalker, version 11