In this post, we’re going to explore the OwningComponentBase class, which was added to Blazor in .NET Core 3 Preview 9. It has a single purpose, to create a service provider scope. This is really useful in Blazor as service scopes behave a bit differently to how they do in say, MVC or Razor Pages applications. Using this class as a base, we can create components which control the lifetime of the service provider scope.
Why do we need OwningComponentBase?
The reason we need OwningComponentBase
is down to how service scopes behave in Blazor. Let’s start by covering what service scopes are and how they work in more traditional ASP.NET Core applications, like MVC. Then we’ll look at how they differ in Blazor.
Service Scopes
Service scopes are used to define the lifetime of a service. In .NET Core there are 3 scopes available to us.
- Transient
- Scoped
- Singleton
Transient services have the shortest lifetime out of the 3 scopes available. With this scope, a new instance of the service is created every-time it’s requested. Meaning that multiple instances of the service could be created in a single request.
Scoped services are created only once per request. So within a single request you will always get the same instance of the service every-time you request it.
Singleton services are created once for the lifetime of the application. This means that every user of the application will receive the same instance of the service until the application is restarted.
In MVC or Razor Pages these scopes work well and do what they say on the tin. This is because these types of applications have a traditional request/response model. But how do these scopes work in Blazor?
The answer, probably not quite as you’d expect. I wrote a blog post a while ago, Service Lifetimes in Blazor, covering how scopes work in Blazor. Please give that a read for an in-depth explanation, but I’ll summarise the important parts here.
Blazor Server Apps
While transient and singleton services still behave the same way, service lifetimes are scoped using the SignalR connection in Blazor Server apps. This means that scoped services behave in a very similar way to Singleton.
This is because while the user is interacting with the application, clicking buttons, navigating between pages, etc. These actions are all happening on the same SignalR connection. Therefor, in terms of service scopes it’s all one request, which keeps scoped services around for much longer than they normally would be.
Blazor WebAssembly Apps
Once again, transient and singleton services behave the same way. Scoped services behave in a similar way to Blazor Server, but for a slightly different reason. This time it’s because the application lives and dies inside the browser on the client. The user is the only user and again there is only ever one request.
Effectively, there are only two service scopes available in Blazor WebAssembly, transient and singleton.
How OwningComponentBase Helps
OwningComponentBase allows us to create components which can control the lifetime of the service scope. This means that when we create a component which inherits from OwningComponentBase
any service(s), and related services, we request via it are disposed of when the component is disposed.
This essentially gives us back a version of scoped lifetime which makes sense in Blazor. For example, if we used OwningComponentBase
as the base for a page component, any services requested by that page would only live for the lifetime of that page.
Because OwningComponentBase
will dispose of any services and related services that share its scope, it makes it a great choice when dealing with repositories or other database abstractions. In these scenarios, without OwningComponentBase
, services can hang around for a very long time. As we covered earlier, scoped services are tied to the lifetime of the SignalR connection in Blazor Server apps. So a repository which uses a DbContext
, for example, could live much longer than expected which may end up being quite problematic.
Using OwningComponentBase
Now we have a good idea about what the class does and the problem it solves, how do we actually use it?
For starters, it is an abstract class which means we can’t just new it up, we must inherit from it. OwningComponentBase
itself inherits from ComponentBase
so you can just replace any ComponentBase
usages with OwnComponentBase
and everything will continue to function as before.
There are two versions of the class, OwningComponentBase
and OwningComponentBase<T>
, and they have slightly different abilities.
OwningComponentBase
OwningComponentBase<T>
provides a single service, of type T
, which we can access via a property called Service
. Say we had a work item repository which looked like this.
public interface IWorkItemRepository
{
IEnumerable<WorkItem> GetAllWorkItems();
}
We can use OwningComponentBase<T>
to create an instance of IWorkItemRepository
and consume it like this.
@page "/workitems"
@inherits OwningComponentBase<IWorkItemRepository>
<h1>View Work Items</h1>
<ul>
@foreach (var workItem in Service.GetAllWorkItems())
{
<li>@workItem.Description</li>
}
</ul>
That’s great but what if we need to use multiple services in our component? That’s where OwningComponentBase
comes in.
OwningComponentBase
While OwningComponentBase<T>
provides a simple and efficient way to request and access a single service. OwningComponentBase
give us the ability to request as many services as we wish, albeit with a bit more work required on our part.
Continuing with the previous example code. Let’s pretend we want to show a work order and its related work items. To do this we need both a IWorkOrderRepository
instance and a IWorkItemRepository
instance.
public interface IWorkOrderRepository
{
WorkOrder GetWorkOrder(int workOrderId);
}
public interface IWorkItemRepository
{
IEnumerable<WorkItem> GetWorkItems(int workOrderId);
}
We can request both of these using OwningComponentBase
’s ScopedServices
property. This property is of type IServiceProvider
and contains a method called GetService
. We can use this to request any services we need in our component, for example.
@page "/workorder/{WorkOrderId:int}"
@inherits OwningComponentBase
<h1>View Work Order</h1>
<p>@workOrder.Description</p>
<h2>Work Items</h2>
<ul>
@foreach (var workItem in _workItemRepository.GetWorkItems(workOrder.Id))
{
<li>@workItem.Description</li>
}
</ul>
@code {
[Parameter] public int WorkOrderId { get; set; }
WorkOrder workOrder = new WorkOrder();
IWorkOrderRepository _workOrderRepository;
IWorkItemRepository _workItemRepository;
protected override void OnInitialized()
{
_workOrderRepository = (IWorkOrderRepository)ScopedServices.GetService(typeof(IWorkOrderRepository));
_workItemRepository = (IWorkItemRepository)ScopedServices.GetService(typeof(IWorkItemRepository));
workOrder = _workOrderRepository.GetWorkOrder(WorkOrderId);
}
}
As you can see, there is a bit more work involved on our part but it does give us access to as many services as we need.
Summary
I think that about wraps things up for this post. We’ve had a good look at the new OwningComponentBase
class which came with .NET Core Preview 9. A class which allows authoring of components which control the lifetime of a service provider scope.
We’ve taken a look at service scopes and how they differ in Blazor compared to traditional ASP.NET Core applications, such as MVC and Razor Pages. Then seen how the OwningComponentBase
class can help us manage these differences. We then finished up with some examples of how to use OwningComponentBase
and OwningComponentBase<T>
.