Orchard Dojo Library for Orchard Core

The Orchard Dojo Library is a portable package of Orchard Core goodies. It supplements Orchard Dojo's trainings and tutorials. These are also part of the best practices and guidelines we use at Lombiq.

You can download the whole Library, file issues or fork it from its repository. Also you can download the Library's textual content as one big concatenated document in HTML.

Orchard link collection

Official sites

Blogs

All blogs from the Orchard community are automatically scraped by Orchard Blogs.

Miscellaneous

Software development guidelines

If something’s not specified, general C# guidelines apply: C# Coding Conventions and General Naming Conventions. The talk How To Design A Good API And Why It Matters is an evergreen as well as the .NET Framework Design Guidelines Digest.

Coding best practices

General principles to keep in mind

Topics

C# best practices

When returning a collection, always return an empty collection if there are no elements, but never null. When accepting a collection as a method argument, however, always check for null.

```csharp IEnumerable MyMethod(IEnumerable collection) { // Check for null and handle it somehow if (collection == null) throw new ArgumentNullException("collection");

if (nothingToReturn) return Enumerable.Empty<int>();
else return normally;

} ```

Keep interfaces as short as possible so it’s relatively simple to provide alternative implementation for them (even when doing unit testing).


If a method would serve just as a shortcut for multiple method calls on the same interface, use extension methods. Whether or not to use an extension method should be decided on a case-by case basis as future-aware as possible: only use extension methods if the shortcut is (almost) trivial and add the method to the interface if the optimal solution is more likely to depend on the specific implementation.

```csharp // Good example: the shortcut is simple public interface IService { void Register(int id); }

public static class ServiceExtensions { void Register(this IService service, DbEntity entity) { service.Register(entity.Id); } }

// Extensions are also useful if you want to provide default arguments for methods and want to do it with overloads public interface IService { IEnumerable GetItems(int maxCount); }

public static class ServiceExtensions { IEnumerable GetItems(this IService service) { // This extension provides a default value for the GetItems() method call service.GetItems(15); } }

// Bad example: GetMany() results in many Get() calls. The implementation of GetMany() is something that the implementation of IService is likely to decide on better. public interface IService { object Get(int id); }

public static class ServiceExtensions { IEnumerable GetMany(this IService service, IEnumerable ids) { return ids.Select(id => service.Get(id)); } } ```

For the extension class use the naming convention of [interface name without the leading I]Extensions as above and put them in the same namespace with the interface (so consumers seeing the interface will likely be able to see the extensions without adding another using statement).


Try to keep the maximal number of arguments on a method to 3.


Almost always return an interface type and return the most generic one making sense for the typical consuming code.

```csharp public interface IService { // When in doubt, use IEnumerable<> for collections IEnumerable GetItems();

// If you need List's certain features like mutability or the ability to access items by index commonly in the consuming code return an IList<>
IList<int> GetItemsList();

} ```

Never use view models in a service interface: services and views have nothing to do with each other.


Use the "empty pattern" where you want to provide a default object.

csharp public class MyClass { // Default will return this single instance, initialized with its default constructor private static readonly MyClass _default = new MyClass(); public static MyClass Default { get { return _default; } } }

This is used by .NET's String class (String.Empty) and also by Orchard's QueryHints class (QueryHints.Empty).


When checking if an IEnumerable<T> is empty always use enumerable.Any() instead of enumerable.Count() == 0.


When writing "async void", think twice. Unless written for event handlers async void should be avoided at least because exceptions in such methods can tear down the whole application. See this SO post. If you write such methods always surround it with a try-catch that catches the base Exception so no exception can escape.


If you insists on using short variable names then use ex for exceptions and e for event handler arguments.


When your class implements multiple interfaces with a lot of methods it's best to explicitly implement them. This way it's immediately visible which method corresponds to which interface.


Using initialization methods on your classes like Init() is a sign of bad design most of the time as this requires the user to remember to call it before anything can be done. Consider refactoring the class to require necessary data through the constructor (probably even using a static factory) or by computing initialization data on the first demand, lazily.


Service classes should be stateless, i.e. their methods should give the same output for the same input.


When referencing another project from the same solution always add a project reference, not an assembly reference.

Orchard best practices

Always do part shape-related heavy work in shape factories inside drivers: this way if the shape is not displayed (i.e. not specified in or hidden from Placement.info) no work will be done.

csharp public override IDisplayResult Display(MyPart titlePart, BuildPartDisplayContext context) { return Initialize<MyPartViewModel>(GetDisplayShapeType(context), model => { // This delegate will only run if the shape is actually displayed. // Do heavy work here. }) .Location("Detail", "Content:5"); }


When writing a theme if something is achievable by only CSS, then use only CSS and avoid having shape template overrides with minimal modifications. If you absolutely have to create shape overrides then try to override the most specific shape possible: e.g. if you need to override the markup of blogposts' title then override just BlogPost-Title (the shape responsible for showing the title) and not the whole Content shape.


If a template uses a static resource (stylesheet or script) always include/require it there even if the template is part of a bigger layout where those resources are already referenced. This makes it easier to keep track of dependent resources and is not prone to errors caused by changes outside the specific template.


For improving client-side performance by preventing blocking script loads always include scripts in the foot if they’re not required immediately on page load. Also consider using the async attribute on scripts if the order in which they’re executed is indifferent.


When you have multiple features in a single module always make the sub-features depend on the main feature for clarity. This will prevent confusion if you want to place some common functionality in the main feature. It should also be the requirement anyway: sub-features are in that module because they have something in common with the main feature.


Although not mandatory, it's good practice to route all your admin controller to under /Admin in a similar way how controllers named "AdminController" are routed by default. This makes it easier to set up rules for the admin area if one needs it.


Texts presented to the user should always be in form of LocalizedStrings (aka T[]). When you want to display dynamic data in the string, it should always have its parameters supplied to it. Never concatenate localized strings with other values as this prevents complete localization. E.g. if you want to display the number of elements use a printf-like pattern:

T["Number of elements: {0}", Model.Count]

See the relevant documentation.


It's nice to have a consistent ordering for dependencies in module manifest files. A good way is to begin with third-party features, then list built-in ones (OrchardCore.*), both in alphabetical order.


When doing I/O-bound work, always use async APIs if available (e.g. web requests, file writes). Using async I/O greatly increases the throughput of the server by not blocking threads to wait for I/O completion.


When writing Migrations it's best to consolidate the latest schema in the Create method and only make UpdateFromX() run for existing installations. The Training Demo module has an example of how to do this.


Never do any non-trivial work (i.e. pretty much anything apart from variable assignments) in the constructors of injectable types. The dependency injection framework can instantiate your type any time, as the tree of dependencies can result in hundreds of instantiations happening when a type is resolved. Thus any work done in a constructor can possibly have a negative performance effect in seemingly unrelated cases.

If you want to produce a value for a field that won't change during the lifetime of the object then do this by lazily producing that value when its first accessed (e.g. with Lazy<T>) .


When you want to access a form field from JavaScript that was built with a statically typed Html helper for a view model property (like with Html.HiddenFor()) then never hard-code the field element's ID into your script: such generated IDs can change with the underlying implementation and by changing the editor prefix. Instead, populate such IDs from your templates, e.g. by passing the output of Html.IdFor() to the script.


When creating a new controller action don't forget to set the page title somewhere, best from the main view template of the action. I.e.:

```cshtml

@RenderTitleSegments(T["My Page"])

```

Or if you just want to update the content of the <title> tag directly (like it is necessary on admin pages, where the title is already displayed):

cshtml @{ Title.AddSegment(T["My Page"]); }

Note that generally it's bad practice to set the title from content part shape templates: those are meant to be a fragment of the layout so they shouldn't set the title directly; the title is to be set by a higher level component that actually knows what the whole page is about.


About displaying validation info in templates:

If you want to display the validation errors corresponding to a specific field, which is generally a good practice, then you can display it like this (you can use other elements than span):

html <span asp-validation-for="MyField"></span>

Most of the time it's good practice to also, or instead display a validation summary on the top of the page, but close to the form:

cshtml @Html.ValidationSummary()

Never display a validation summary from a content part editor for the same reason as not to set the page title (see above).


When creating ad-hoc shapes then (unless the shapes are very generic) prefix the shapes' names with the module's name (e.g. My_Company_My_Module_My_Shape). Shape names are global identifiers, so if they're only interesting for your module you have to use an appropriate name.


Remember authorization! When letting the user fetch content items by ID or otherwise in any way remember that a malicious user might try to trick your code into fetching content not intended to be shown. As a rule of thumb you should always authorize the user's access (through the IAuthorizationService service) to a content item object.

Never check the "Own" content permissions (like DeleteOwnContent) directly, just the generic ones (e.g. DeleteContent) as the former ones are handled internally by the latter ones.


When you have no choice but catching the base Exception then use the Exception.IsFatal() extension method to not deal with fatal exceptoins.

JavaScript best practices

Prefix jQuery objects with the dollar sign ($) so they can be distinguished from other objects.

var $header = $("#header");

Instead of using the $ variable directly use a wrapper to inject the jQuery object and only use the dollar sign in the local scope.

// The dollar sign will be used only inside the anonymous function here.
(($) => {
    // The variable $ now refers to jQuery.
})(jQuery);

Add any DOM manipulation code and event handlers inside the document.ready() function to make sure the script does not try to find the elements before the DOM has finished loading. This is recommended by the official jQuery documentation.

// Notice how it's a shorthand for a wrapper for the $ variable (as above) and also a document.ready() at once.
// Use this if you only want to write a quick document.ready().
jQuery(($) => {
    $('.elementClass').on('click', () => { // Click event handler.
        alert('I have been clicked.');
    });
});

Try to avoid adding variables to the global scope. A handy way of exposing globals is to namespace them under jQuery as demonstrated with the following example:

(($) => {
    $.extend(true, {
        myModule: {
            // Such deep nesting is not always necessary, the method could be on this level directly
            myClass: { // More of a "class" than a real class of course
                myMethod() {
                    alert('myMethod called!');
                },
            },
        },
    });

    // You can use the above like this:
    $.myModule.myClass.myMethod();
})(jQuery);

When you want to access resources under a given URL of the current web application (like fetching data from a web API endpoint) never hard-code the URL into yours scripts. URLs can change and may depend on the environment (a trivial example being the usage of ApplicationPath that e.g. could prefix URL's during local development but can be empty in the production environment).

Instead inject such information into your scripts from templates.

CSS best practices

Use a language that eases CSS development and compiles into CSS like LESS or SASS. It's really worth trying! (And there's good tooling support.)


When something is possible to style in a straightforward way without the usage of images by only using CSS (even e.g. by using font icons), then do it from CSS.


Try to avoid HTML markup that serves just to enable some kind of styling.


For HTML classes and IDs use dashed names e.g. this-is-a-class. In Orchard modules you may prefix these with the module name.

Source control best practices

The following advice applies to the Git source control system.

Committing

Branching

Code styling

C# styling

If there length of the parameter list for a method is too long to read conveniently in terms of line length (due to the 3-argument rule this should rarely happen for methods but constructors with dependency injection) break it into multiple lines parameter by parameter.

csharp public class MyClass { public MyClass( IDependency1 dependency1, IDependency2 dependency2, IDependency3 dependency3) { // ... } }


Prefix private fields with an underscore (_).


Have a standard ordering of members depending on their visibility and whether they're instance- or class-level, etc.

Notice the order:

Within this order, fields and properties are sorted first by static vs. non-static, and inside each of these by private, protected, public.

Methods and inner classes are sorted public, protected, private.

```csharp public class MyClass { // Constant fields come before everything else. public const string MyConst = "const";

// Readonly fields should appear before non-readonly fields
private readonly string _myReadonly = "readonly";

// Static fields first
private static string _myStaticField = "field";

// Private fields
private string _myField = "field";

protected string _myProtected = "field";

// Properties next
public int MyProperty { get; set; }

// Then the constructor(s)
public MyClass()
{
}

// Public methods
public void MyMethod()
{
}

// Protected methods
protected void MyProtected()
{
}

// Private methods
private void MyPrivateMethod()
{
}

// Static methods
public static void MyPublicStaticMethod()
{
} 

private static void MyStaticMethod()
{
}

// Inner classes
public class MyInnerClass
{
}

} ```


If an expression is short, omit line breaks when applicable to keep the code compact (as long as readability is not hurt), e.g.:

csharp public class MyClass { private int _myField; public int MyProperty { get { return _myField; } } }

CSS styling

Structure your stylesheet's content logically under titles. Use the following comment formats for different levels of titles:

```css /* First-level title *********************/

// Second-level title // -------------------------

/Third-level title/ ```

Use line breaks to space out blocks of code.

Naming conventions

Rules of thumb for refactoring

Consider refactoring in these cases:

Renaming a project

You should do the following steps to rename an existing .NET project (including an Orchard Core module or theme).

  1. Make a backup or commit to source control before attempting the rename.
  2. Rename the project from inside Visual Studio. This will change the project's name in a lot of manifest files.
  3. Search and replace the project's name in all files of the project or even of the solution (if you project's name is not a unique text be careful). This will rename all namespaces too.
  4. Rename the project's folder (if it has one) to match the project's names. You'll have to re-add the project file under its new location to the solution as well as to other projects' references (if any).

Inline documentation guidelines

Code review guidelines

Doing static code reviews is a great way to improve code quality and share knowledge in a team.

Development environment advice

Some advice on how to set up your development environment for Orchard Core development.

Software to install

Below you can find pieces of software that you should install for the best Orchard Core developer experience. Also check out the official documentation.

Visual Studio tips

Orchard performance optimization guidelines

Orchard performance checklist

When optimizing an Orchard Core site's performance (or just putting it into production) check these points for the most obvious ways for a boost.

Detecting performance bottlenecks

The built-in Mini Profiler module is an easy to use Orchard module for pinpointing (mostly DB-related) bottlenecks quickly, even on a production machine.

Orchard Core training guidelines

The following guidelines serve as a base for Orchard Core trainings and you're welcome to hold your own Orchard Core training using these guidelines.

Training methodologies

For methodologies for various forms of Orchard training see training methodologies.

Prerequisite knowledge of participants

During courses we routinely touch on various technologies and paradigms used by Orchard. The knowledge of these is thus an advantage though not a necessity, and participants will also have a chance to learn about them in practice. Here are some keywords of some technologies and paradigms used in Orchard Core:

Overall what we think is roughly what participants should already know about:

Technical requirements

The following tools are needed for an Orchard training:

Topics

The topics are each divided into individual modules. These modules can, but don't necessarily have dependency on each other.

When conducting a training even before these start with an introduction of yourself, then of the participants to get a general feeling of where everybody is coming from, and what would be interesting for them. Note that the indicated time requirements are more like guidelines on the minimal recommended time. Allocate sufficient time above this for discussions, and in the case of multi-day trainings, for recaps at the beginning of each day.

Training methodologies

Following are methodologies for various forms of Orchard Core trainings.

University course

Class work and examination

Lesson structure

Every lesson begins with a short warm-up task incorporating the topics of the previous lesson.

Lessons generally have repeated cycles of the following form:

  1. Presentation: the course leader explains the current topic (10-15 minutes), if the content is practical (like doing some dashboard work) students follow individually
  2. Group work
    • After hearing the presentation about how to solve a certain problem students are encouraged to try out the new techniques for given tasks in form of a group work. The task either
    • consists of the topic demonstrated before
    • or is something slightly new that can be derived from the demonstration or learned by reading a short documentation. The former one is a good choice if there is enough time, the latter one is efficient if there's only limited time available.
    • Groups of 2-3-4 try to solve a problem while the course leader is helping their work and is available for questions
  3. Evaluation of the group work: discussing common questions and issues

Examination

The course has no special examination, instead students should create and finish and Orchard-based web application project.

Schedule

Additional is the time needed for student presentations (depends on the number of attendees) since the course’s final lesson consists of student presentations and code reviews.

Intensive course

Since intensive courses should be tailored to the participants' needs the following points are just outlines and tips. Time constraint is also a factor that determines how in-depth the training can be, how many demonstrations can be carried out and how big is the part of the API that's only shown.

If the course is online:

Core concepts and basic usage (training topic)

Introduction

Time requirement: 1h 0m

Dependencies: none

Parent topic: Core concepts and basic usage

Basic site management

Time requirement: 2h 0m

Dependencies: none

Parent topic: Core concepts and basic usage

Intermediate content management

Time requirement: 1h 0m

Dependencies: Basic site management

Parent topic: Core concepts and basic usage

Advanced content management

Time requirement: 2h 30m

Dependencies: Intermediate content management

Parent topic: Core concepts and basic usage

Expert content management

Time requirement: 1h 30m

Dependencies: Advanced content management

Parent topic: Core concepts and basic usage

Customization features

Time requirement: 1h 0m

Dependencies: Intermediate content management

Parent topic: Core concepts and basic usage

Basic maintenance

Time requirement: 0h 30m

Dependencies: Basic site management

Parent topic: Core concepts and basic usage

Theme development (training topic)

Getting started with theme development

Time requirement: 2h 30m

Dependencies: none

Parent topic: Theme development

Advanced theme development

Time requirement: 30m

Dependencies: Getting started with theme development

Parent topic: Theme development

Module development and Orchard Core APIs (training topic)

The below topics are the core of what an Orchard developer should know. Other, less important items are listed under Extended Orchard Core APIs.

Getting started with module development

Time requirement: 1h

Dependencies: none

Parent topic: Module development and APIs

Basic techniques in module development

Time requirement: 2h 30m

Dependencies: Getting started with module development

Parent topic: Module development and APIs

Developing custom content

Time requirement: 2h 30m

Dependencies: Basic techniques in module development

Parent topic: Module development and APIs

Intermediate techniques in module development

Time requirement: 3h 0m

Dependencies: Basic techniques in module development

Parent topic: Module development and APIs

Advanced techniques in module development

Time requirement: 2h 30m

Dependencies: Intermediate techniques in module development

Parent topic: Module development and APIs

Automated testing in Orchard Core

Time requirement: 2h 0m

Dependencies: Basic techniques in module development

Parent topic: Module development and APIs

Complementary topics in module development

Time requirement: 2h 0m

Dependencies: Basic techniques in module development

Parent topic: Module development and APIs

Extended Orchard Core APIs (training topic)

The knowledge of the below APIs is not necessary for all developers as these are needed only for more special tasks.

There is no need to go into details about all providers, evaluating a few provider types used in Projector and a Token provider is enough.

Extended Orchard Core APIs I

Time requirement: 1h

Dependencies: Intermediate techniques in module development

Parent topic: Extended APIs

Extended Orchard Core APIs II

Time requirement: 2h 30m

Dependencies: Intermediate techniques in module development

Parent topic: Extended APIs

Extended Orchard Core APIs III

Time requirement: 1h 30m

Dependencies: Intermediate techniques in module development

Parent topic: Extended APIs

Web API (training topic)

Time requirement: 1h 0m

Dependencies: Intermediate techniques in module development

Deployment and optimization (training topic)

Optimization

Time requirement: 0h 30m

Dependencies: Basic techniques in module development

Parent topic: Deployment and optimization

Manual deployment

Time requirement: 1h 45m

Dependencies: Getting started in module development

Parent topic: Deployment and optimization

Continuous integration/deployment and Azure hosting

Time requirement: 1h 30m

Dependencies: Getting started in module development

Parent topic: Deployment and optimization

Team training (training topic)

Time requirement: 1h

Dependencies: none

Development utilities

Contained here are various utilities aiding daily development.

Visual Studio code snippets

You can use these code snippets to quickly generate code in some common scenarios during Orchard Core module and theme development. The snippets follow Orchard naming conventions. Check out this demo video for a quick overview of our snippets.

To effectively use this collection of VS snippets just point the Snippets Manager to where you cloned or downloaded this repository (https://github.com/Lombiq/Orchard-Dojo-Library). To do this go under Tools → Code Snippets Manager → select the C# language → Add and Add the whole folder. For Razor snippets to also work select the HTML Language and do the same. Do note that Razor snippets will only be suggested when you hit Ctrl + space first.

The snippets also automatically add the necessary namespaces.

We've taken care to place the $end$ terminating symbol to a place where one most possibly wants to follow up with coding. Thus if you exit snippets by hitting enter the cursor will be placed where you most likely want to write next.

These snippets are constantly used at Lombiq and updated according to our experiences.

General snippets

Orchard Core snippets

Orchard Core snippets are prefixed with "oc" for distinction.

Orchard Dojo Library contribution guidelines

The Orchard Dojo Library is fully open source and can be edited by anyone. If you find an error or would like to improve it, you're more than welcome; just submit a pull request!

The Library is stored as Markdown-formatted text files in a repository on GitHub. The files can be edited with any text editor, but we recommend Notepad++, or VS Code which offers a live preview for Markdown files.

Orchard Dojo Library for Orchard Core license

Copyright © 2020, Lombiq Technologies Ltd.

All rights reserved.

For more information and requests about licensing please contact us through our website.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.