Posts tagged modules
Posts tagged modules
3 notes &
One of the powers of Orchard is a gallery filled with useful modules. Just install them and you’re good to go. But if you want to build a high-performance website with Orchard CMS, you’re gonna have to do more than that. The modules are really generic and that could cost you in performance. Also, they’re contributed by the community, and not everyone has the performance focus you might need.
Output Caching module
The output caching module for instance, does a terrific job at outputcaching. If I had built it myself it wouldn’t be as flexible with a cms module where editors can change the cachetime per route and even invalide specific cache entries. But for my application it can be modified to greatly increase the response times of a cached page.
For each page that is outputcached, the OutputCacheFilter computes a CacheKey that’s unique for this specific url and serversettings by joining the url, tenant, culture and themeid.
private string ComputeCacheKey(ActionExecutingContext filterContext)
{
var keyBuilder = new StringBuilder();
keyBuilder.Append("tenant=").Append(_shellSettings.Name).Append(";");
keyBuilder.Append("url=").Append(filterContext.HttpContext.Request.RawUrl.ToLowerInvariant()).Append(";");
foreach (var pair in filterContext.ActionParameters)
keyBuilder.AppendFormat("{0}={1};", pair.Key, pair.Value);
keyBuilder.Append("culture=").Append(_workContext.CurrentCulture).Append(";");
keyBuilder.Append("theme=").Append(_themeManager.GetRequestTheme(filterContext.RequestContext).Id).Append(";");
return keyBuilder.ToString();
}
In my application each tenant has its own culture. So I don’t need both of them in my cachekey. When I take out the line that includes the CurrentCulture, the page response time drops from 45ms to 5ms!
SecureSocketsLayer module
The SecureSocketsLayer also gets executed for each page request (even the outputcached ones!). In it’s filter is the following code:
var settings = _services.WorkContext.CurrentSite.As<SslSettingsPart>();
This does multiple database queries and costs about 40ms. On an outputcached page that’s a lot of unneccesary overhead!. In my specific application I only needed to do one area over SSL. So I put the setting (EnableSSL) in the AppSettings, and created my own filter with a tiny piece of code.
var wishedPort = 80;
if (UseSsl)
{
var area = filterContext.Controller.ControllerContext.RouteData.Values["area"];
if (area != null && area.ToString() == "Viaselect.Checkout")
wishedPort = 443;
}
Conclusion
When you install a module, take a good look at the code! The developer might have made choices differently than you would have made them. And sometimes it’s a really quick fix to tune a module and benefit from it’s extra’s!
11 notes &
When you’re creating multiple features in an Orchard module, it’s important to specify of each class for which feature it’s needed. The Q42.DbTranslations module for instance, contains two features: Q42.DbTranslations and Q42.AdminCultureSelector.
First attempt I only decorated the Handler to be specific to this feature:
[OrchardFeature("Q42.AdminCultureSelector")]
public class AdminCultureSettingsPartHandler : ContentHandler
{
public AdminCultureSettingsPartHandler(IRepository<AdminCultureSettingsPartRecord> repository)
{
Filters.Add(new ActivatingFilter<AdminCultureSettingsPart>("Site"));
Filters.Add(StorageFilter.For(repository));
}
}
I expected the AdminCultureSettingsPart not to be attached to Site, and therefore the whole feature would be disabled. But alas, it didn’t work. So I decorated the Part, the PartRecord and the Driver with OrchardFeature, and that fixed the problems.
But then you’re not there yet. Because when you have multiple features in a module, everything that isn’t specifically decorated with an OrchardFeature, is automatically allways enabled. So everything specific for DbTranslations also needed to be decorated.
Actually, every class except Migrations needs to be decorated, when you have multiple features in one module. In the end I decided to split the two and create separate modules for them :)
2 notes &
Technically it isn’t my first module, but the first to enter the Gallery. And it’s called the Database Translations Module.
Two multilingual projects at Q42 ran into the problem that the editors weren’t able to translate the strings we were implementing in the code. But we were using the nifty T function in Orchard that translates strings.
So the goal was to make the strings editable. But we also made it possible to flush the dictionary cache once you edited strings, so the changes would be visible instantly without affecting performance.
The heart of the application is our own ILocalizedStringManager, which takes over from the DefaultLocalizedStringManager.
[OrchardSuppressDependency("Orchard.Localization.Services.DefaultLocalizedStringManager")] public class LiveLocalizedStringManager : ILocalizedStringManager {
It’s an exact copy of the DefaultLocalizedStringManager, but with one minor change: the LoadTranslationsForCulture function gets its content from the database instead of from *.po files.
private IDictionary<string, string> LoadTranslationsForCulture(string culture, AcquireContext<string> context) { IDictionary<string, string> result = new Dictionary<string, string>(); var translations = _localizationService.GetTranslations(culture); foreach (var t in translations) { var scope = t.Context; var id = t.Key; string scopedKey = (scope + "|" + id).ToLowerInvariant(); if (!result.ContainsKey(scopedKey)) { result.Add(scopedKey, t.Translation); } else { result[scopedKey] = t.Translation; } } return result; }
There’s still a lot of features I want to implement, but I’ve already heard some positive feedback from the Orchard community. If you want to have a further look, check out the module on github.