SOLID SharePoint apps with MVC

SOLID SharePoint apps with MVC

Practical code examples of ASP.NET MVC applications that connect to a SharePoint Server and comply with the SOLID principles.

 

Be SOLID

The principles of SOLID are guidelines that can be applied to software development to improve legibility and maintainability of software applications. In this article I’ll explores a few best practices and design patterns for developing ASP.NET MVC applications that connect to a SharePoint Server and comply with the five SOLID principles:

·         Single responsibility

·         Open/close

·         Liskov substitution

·         Interface segregation

·         Dependency injection

We also try to understand how to abstract the dependency between MVC and SharePoint, the advantages in general that writing SOLID code brings, and how software development can be really agile and adaptive to changes with minimal effort.

 

Single Responsibility

Have you ever checked in a cheap hotel and found something like the hair dryer in the picture below?

A tightly-coupled hair dryer

 

You may think that the hotel manager doesn’t trust you… and you may be right! For some reasons, hotel management considers more effective tying the hair dryer to the wall and so prevent risk of stealing it, than the cost of replacement in case it burns down and they have to call an electrician to do the wiring, besides leaving the customer without the service for some time.

This is what I call a tightly-coupled service: the hair dryer is physically attached to the power outlet on the wall and cannot be detached. The power outlet provides one service only: power to the hair dryer. The hair dryer itself cannot be considered separately, and the whole appliance has to be considered as a single unit, as it provides one final service only: drying your hair. But its reusability is nil.

In the software engineering practice, this is equivalent to writing large classes of objects that perform several operations, often with strong dependency on other types. For example, assuming that we are writing an application for registration of students to a University course, we may be tempted to define our “big course” and the enrolment process in a single class:

public class BigCourse

{

    public List<Student> RegisteredStudents { get; set; }

 

    public List<Student> WaitlistedStudents { get; set; }

 

    public int Capacity { get; set; }

 

    public bool EnrolStudent(Student student)

    {

        if (RegisteredStudents.Count >= Capacity)

        {

            WaitlistedStudents.Add(student);

            return false;

        }

 

        if (RegisteredStudents.Contains(student))

        {

            return false;

        }

 

        RegisteredStudents.Add(student);

        return true;

    }

}

BigCourse class

 

As you notice from the class definition in the snippet of code, the BigCourse class defines two collections of Student objects for registered attendees and students in waitlist; it also defines the capacity of the course (max number of student accepted); and a method for enrolment of the student, which checks whether the capacity is exceeded or the student is already registered to the course, before actually adding him/her to the list of registered students. If the capacity is exceeded, the student is added to the waitlist.

In its simplicity, this class is effective and gets to the point, given our current requirements. However, as our business model extends, we immediately encounter some limitations: what if our University wishes to offer online courses, in addition to traditional classroom-based ones? What if there is no waitlist for online courses and students are immediately confirmed? With any change happening in our application, the BigCourse class will be immediately affected, growing into an intricate sequence of if conditions, harder and harder to read and maintain.

The principle of Single Responsibility teaches us to keep our objects as simple and nimble as possible. Irrespective on what design methodology you go for, whether DDD (Domain Driven Design), TDD (Test Driven Design), UXDD (User Experience Driven Design), etc., limiting the scope of classes in your application increases readability and maintainability of your software. Our application for course registration will probably benefit of a separation of concerns into different classes as in the following diagram:

Class diagram of the University Course registration application

 

Wow, even 8 types to replace my one big fat course? (No pun intended with the Greeks). Yes, and I limited myself J. And each of them serves a precise SOLID purpose:

·         CourseTemplate is the base class for abstracting some common information; this will come useful for introducing different types of courses without changing the way they are used – more of this when talking about the Liskov Substitution principle.

·         InstructorLedCourse and OnlineCourse are concrete implementation of the abstract class; they can be used interchangeably, providing different business logic during registration; this process does not need to be changed in its implementation, but it is still specific to the type of course: Close to change, Open to extension.

·         IRegistrationService is the contract that the relevant services for registration of a student to a course need to implement, in line with the principle of Interface Segregation. When introducing the MVC application in the context of SharePoint, the concrete implementation of this interface will be bound together at runtime in the object composition root (that is, the point of execution of the MVC application where dependencies are injected using a DI – Dependency Injection – container).

·         Student and Registration are additional classes to slim the BigCourse down and keep responsibilities separated: one Single Responsibility for each of them, that is describing the entity.

 

The source code for all these types is in the project attached to this article.

 

Open / Close

Open to extension, close to change. As simple as that. Simply put, it means that you should not be forced to make changes to your classes every time you want to add capability to your application, but rather extend its functionality by adding “something” around / instead of the base class. Options are:

·         Class extensions: Methods added to an existing class by referencing it in a separate static class by special use of the “this” keyword (in C#).

·         Programming by contract (interface) and binding the interface to a concrete implementation (dependency injection), then replace the existing object with a new one with the new functionality.

·         Implement the Composite design pattern for combining multiple functionalities into a single object (smells of violation of the Single Responsibility principle, but each object is still independent, you are just composing their functionality into a single unit of work for ease of use).

·         Implement the Decorator design pattern for adding functionality around the base object. Typically used in AOP (Aspect Oriented Programming) to add execution of cross-cutting concerns before and after each method. Some frameworks facilitate this by intercepting the call to the base method and decorating it with the defined aspects.

 

Let’s see the implementation of the Composite and Decorator design patterns in our University application.

 

Composite Pattern

In respect of the Single Responsibility principle, we have separated the functionality of registration and waitlist management, and have defined the following two interfaces for each service:

public interface IRegistrationService

{

    bool Enrol(Registration registration);

}

Interface for the Registration service

 

public interface IWaitlistService

{

    void Add(Registration registration);

}

Interface for the Waitlist service

 

We want to combine both functionalities into a composite object, for ease of execution (rule of thumb: use this pattern with moderation, you may end up in a tangled code easily).

public class CompositeRegistration : IRegistrationService, IWaitlistService

{

    protected IRegistrationService registrationService;

    protected IWaitlistService waitlistService;

 

    public CompositeRegistration(

        IRegistrationService registrationService, IWaitlistService waitlistService)

    {

        this.registrationService = registrationService;

        this.waitlistService = waitlistService;

    }

 

    // Implement IRegistrationService

    public bool Enrol(Registration registration)

    {

        bool result = this.registrationService.Enrol(registration);

 

        if (registration.Status == RegistrationStatus.Waitlisted)

        {

            Add(registration);

        }

 

        return result;

    }

       

    // Implement IWaitlistService

    public void Add(Registration registration)

    {

        this.waitlistService.Add(registration);

    }

}

The composite Registration class

 

The CompositeRegistration class implements both IRegistrationService and IWaitlistService interfaces, and their dependency is injected in the constructor. The implementation of each interface simply invokes the pertinent method in the injected concrete implementation of the interface, keeping responsibilities separate.

 

Decorator Pattern

The Decorator design pattern is used to add functionality around an existing object. In our case, we want to check that the person registering a student to a course is authorised to do so, and, upon successful enrolment, notify the student. Security check and notification are two typical cross-cutting concerns that find their place in AOP.

public class RegistrationDecorator

{

    protected IIdentity identity;

    protected IRegistrationService registrationService;

    protected INotificationService notificationService;

 

    public RegistrationDecorator(IIdentity identity,

        IRegistrationService registrationService,

        INotificationService notificationService)

    {

        this.identity = identity;

        this.registrationService = registrationService;

        this.notificationService = notificationService;

    }

 

    public bool Enrol(Registration registration)

    {

        // Security check: Only Instructors can enrol students

        if (!this.identity.IsInRole("Instructor"))

        {

            return false;

        }

 

        bool enrolled = this.registrationService.Enrol(registration);

 

        // Notify student

        if (enrolled)

        {

            this.notificationService.Notify(registration);

        }

 

        return enrolled;

    }

}

The Registration decorator class

 

The Enrol method has now been “decorated” with additional functionality for security check and notification. In between these two events, the usual call to the Enrol method of the original registration service remains untouched.

 

Liskov Substitution

Asking for explaining the SOLID principles is a typical question during interviews for a software engineering position. And when it comes to the L, I see most candidates struggling even with the pronunciation of the name of the American scientist and professor Barbara Liskov, not least attempting into an elucidation of her substitution principle. I won’t attempt to embarrass myself in public too, and the best way I know to clarify what the Turing award winner had in mind is reverting to my hair dryer example once more.

If you remember, we said that the hair dryer in the cheap hotel is tightly coupled to the power outlet: the two systems provide one limited service, despite being designed for different purposes. Now, what happens if we… unplug the cord? No, I am not asking to cut the cord with scissors, that would obviously make the unit unusable. I am suggesting, instead, that we recommend hotel management to supply hotel rooms with detachable hair dryers. As a result of this “independence”, the power outlet is now free to accept other electrical appliances. Hey, I said “electrical appliance”! That’s identifying a class of common objects that has something in common: they all need electricity to operate. And guess who can provide electricity? Yup, our power outlet, which now has a new friend, a hair clipper! And surprise, the power outlet still works (that is, it still supplies electricity, just used by another device). What has just happened? We have replaced a type of devices (the hair dryer) with another type that has a similar dependency (on electricity), and both still work as expected.

That’s exactly the Liskov substitution principle: similar objects (better: objects deriving from the same definition, or implementing the same contract), can be replaced without causing interruption of service on either end of the connection.

 

The Liskov Substitution principle is the foundation of object segregation and inheritance in Object Oriented Programming

 

Interface Segregation

So, in the previous section we learned that two types of objects that have something in common can be swapped without breaking the system. What makes them having that “something in common”? Class inheritance is one approach; the other one is implementing the same interface. The hair dryer and the hair clipper has to respect a “contract” to be able to work with the provided power outlet: they need electricity at the defined frequency and voltage. And they also need to provide a similar “male” plug to fit into the wall outlet (but there are workarounds to that, as we will see shortly when describing the Adapter design pattern). That’s defining the contract of work among electric appliances. That’s what interfaces, in software, are for.

 

Adapter Pattern

The third design pattern that I want to present in this article is the Adapter design pattern. And it does exactly what it says, it “adapts” (fits) one object into another one. British and Americans, two people separated by the same language and different units of measure, power voltage and love for barbeque can now rejoice: we have the solution!

Our University has decided to offer Saturday events to its students, and the application should reflect this, by adding the possibility to register a student. As these events are organised by a third-party institution called “Saturday Inc.”, we are given a library with close source code. This library contains the SaturdayEvent class, that, in violation of the Single Responsibility principle (we didn’t develop it ourselves, after all, right?) implements definition of the Saturday event itself and the registration process.

namespace SaturdayInc.Models

{

    public class SaturdayEvent

    {

        public Location City { get; set; }

 

        public DateTime EventDate { get; set; }

 

        public IList<Sponsor> Sponsors { get; set; }

 

        public bool Register(Student attendee);

    }

}

The third-party SaturdayEvent class

 

The registration adapter will then add a method to enrol students to a Saturday event, in a similar way as it currently happens for regular courses. The existing functionality remains untouched, and support for a new object type has been introduced.

public class RegistrationAdapter : IRegistrationService

{

    protected IRegistrationService registrationService;

 

    public RegistrationAdapter(IRegistrationService registrationService)

    {

        this.registrationService = registrationService;

    }

 

    // Implement IRegistrationService

    public bool Enrol(Registration registration)

    {

        return this.registrationService.Enrol(registration);

    }

 

    // Adapt the Enrol method to the external SaturdayEvent object

    public bool Enrol(Registration registration, SaturdayEvent saturdayEvent)

    {

        return saturdayEvent.Register(attendee: registration.Student);

    }

}

The Registration adapter class

 

Dependency Injection

Entire books have been written about the principle of Inversion of Control and its implementation via the Dependency Injection design pattern. Modern software engineering cannot escape adopting a plausible technique for avoiding object dependency and providing a centralised location for object composition. This can be done “manually” or by adopting a proper DI container, there are no excuses to hard code your concrete objects in your classes!

 

DI Containers

A few .NET frameworks that help implementing the Dependency Injection design pattern in your applications:

NINJECT

ninject.org

CASTLE WINDSOR

castleproject.org

STRUCTUREMAP

structuremap.github.io

SPRING.NET

springframework.net

AUTOFAC

autofac.org

UNITY (formerly Microsoft P&P)

unity.codeplex.com

 

I would love to provide examples of utilisation for each of them, but principles of use and syntax are very similar, so I’ll stick to Ninject for the sake of our University application. Time to introduce MVC and SharePoint in the mix!

 

Go MVC

Disclaimer: This is not built on .NET Core, which provides a native DI mechanism built in the .NET framework. We have a dependency on the SharePoint Client-side Object Model (CSOM), so, at least at time of writing, we would need to stick to MVC 5 and .NET 4.x

In our solution, we want to connect our MVC application to a SharePoint Server backend. We use the C# Object Model of SharePoint available in the Microsoft.SharePoint.Client package, easily installable from NuGet.

Microsoft SharePoint Client Object Model package in NuGet

 

The SharePoint CSOM exposes a Client Context that allows connection to a SharePoint Server from the MVC application. As we have wisely learned in the chapters before, we are not hard-coding the dependency to SharePoint in our controllers. Rather, we will use Ninject to define the dependency in the object composition root of the application, by binding a context interface to its concrete implementation. For immediate testing purposes and for future maintainability (say we want to replace SharePoint with a different backend system), all we need to modify is the binding only, with no further changes to our controllers in the MVC application.

The MVC application and its injected dependency to SharePoint

 

Controller

Models are mapped after entities (list items) in SharePoint and Views are simply displaying those models in HTML, so won’t be spending too much time here. What really matters in the context of our SOLID conversation are controllers. A controller is where services are consumed and business logic executed. Yes, we could introduce an additional layer of abstraction and separation between controllers and services, but that’s purely academic for the purpose.

Let’s make an example of a Course action in the Home controller. This action is accessible via the route /course/{id} and displays the course detail page for the course identified by the “id” parameter.

public class HomeController : Controller

{

    public ActionResult Course(int id)

    {

        var course = this.context.Find<InstructorLedCourse>(id);

        if (course == null)

        {

            return HttpNotFound();

        }

 

        course.ContentItems = this.context.Get<Content>()

            .Where(c => c.CourseId == course.Id)

            .OrderBy(c => c.Name)

            .ToList();

 

        return View(course);

    }

The Course action in the Home controller

 

A few things worth noticing here:

·         There is a reference to a local context that exposes the method Find for retrieving an instance of an InstructorLedCourse class.

·         context is a reference to the SharePoint Client Context, and it is built with Ninject (more in a moment…)

·         InstructorLedCourse is a concrete class as extension of the base CourseTemplate; it can be replaced with another class of the same base type for returning different types of courses without breaking functionality

 

SharePoint Context

Access to SharePoint is provided by the ClientContext class in the Microsoft.SharePoint.Client namespace. This is encapsulated into a SharePointContext class that implements the IRepositoryContext interface. For more information about using the SharePoint ClientContext, please refer to its MSDN documentation. And in the context of MVC, to my article Building an MVC application for SharePoint.

public class SharePointContext : IRepositoryContext, IDisposable

{

    public SharePointContext(string sharePointSiteUrl)

    {

        Context = new ClientContext(sharePointSiteUrl);

    }

 

    public ClientContext Context { get; protected set; }

SharePoint context wrapper around the CSOM ClientContext

 

We are programming by contract, we are decorating our strong dependency (the SharePoint client context), and soon we will bind the interface to a concrete implementation. You see, the long conversation before start making sense now…

What I surely want to avoid is hard-coding a reference to SharePointContext in my controller, such as something like:

private SharePointContext context = new SharePointContext("<URL>");

 

This is creating a tight coupling between HomeController and SharePointContext. The MVC application must have an active connection to SharePoint to work properly. I am not able to mock the context for testing purposes as it is declared private inside the controller class and strongly typed to a concrete class.

If you read all the books about Inversion of Control, you will know the answer already: dependency should be abstracted to an interface and injected from outside. Hence the meaning of “inversion”: it’s not the object using the dependency to create an instance internally, but the concrete object being injected into its consumer. How?

·         Constructor injection: The dependency is passed as a parameter to the constructor of the controller.

·         Property injection: The dependency is assigned to a property of the controller, and then used internally.

 

public class HomeController : Controller

{

    public HomeController(IRepositoryContext context)

    {

        this.context = context;

    }

 

    private readonly IRepositoryContext context;

Injection of dependency by constructor

 

public class HomeController : Controller

{

    public IRepositoryContext Context { get; set; }

Injection of dependency by property

 

Different schools of thought exist on which method is more appropriate, and each of them has pros and cons. In a nutshell: use constructor injection when the object must have the dependency, that is the dependent reference must exist otherwise the object does not work. Instead, use property injection when you have a good default standard (and don’t forget to set it in the setter method of the property) and you want to have the flexibility to provide an optional alternative, for example a mock for testing purposes. Good news, whatever strategy you decide for dependency injection, our DI container of choice, Ninject, supports both.

 

DI with Ninject

The use of DI containers helps maintaining a centralised location for setting all the dependencies, and then inject them at runtime in the consuming objects. This central location is called the Composition Root, and it depends on the nature of the application. In a Console app, it’s typically the Main method of the Program class. In an ASP.NET MVC application, you may want to build all your objects once for the lifecycle of the application, and a good place to do so is the Application_Start method in the Global.asax file.

public class MvcApplication : System.Web.HttpApplication

{

    protected void Application_Start()

    {

        AreaRegistration.RegisterAllAreas();

        RouteConfig.RegisterRoutes(RouteTable.Routes);

 

        DIContainer = new ServiceInjectionContainer();

    }

 

    protected ServiceInjectionContainer DIContainer;

}

Reference to the DI container in the MVC application start up method

 

Yes, we have a strong reference here to the DI container of choice. But this is the only place in the entire application where object reference is hard coded (at condition that we apply the DI pattern consistently, obviously). If tomorrow you want to replace your dependencies or even the DI container, there is only one place that requires change, with no impact on the rest of the application. Pretty powerful!

 

Injection Container

The Ninject framework works around two main classes:

·         The Injection Container

·         The Dependency Resolver

 

The Injection Container represents the entry point for the DI engine to resolve dependencies. In an application, there should be one instance only of the DI container for a specific set of services to be resolved. Ninject exposes this entry point through the StandardKernel class. This class requires an instance of the Dependency Resolver in input, as we will see shortly.

But first of all, we need to reference Ninject in our project. Easy done, Ninject is available in NuGet.

Ninject package in NuGet

 

Then we can write our own ServiceInjectionContainer class that was instantiated in the Global.asax. As said, when creating an instance of the Ninject’s Standard Kernel, the Dependency Resolver is passed as parameter. There is where the mapping of interfaces to concrete classes is defined.

public class ServiceInjectionContainer : IDisposable

{

    public ServiceInjectionContainer()

    {

        _kernel = new Ninject.StandardKernel(new ServiceDependencyResolver());

    }

 

    private readonly Ninject.IKernel _kernel;

}

Implementation of the Injection Container with Ninject

 

Dependency Resolver

By programming by interface, we have built our University application with a number of services for registration and waitlist management, as well as abstracted the reference to the SharePoint client context. Now it’s time to specify the concrete class that implements the interface, and bind them in the Dependency Resolver module of Ninject.

This is a class that inherits from NinjectModule. All We have to do is to override the Load method and specify a list of binding conditions between the interface and its concrete implementation. The syntax is:

            Bind<Interface>().To<Implementation>();

internal class ServiceDependencyResolver : NinjectModule

{

    public override void Load()

    {

        Bind<IRepositoryContext>().To<SharePointContext>()

            .WithConstructorArgument(SharePointContext.SharePointSiteUrl);

    }

}

Binding the Repository contract to the SharePoint client context

 

In the Load method, we would register the dependency to the SharePoint context, as well as the other services used in the application. The DI container will use this mapping for instantiating a concrete object instance every time there is an injected reference to the mapped interface in our code. Easier to do than to say!

 

Ninject is a lightweight still powerful DI container

 


  Comments

 

Stefano
On 30 Sep 2016 at 13:47
You are more than welcome Vinay, thanks for your comment.
Vinay Joseph
On 29 Sep 2016 at 03:25
Brilliant work Stefano! Thank you for this insightful post.
 Source Code

 Related Content
A flexible Default Value for your DateTime properties
When creating an MVC application with Entity Framework, it is possible to set default values for most properties in a model using the DefaultValue attribute. However, no much flexibility is offered for a DateTime property. This article presents a custom validation attribute for DateTime types that accepts different formats for defining the default value of the property.
Adding a Secured Geo-located Audit Trail
How I built a social sharing component for my own web site and added a secured geo-located audit trail. Step by step, let’s analyse technologies and source code for developing this component.
Adding Social Sharing to a Web Site
How I built a social sharing component for my own web site and added a secured geo-located audit trail. Step by step, let’s analyse technologies and source code for developing this component.
Best practices for mobile form design in SharePoint
Build effective SharePoint forms with Nintex that are accessible anywhere, at any time, and on any device. You built the workflows, you built the forms, now make them mobile.
Bring your “A” game to ESPC16
With just over 3 weeks to go to Europe's largest gathering of SharePoint & Office 365 professionals, take a look at these tips that will help you get the most out of ESPC16…
Building an MVC application for SharePoint
Learn how to write code to perform basic operations with the SharePoint 2013 .NET Framework client-side object model (CSOM), and build an ASP.NET MVC application that retrieves information from a SharePoint server.
CIO vs CTO
What are the synergies and differences of the roles of a Chief Information Officer and a Chief Technology Officer? An open conversation about two roles with one mission…
Coded UI test automation of MVC applications with Visual Studio
Whether you are a software developer, tester, administrator or analyst, this article can help you master different types of UI testing of an MVC application, by using Visual Studio for creating coded UI test suites that can be automated for continuous execution.
Converting GIS spatial coordinates
Different formats and standards exist for describing geographical coordinates in GIS systems and applications. This article explains how to convert between the most used formats, presenting a working library written in C#.
Creating mobile accessible forms in SharePoint
With the release of the Nintex Mobile apps, SharePoint users can now optimise their experience across popular mobile devices and platforms.
Define your Performance Testing strategy with Visual Studio
Performance Testing is an essential part of software testing, with the specific goal of determining how a system performs in terms of responsiveness and stability under a particular workload. In this series of posts we’ll define and execute a good strategy for testing performance of an application using Visual Studio.
Disserting about colliding GUIDs and the Big Bang Theory
Can you generate two identical GUIDs? Would the world end if two GUIDs collide? How long does it take to generate a duplicate GUID and would we be still here when the result is found?
GIS Location Services in Dynamics CRM
A design paper about implementing GIS-based services for a local Council in Dynamics CRM, structuring address data, and delivering location services in the form of WebAPI endpoints via an Enterprise Service Bus.
Group or Team?
All teams are groups but not all groups are teams. What defines a group and what a team? When do we need one over the other one?
How to Give Constructive Feedback
Learning to give and receive constructive feedback is an essential part of learning, growing, improving and achieving our goals.
Mirroring an iPad on your laptop
Have you ever wanted to see your iPhone or iPad on a larger screen? Play games, watch movies, demo apps or present to your computer from your iPhone or iPad. Reflector mirrors iOS devices on big screens wirelessly using iOS built-in AirPlay mirroring.
Mobilize your SharePoint workflows
Build workflow applications in SharePoint that can be accessed on mobile devices using the Nintex solution for business process mobilization.
Natural String Sorting
Have you ever desired to have in your code a way to order a sequence of strings in the same way as Windows does for files whose name contains a mix of letters and numbers? Natural string sorting is not natively supported in .NET but can be easily implemented by specialising a string comparer and adding a few extensions to the enumerable string collection.
Sales Effectiveness in Dynamics CRM with Azure IoT and Machine Learning - Part 1
How can an organisation optimise its sales channels and product targeting by building a 365-degree view of its customers in Dynamics CRM? The answer, and topic of this article, is with the help of Azure IoT and Machine Learning services!
Scaling Applications with Azure Redis and Machine Learning - Part 1
This article presents design best practices and code examples for implementing the Azure Redis Cache and tuning the performance of ASP.NET MVC applications, optimising cache hit ratio and reducing “miss rate” with smart algorithms processed by Machine Learning.
Software Development Management in 11 steps
What it takes to be a great Software Development Manager? I have been building software for the last 15 years and have collected a few stories and experiences to share. Some I have used as questions when interviewing candidates. In 11 points, this is my story to date.
The art of outsourcing projects
Outsourcing may look financially attractive, but working with companies in far-off lands introduces challenges that, if not considered properly, can drive any project to failure. Let’s explore some common pitfalls when working with offshore partners and a common-sense approach to work around them.
The value of hashtags
Customers expect a modern approach to advertising. Digital advertising can leverage evolving technology to provide just-in-time, just-at-the-right-place promotions.
We don't need no Yoda's syntax
There is an urban myth in the programmers’ community that the so called “Yoda’s syntax” performs better when checking an object for nullity. Let's demystify it...