Extending the Fluent API

Extending the Fluent API

Posted on May 29, 2013 0 Comments

The content you're reading is getting on in years
This post is on the older side and its content may be out of date.
Be sure to visit our blogs homepage for our latest news, updates and information.

Using Sitefinity’s Fluent API is a great way to write readable code while extending the capabilities of a project, and developers can extend the Fluent API as well.

What is the Fluent API?

Sitefinity includes several APIs. Along with the Fluent API, it includes an imperative API and a REST API. Each is useful for particular tasks, but I really enjoy working with fluent APIs. Its declarative style makes code more readable, going from left-to-write in the style of many natural languages.

Many .NET developers are familiar with the fluent interface provided by LINQ to Objects, but all .NET developers should be familiar with a fluent interface contained within the .NET Framework before Eric Evans coined the term in 2005.

public string Standardize(string path)
{
    return path.Trim().Replace(' ', '-').ToLower();
}
Figure 1: Another Fluent Interface

In Figure 1, the Standardize method transforms a string. If one were to pass in the value Ada Lovelace, the return value would be ada-lovelace. The body of the method calls a series of methods beginning with Trim on the path variable, each returning a new string. This is a clear representation of a fluent interface: it utilizes method chaining and the context remains the same in each call.

Sitefinity’s Fluent API is more complex than the fluent interface of the System.String class. Instead of immutable data transformations, it produces side effects (changes in state), and its context is more extensive than can be represented with one class. It uses a progressive interface. Each step guides you to the next.

App.WorkWith().BlogPost().CreateNew().SaveChanges();
Figure 2: Create a new blog entry and save it in Sitefinity Fluent API

Figure 2 demonstrates a very clear path in creating a new, empty blog post then saving the changes. The documentation goes into more detail, but Visual Studio’s IntelliSense will guide you as well. At each step in the chain, Visual Studio shows the next, valid options.

How to Extend It

C# developers can use extension methods to extend any fluent interface. To illustrate this, consider the method operating on string in Figure 1. If I make this an extension method, I have in effect substituted a word for a lengthy phrase in the domain specific language (in this case, the string transformation DSL).

public static class Extensions
{
  public static string Standardize(this string path)
  {
      return path.Trim().Replace(' ', '-').ToLower();
  }
}
…
// example
var url = domain + “/” + path.Standardize();
Figure 3: Defining a transformation or method chain

This technique is useful for removing duplicate method chains found throughout a project. If appropriate names are used, the code becomes more readable. Additionally, the code is easier to test since you can ensure that particular chain is correct.

You may wish to do more than simply create definitions. Andy Jeffery asked the following question in the Sitefinity Devs community on Google+.

With the fluent API I can use the following to get all "live" images into a list..

 App.WorkWith().Images().Where(b => b.Status == ContentLifecycleStatus.Live).Get().ToList();

Now, if I have a Dynamic Content Item (called Contact), how would I use the fluent API to do similar?

The Fluent API does not include this functionality, but a commenter suggested a solution from the imperative API.

DynamicModuleManager.GetManager().GetDataItems(TypeResolutionService.ResolveType("Telerik.Sitefinity.DynamicTypes.Model.your_contact_type..")).Where(c => c.Status == ContentLifecycleStatus.Live)

To create a fluent solution with minimal hassle, I’m going to use the provided imperative code and the base classes used by the Fluent API. I begin with the BasePluralFacade<TCurrentFacade, TSingularFacade, TParentFacade, TDataItem> class. It only requires a BaseFacade for the TSingularFacade generic parameter. However, using the BaseSingularFacade<TCurrentFacade, TParentFacade, TDataItem> class is straightforward, and I won’t need to provide extra functionality for single items.

public class DynamicContentPluralFacade<TParentFacade, TDynamicType> : BasePluralFacade<DynamicContentPluralFacade<TParentFacade, TDynamicType>, DynamicContentSingularFacade<TParentFacade>, TParentFacade, DynamicContent>
    where TParentFacade : BaseFacade
    where TDynamicType : DynamicTypeBase
{
    public DynamicContentPluralFacade(AppSettings settings)
        : base(settings)
    {
    }

    protected override IManager InitializeManager()
    {
        return DynamicModuleManager.GetManager();
    }

    protected override IQueryable<DynamicContent> LoadItems()
    {
        return DynamicModuleManager.GetManager().GetDataItems(typeof(TDynamicType));
    }
}

public class DynamicContentSingularFacade<TParentFacade> : BaseSingularFacade<DynamicContentSingularFacade<TParentFacade>, TParentFacade, DynamicContent>
    where TParentFacade : BaseFacade
{
    public DynamicContentSingularFacade(AppSettings settings)
        : base(settings)
    {
    }

    protected override IManager InitializeManager()
    {
        return DynamicModuleManager.GetManager();
    }
}
Figure 4: DynamicContent facades

Plugging these facades into the Fluent API requires a simple extension method for the FluentSitefinity class. I chose not to extend FluentDynamicData since it hides AppSettings.

public static class SitefinityExtensions
{
    public static DynamicContentPluralFacade<BaseFacade, TDynamicType> DynamicContent<TDynamicType>(this FluentSitefinity fluent)
        where TDynamicType : DynamicTypeBase
    {
        return new DynamicContentPluralFacade<BaseFacade, TDynamicType>(fluent.AppSettings);
    }       
}
Figure 5: Extension method for FluentSitefinity

That’s it. Since I’m using the base façade classes, everything else falls into place. Andy can now use the Fluent API to access dynamic content.

App.WorkWith().DynamicContent<Contact>()
              .Where(b => b.Status == ContentLifecycleStatus.Live)
              .Get()
              .ToList();
Figure 6: Using the Fluent API for dynamic content

Conclusion

The solution to extending any fluent API in C# is extension methods. Extensive fluent APIs, such as the one found in Sitefinity, may require some research in its interfaces and classes to achieve an optimal result. It isn’t necessary to follow the original API exactly, but consistency is important. You’re adding onto a language, and the accent isn’t as important as being understood.

Update: I created the project using a type I created through code rather than the module builder. I updated the code to support passing in the fully qualified string name and posted it to a new GitHub project, Sitefinity and Beyond.

Chris Eargle

View all posts from Chris Eargle on the Progress blog. Connect with us about all things application development and deployment, data integration and digital business.

Comments

Comments are disabled in preview mode.
Topics

Sitefinity Training and Certification Now Available.

Let our experts teach you how to use Sitefinity's best-in-class features to deliver compelling digital experiences.

Learn More
Latest Stories
in Your Inbox

Subscribe to get all the news, info and tutorials you need to build better business apps and sites

Loading animation