Sortable lists, IScopedDistributedCache - This week in Orchard (17/01/2020)

Gábor Domonkos's avatar
Caching, Documentation, This week in Orchard, Role, Recipes

Get ready for a deep dive! In our post, we will introduce the IScopedDitributedCache service and show how to use it in the RoleStore. And we are going to solve the mystery about do you need the Model prefix to access different kinds of properties of a model in Liquid and in Razor too?

Orchard Core updates

Document recipes new Lucene settings

There is new documentation on the updated Lucene index settings. Now to create an index from a recipe, it's not just about passing the name of the analyzer. You have to provide the following:

  • AnalyzerName: the name of the analyzer, that you want to use.
  • IndexLatest: a boolean value that indicates that if you want to index the latest or just the published versions.
  • IndexedContentTypes: the list of types you want to index.

Re-use in some places the ContentItem Liquid property

There was a change in the Liquid TemplateContext, that broke some properties, that are available in the templates. If your AliasPart, AutoroutePart or Workflows patterns don't work anymore, it's because of the previous change. Now, this issue has been fixed.

For example in an Autoroute pattern that used to render the custom URL of the content type you could do something like: {{ ContentItem.DisplayText | slugify }} That will generate a nice text for your routes. But in some cases, you have to write {{ Model.ContentItem.DisplayText | slugify }}.

Maybe using the Model prefix is collateral damage, but maybe it has a purpose. Anytime we have a Liquid template for a shape we do Model.ContentItem to get the ContentItem.

In Razor everything is Model.Something. It has to be because in Razor we are doing C# and the current context is the page and the page is a class that doesn't have our properties like @ContentItem. So the only thing we could get is the @Model that is typed to the generic type of the RazorPage and Model will provide all the properties like the ContentItem.

Because we do too much Razor, it could be possible that we reused this approach into some Liquid patterns. In Liquid it's not necessary to have the Model prefix. But it appears that there is already a way in Fluid to use a first-level model without a custom prefix (so we don't need a Model prefix), by setting the TemplateContext.Model property.

In conclusion: in Liquid that is easier to write {{ ContentItem.DisplayText }}, you don't need the Model prefix. But in Razor, you have to use the Model prefix.

Constraint admin controllers to mapped routes

Since we can change the prefix of the admin route, we have to ensure that every admin controller's actions have the correct prefix to use. There is an implementation of the IActionConstraint, called AdminActionConstraint, that checks if we are in the admin and the route doesn't start with the admin URL prefix (_adminUrlPrefix), then it will create a warning log entry.

So, the AdminActionConstraint applies a convention that restraints all AdminControllers or [Admin] controllers to use the mapped route only, and not the default route applied by MVC.

Fix the recipes not found bug in the documentation

We have a guide about how to create a new decoupled CMS Website using Orchard Core. It told you to add the OrchardCore.Application.Cms.Core.Targets NuGet package to your new ASP.NET Core project, because this package won't contain the themes.

And when you do a decoupled CMS site you will don't need the themes, because you want to create your own front end. The issue is with this package on the master branch is that the setup doesn't contain any recipe in the RC1 version. The community fixed that bug for now, but it's just in the dev branch yet. So, if you follow the guide and you using the RC1 version of that package, when you arrive at the setup screen you will not be able to set up the site because there will be no recipe to choose from. Mitigation is to use the OrchardCore.Application.Cms.Targets package, that contains all the themes and recipes. Of course, in this case, you will get several unwanted themes, but you will not be blocked to continue following the guide. So, it's not the perfect solution, but when there will be a new version of Orchard Core, we can rewrite this back to use the OrchardCore.Application.Cms.Core.Targets NuGet. There is also a hint about if you are using the nightly builds of Orchard Core then you should use the Core package instead.

Creating IScopedDitributedCache

The main goal is to increase the performance of Orchard Core and try to make it faster from time to time. To measure that, check the blog posts Liquid template page and start to remove everything from there. Just to see how fast could be the performance if we didn't render anything. Then start to remove stuff from the controller that displays content. For example, let's take a look at the ItemController. This is the controller, that renders content items by default. Take a closer look at the Display method of this controller!

It first loads the content item. If it's not there, we return a 404. Then we check the ViewContent permission for this content item for the current user. We return Forbid if they authenticated but don't have the proper permission, or return Challange if the user is anonymous.

Now let's build the display! That will build a shape containing all the part shapes and everything for this content item. So, it will render the display of this content item and we send this shape to a view that will just call Display of the shape. That's what we do in this method.

The thing that made it slow actually is that one which checks the permissions, the _authorizationService.AuthorizeAsync. The user is anonymous and checking that the anonymous users could see something was the bottleneck of the performance. For each display of the site, there would be a database query just to authorize the anonymous user to be able to see something. This is actually a regression. Once you remove this code the controller will be much faster.

An authenticated user would have added Claims cached in the User object. But in the case of an anonymous user, nothing was cached. Each request for an anonymous user would ask the permission for the anonymous user from the RoleStore. The RoleStore would do a query all the time.

There is a new service, called ScopedDistributedCache. The idea is to use a distributed cache, such that we would store the Roles document in memory if we have a single node or in a store that is shared across every node if you have multiple nodes. Scoped means if you do multiple queries on the same cache entry for the same request, then we will load it from the DistributedCache only once. That will be much faster if you have multiple queries.

You can see the usage of the ScopedDistributedCache in the RoleStore. Instead of injecting the IMemoryCache (that puts everything in the memory that shared by all the request) here comes the IScopedDitributedCache. The code is much simpler with that: you just pass it an object. Check the GetRolesAsync and the UpdateRolesAsync methods of the RoleStore.


Make lists sortable with ordering setting

Let's say you have a site that you set up using the Blog recipe. In that recipe you can find a Blog content type, that is used as a container of the Blog Post content items. To behave the Blog content type this way, you need to add the ListPart to it, which will add the list behavior. Here you could see a new option before the Contained Content Types list, called Enable Ordering. If you check this option you will enable the manual ordering of the items. Let's put a tick here!

Now head to the Content -> Content Items page and select the one named Blog (or just simply hit Blog from the menu). Here you can see the list of the blog posts contained in this blog content item. Let's add more posts to it to see the power of this new feature! By using a simple drag and drop, you can set the order of the different items and save it on the fly. Check out the following GIF to see how the reordering works!

This feature is under development, but you can find the code in this pull request. And don't forget to watch the recording about this demo, where you can also hear an informative discussion about some interesting questions to solve, for example, what about reordering content items that have a published and a draft version too?

News from the community

New Orchard Core site: Buzz Interactive

Buzz believes that great digital products are built on intelligent strategy and outstanding technical abilities. They partner with vibrant clients and help them create future-proof platforms for web, app, and VR. And they use Orchard Core for their site!

If you are interested in more websites using Orchard and Orchard Core, don't forget to visit Show Orchard. Show Orchard is a website for showing representative Orchard CMS (and now Orchard Core) websites all around the internet. It was started by Ryan Drew Burnett, but since he doesn't work with Orchard anymore, as announced earlier it is now maintained by our team at Lombiq Technologies.

Tell us about your .NET performance challenges! - Hastlayer developer survey

Help us build the nerdiest .NET thing, Hastlayer: It turns performance-critical sections of .NET programs into computer chips! If you fill out our short questionnaire you can win a cool compute accelerator board worth $265! Check it out here:

The reason we're asking this is that we're building a .NET hardware accelerator, Hastlayer ( it turns your program into a chip!) and want to better understand what other developers do. Thank you in advance!

Orchard Dojo Newsletter

Now we have 113 subscribers of the Lombiq's Orchard Dojo Newsletter! We have started this newsletter to inform the community around Orchard with the latest news about the platform. By subscribing to this newsletter, you will get an e-mail whenever a new post published to Orchard Dojo, including This week in Orchard of course.

Do you know of other Orchard enthusiasts who you think would like to read our weekly articles? Tell them to subscribe here!

If you are interested in more news around Orchard and the details of the topics above, don't forget to check out the recording of this week's Orchard meeting!

No Comments

Add a Comment