Orchard Core and Service Fabric - Part 4 - Creating the BooksCatalog Orchard Module

Consuming the BooksCatalog Service

In order to consume our BooksCatalog Service Fabric service from our Orchard Core application, we have a number of options.

  1. We could create a custom middleware straight within the BooksUnlimited.Web project and add it to the middleware pipeline from the Startup class.
  2. We could create a custom Orchard Core module.

Since this blog post is about Orchard Core, you probably guessed that we'll be going with option 2.

Creating the BooksCatalog Custom Orchard Core module

To create an Orchard Core module, we need to do the following things:

  1. Create a new Class Library project stored in the Modules folder of the ASP.NET Core host application (BooksUnlimited.Web in our case). We also need to tell Orchard to look for modules in the Modules folder.
  2. Add a package reference to OrchardCore.Module.Targets.
  3. Create a module manifest file.
  4. Add a project reference from BooksUnlimited.Web to the BooksUnlimited.BooksCatalog module. This is different from Orchard 1, where we didn't have to explicitly add a project reference to our modules.

Additionally, for our application, we'll need to add the following package and project reference:

  1. Add the Microsoft.ServiceFabric.Services.Remoting package. We need this package in order to be able to generate a runtime proxy that implements the IBooksCatalogService interface defined by our BooksUnlimited.Services.BooksCatalog.Abstractions class library.
  2. Add a project reference to BooksUnlimited.Services.BooksCatalog.Abstractions.

All that sounds easy enough. Let's create the Class Library project and call it BooksUnlimited.BooksCatalog, and make sure it's stored in a folder called Modules. However, since this new Modules folder would sit directly in the root of the BooksUnlimited.Web project, this module would automatically become part of the project. This is not what we want, so make sure to exclude that folder. if You forget about it, don't worry; the compiler will remind you with a bunch of errors due to the fact that there are now two AssemblyInfo classes (the additional one coming from the module's project).

When creating the Class Library, make sure to create it as a .NET Core 2.0 project targeting .NET Framework 4.7. However, since we'll be needing the Microsoft.ServiceFabric.Services.Remoting package again, which currently only supports the full .NET Framework, we need to manually edit the generated .csproj file to change the target framework monikor from netcoreapp2.0 to net47. And while we're at it, let's also change the Project Sdk attribute from Microsoft.NET.Sdk to Microsoft.NET.Sdk.Web. This will cause Visual Studio to provide proper tooling for when dealing with Orchsrd modules. For example, when we now try to add a view to a Views folder, Visual Studio will provide a context menu containing a number of View template options.

Next, add the Orchard.AsModule package. This package is kind of like a "marker" package to indicate that our class library is a module. It also does something clever with the build pipeline that I don't recall, but I'll come back here and update this text when I remember. It does explain the appearance of the PAckage.Build.props file that you'll notice.

We also need to add a project reference to the BooksUnlimited.Services.BooksCatalog.Abstractions class library, because we'll be using that interface to communicate with the BooksCatalog service.

Next, add a file called Module.txt to the project's root and add the following lines:

Name: Books Unlimited Catalog
Description: The Books Unlimited Books Catalog module turns your site into a full-blown books catalog.
Features:
BooksUnlimited.BooksCatalog:
Category: Books
Description: Enables access to the BooksCatalog microservice hosted by Service Fabric

If you're an experienced Orchard developer, then you're very familiar with Module manifests too. As you can see, the manifest format hasn't changed with Orchard Core.

At this point, we have a valid Orchard Core module, but it doesn't contain any funtionality yet. Let's change that by adding a Controller and a View. Since Orchard CMS is based on ASP.NET MVC, the OrchardCms middleware will already have provided ASP.NET middleware and routing configuration, so we don't have to do anything about that. Go ahead and create two folders in the module's project: a Controllers and a Views folder. Next, create a HomeController containing an Index action, and an Index view for that action. The resulting structure should look like this:

Now it's time to start writing code that let's us call the IBooksCatalogService via a Service Fabric Remoting. To do this, we will add another class to the root of the module called Startup, which should derive from OrchardCore.Modules.StartupBase. This Startup class allows our module to further configure the middleware pipeline as well as register services with the ASP.NET's service container. We want to register the IBooksCatalogService, and when resolved, we want an instance of a Service Fabric runtime proxy. Here's how that looks:

public class Startup : StartupBase
{
public override void ConfigureServices(IServiceCollection services)
{
services.AddScoped(serviceProvider =>
{
var serviceContext = serviceProvider.GetRequiredService<StatelessServiceContext>();
var applicationName = serviceContext.CodePackageActivationContext.ApplicationName;
return ServiceProxy.Create<IBooksCatalogService>(new Uri($"fabric:/{applicationName}/BooksCatalog"));
});
}
}

The most important line is the one where we call ServiceProxy.Create(). That method will instantiate and return an actual proxy instance that will perform calls on our BooksCatalog service remotely. Notice that in order for this proxy to be able to locate our service, we need to pass in a URI. The URI to use is listed on the Service Fabric Explorer (fabric:/BooksUnlimited/BooksCatalog), but notice that I'm not hardcoding the application name. This is so that we could potentially create more application instances of our BooksUnlimitedType SF application type.

In case you're wondering how ASP.NET Core is able to resolve a StatelessServiceContext from the service provider, have a look at the Web class in the BooksUnlimited.Web project:

As you can see, this class generated by Service Fabric's project template for ASP.NET Core is adding the provided serviceContext object as a singleton to the services collection, which will be available before Startup is called (where ORchard will internally invoke Startup on all enabled modules).

Anway, now that we have our services in places, it's time to use it!

Let's go to our HomeController in the module, and add the follwoing code:

public class HomeController : Controller
{
public HomeController(IBooksCatalogService booksCatalogService)
{
this.booksCatalogService = booksCatalogService;
}

private readonly IBooksCatalogService booksCatalogService;

public async Task<IActionResult> Index()
{
ViewBag.Books = await booksCatalogService.GetBooksAsync();
return View();
}
}

Notice that because we registered IBooksCatalogService with ASP.NET Core's services collection, we can inject an instance of it in our controller's constructor. We use this service from the Index action by calling GetBooksAsync() and storing the results in the ViewBag.

Next, update the Index.cshtml view with the following code:

<h1>Books Catalog</h1>
<ul>
@foreach (var book in ViewBag.Books)
{
<li>
<article>
<header>
<h3>@book.Title</h3>
<span>By: @book.Author</span><br />
<span>Genre: @book.Genre</span>
</header>
</article>
<summary>
@book.Summary
</summary>
</li>
}
</ul>

This will generate a simple <UL> list that renders all of our books that were returned from the BooksCatalog service.

Enabling the module

Now that we have the module in place, it's time to try it out. Start or deploy your application to your local SF cluster, go through setup again (which will be lost when you redeploy) and go to the Orchard Admin (http://localhost:8303/admin in my case).

Once loggedin, go to Design -> Modules, and look for our custom Books Unlimited - Books Catalog feature, and click Enable.

You'll notice that it took less than a second to enable the feature. Compare that to Orchard 1, when enablign a feature could easily take a couple of seconds, and you'll again realize that Orchard Core is a different beast entirely.

Now that our module is enabled, it's time to try it out. Navigate to http://localhost:8303/BooksUnlimited.BooksCatalog/Home/Index (don't forget to use your own port number), et voila, we get a list of books provided by our BooksCatalog service:

Although this page could use some styling, it should be clear by now how easy it is to integrate Orchard Core with Service Fabric services.

Conclusion

With that, we succesfully added an Orchard Core project to Service Fabric!

We created a custom Orchard Core module and got it to talk to a Service Fabric service.

If you're developing a service oriented architecture and are looking for a runtime to host microservices, then Service Fabric could be a great fit.

Orchard Core is showing great promise to becoming the next big thing in the world of open source content management systems. Although it's not even in Beta release at the moment of writing, it's already a great application framework where we can pick and choose the components we want to use.

Leave a comment