<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Shaun Curtis</title>
    <description>The latest articles on Forem by Shaun Curtis (@shauncurtis).</description>
    <link>https://forem.com/shauncurtis</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F432695%2F2de2c710-15b4-460e-a220-b7943ef53736.jpeg</url>
      <title>Forem: Shaun Curtis</title>
      <link>https://forem.com/shauncurtis</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/shauncurtis"/>
    <language>en</language>
    <item>
      <title>The Blazor Component Service Scope Conundrum</title>
      <dc:creator>Shaun Curtis</dc:creator>
      <pubDate>Tue, 24 Jan 2023 19:27:49 +0000</pubDate>
      <link>https://forem.com/shauncurtis/the-blazor-component-service-scope-conundrum-22fm</link>
      <guid>https://forem.com/shauncurtis/the-blazor-component-service-scope-conundrum-22fm</guid>
      <description>&lt;p&gt;Apply good design practices to components, and you separate out data management from the display function.  A component such as &lt;code&gt;FetchData&lt;/code&gt; in the demo project "Fetches(Manages) data AND displays it in a table".  There's an &lt;strong&gt;AND&lt;/strong&gt; in there: a very good indicator that &lt;code&gt;FetchData&lt;/code&gt; has multiple concerns/responsibilities.&lt;/p&gt;

&lt;p&gt;Apply the &lt;em&gt;Single Responsibilty Principle&lt;/em&gt; and you have two classes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;WeatherForcastListForm&lt;/code&gt; - a component that displays a list of WeatherForecasts&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;WeatherForecastListPresenter&lt;/code&gt; - an object that interfaces with the data pipeline to manage the list of WeatherForecasts.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu051riv9373mtrnatmrp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu051riv9373mtrnatmrp.png" alt="Pipeline" width="800" height="221"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Take those same design principles further, and you inject an instance of &lt;code&gt;WeatherForecastListPresenter&lt;/code&gt; into &lt;code&gt;WeatherForcastListForm&lt;/code&gt; from DI with the same lifecycle scope as the form.&lt;/p&gt;

&lt;p&gt;In the DotNetCore framework that presents a dilemna.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;Scoped&lt;/code&gt; is too wide: it's scoped for the duration of the SPA session.  It works if used in one place and you want to persist state for the duration of the SPA session.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Transient&lt;/code&gt; is too narrow.&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Sub-components in the Form can't use DI to access the same instance of &lt;code&gt;WeatherForecastListPresenter&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Any class implementing &lt;code&gt;IDisposable&lt;/code&gt; or &lt;code&gt;IAsyncDisposable&lt;/code&gt; should never be scoped as &lt;em&gt;Transient&lt;/em&gt;.  The DI service container maintains a reference to the instance to &lt;em&gt;Dispose&lt;/em&gt; it when the container itself is &lt;em&gt;Disposed&lt;/em&gt;.  You create a "memory leak" in your application as copies of &lt;code&gt;WeatherForecastListPresenter&lt;/code&gt; build up every time you visit the form.  They are only disposed when you close down or refresh you session with the application.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's no a clean fit.&lt;/p&gt;

&lt;p&gt;Step forward &lt;code&gt;OwningComponentBase&lt;/code&gt;.  It creates it's own scoped service container which it disposes when the component is disposed.  It has the same scope as the component.&lt;/p&gt;

&lt;p&gt;Unfortunately there's a fatal flaw: any scoped service your service depends on is also created in the same container.  It is after all just a &lt;em&gt;Scoped&lt;/em&gt; container.&lt;/p&gt;

&lt;p&gt;Consider &lt;code&gt;AuthenticationService&lt;/code&gt;.  The instance in the SPA scoped container is the one your service needs, but it gets a new vanailla one with no user information.  Same with any Notification services, the NavigationManager and many others.&lt;/p&gt;

&lt;p&gt;It's Ok for services with no dependancies, but....  we don't code many of those!&lt;/p&gt;

&lt;h2&gt;
  
  
  Solving the Conundrum
&lt;/h2&gt;

&lt;p&gt;The fact is, the DotNetCore service container configuration is designed around the old MVC server side model.  There's no scope, or container, that matches the scope of a component.  Until Microsoft provides one, we need a workaround.&lt;/p&gt;

&lt;p&gt;My solution is described below.  It's uses a simple time service and component that display/update a time value to demonstrate the solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Repo
&lt;/h2&gt;

&lt;p&gt;The repo and latest version of this article can be found here &lt;a href="https://github.com/ShaunCurtis/Blazr.ComponentServiceProvider" rel="noopener noreferrer"&gt;Blazr.ComponentServiceProvider&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Demo Timer Service
&lt;/h2&gt;

&lt;p&gt;The simple Timer service interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ITimeService&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;event&lt;/span&gt; &lt;span class="n"&gt;EventHandler&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;TimeChanged&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;UpdateTime&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The concrete service.  Debug code is included to monitor instances and disposal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TimeService&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ITimeService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IDisposable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IAsyncDisposable&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Guid&lt;/span&gt; &lt;span class="n"&gt;InstanceId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Guid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NewGuid&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;asyncdisposedValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;disposedValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToLongTimeString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;event&lt;/span&gt; &lt;span class="n"&gt;EventHandler&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;TimeChanged&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;TimeService&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"TimeService - instance &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;InstanceId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; created"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;UpdateTime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToLongTimeString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;TimeChanged&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;Invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EventArgs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;ValueTask&lt;/span&gt; &lt;span class="nf"&gt;DisposeAsync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;asyncdisposedValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"TimeService - instance &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;InstanceId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; async disposed"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;asyncdisposedValue&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ValueTask&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompletedTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;disposing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;disposedValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;disposing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"TimeService - instance &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;InstanceId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; disposed"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="n"&gt;disposedValue&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;disposing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;GC&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SuppressFinalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  TimeStamp
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;TimeStamp&lt;/code&gt; is a simple component thats displays and updates the &lt;code&gt;TimeService&lt;/code&gt;.  &lt;code&gt;ITimeService&lt;/code&gt; is cascaded.  Debug code shows when &lt;code&gt;SetParametersAsync&lt;/code&gt; async is called.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;@namespace&lt;/span&gt; &lt;span class="n"&gt;Blazr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UI&lt;/span&gt;
&lt;span class="n"&gt;@implements&lt;/span&gt; &lt;span class="n"&gt;IHandleAfterRender&lt;/span&gt;
&lt;span class="n"&gt;@implements&lt;/span&gt; &lt;span class="n"&gt;IHandleEvent&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="err"&gt;="&lt;/span&gt;&lt;span class="nc"&gt;bg&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;light&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="s"&gt;"&amp;gt;
&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;TimeStamp&lt;/span&gt; &lt;span class="n"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="err"&gt;="&lt;/span&gt;&lt;span class="nc"&gt;m&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="s"&gt;"&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="err"&gt;="&lt;/span&gt;&lt;span class="nc"&gt;btn&lt;/span&gt; &lt;span class="n"&gt;btn&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="s"&gt;" @onclick=Clicked&amp;gt;Update Timestamp&amp;lt;/button&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TimeService&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="s"&gt;"No message set."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="err"&gt;="&lt;/span&gt;&lt;span class="nc"&gt;mt&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="n"&gt;bg&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;dark&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;white&lt;/span&gt;&lt;span class="s"&gt;"&amp;gt;
&lt;/span&gt;        &lt;span class="n"&gt;Parameters&lt;/span&gt; &lt;span class="n"&gt;Set&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="n"&gt;at&lt;/span&gt; &lt;span class="n"&gt;@this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ParametersChangedTimeStamp&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="n"&gt;@code&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;CascadingParameter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;ITimeService&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;TimeService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;!;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;ParametersChangedTimeStamp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Not Set"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnInitialized&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TimeService&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;NullReferenceException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"The &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetType&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;FullName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; required a cascaded ITimeService"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnParametersSet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Debug&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"TimeStamp - Parameter Change"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ParametersChangedTimeStamp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToLongTimeString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnParametersSet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Clicked&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;TimeService&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;UpdateTime&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Saving CPU Cycles - No AfterRender Handling&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="n"&gt;IHandleAfterRender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnAfterRenderAsync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompletedTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Saving CPU Cycles - shortcut the UI event handling code. One render per UI event&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;HandleEventAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EventCallbackWorkItem&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;InvokeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;StateHasChanged&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Service Utilities
&lt;/h3&gt;

&lt;p&gt;We need code to create and manage our &lt;em&gt;service&lt;/em&gt;.  &lt;/p&gt;

&lt;p&gt;It must create an instance of &lt;code&gt;TService&lt;/code&gt; which:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;May or may not have DI dependancies.&lt;/li&gt;
&lt;li&gt;May or may not implement &lt;code&gt;IDisposable&lt;/code&gt; and/or &lt;code&gt;IAsyncDisposable&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;May be an interface or base class service definition in the ServiceContainer.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;ActivatorUtilities&lt;/code&gt; is a little known utility class that creates and populates object instances with dependancies.&lt;/p&gt;

&lt;p&gt;The code implements extension methods on &lt;code&gt;IServiceContainer&lt;/code&gt; to provide the functionality.  It's deceptively simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ServiceUtilities&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;TService&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;GetComponentService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;IServiceProvider&lt;/span&gt; &lt;span class="n"&gt;serviceProvider&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;TService&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;
    &lt;span class="err"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;var&lt;/span&gt; &lt;span class="n"&gt;serviceType&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serviceProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()?.&lt;/span&gt;&lt;span class="nf"&gt;GetType&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serviceType&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ActivatorUtilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CreateInstance&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;serviceProvider&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ActivatorUtilities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;serviceProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;serviceType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;TService&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;serviceType&lt;/code&gt; is either the concrete DI registered object for &lt;code&gt;TService&lt;/code&gt;, or &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If &lt;code&gt;ServiceType&lt;/code&gt; is null, there's no definition for &lt;code&gt;TService&lt;/code&gt; in the service container; it must be activated directly.&lt;/p&gt;

&lt;p&gt;If service type is a type, it's activated as the supplied concrete type.&lt;/p&gt;

&lt;p&gt;In either case, the method may return a null if &lt;code&gt;CreateInstance&lt;/code&gt; can't create an instance.&lt;/p&gt;

&lt;p&gt;There's a second Try wrapper method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;TryGetComponentService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;IServiceProvider&lt;/span&gt; &lt;span class="n"&gt;serviceProvider&lt;/span&gt;&lt;span class="p"&gt;,[&lt;/span&gt;&lt;span class="nf"&gt;NotNullWhen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="n"&gt;TService&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;TService&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;
    &lt;span class="err"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;service&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serviceProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetComponentService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  CascadingComponentService
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;CascadingComponentService&lt;/code&gt; is a a component wrapper to encapsulate service creation and disposal.&lt;/p&gt;

&lt;p&gt;The main code is implemented in &lt;code&gt;SetParametersAsync&lt;/code&gt;: everything happens before any render event occurs. &lt;code&gt;IAsyncDisposable&lt;/code&gt; is implemented to ensure &lt;code&gt;TService&lt;/code&gt; is disposed correctly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;@namespace&lt;/span&gt; &lt;span class="n"&gt;Blazr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UI&lt;/span&gt;
&lt;span class="n"&gt;@typeparam&lt;/span&gt; &lt;span class="n"&gt;TService&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;TService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;
&lt;span class="nc"&gt;@implements&lt;/span&gt; &lt;span class="n"&gt;IAsyncDisposable&lt;/span&gt;
&lt;span class="n"&gt;@implements&lt;/span&gt; &lt;span class="n"&gt;IHandleAfterRender&lt;/span&gt;
&lt;span class="n"&gt;@implements&lt;/span&gt; &lt;span class="n"&gt;IHandleEvent&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CascadingValue&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"this.ComponentService"&lt;/span&gt; &lt;span class="n"&gt;IsFixed&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;@this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChildContent&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;CascadingValue&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="n"&gt;@code&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;RenderFragment&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;ChildContent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Inject&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;IServiceProvider&lt;/span&gt; &lt;span class="n"&gt;serviceProvider&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;!;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;TService&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;ComponentService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;!;&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;_firstRender&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;IDisposable&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;_disposable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;IAsyncDisposable&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;_asyncDisposable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;SetParametersAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ParameterView&lt;/span&gt; &lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetParameterProperties&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_firstRender&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ComponentService&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;serviceProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetComponentService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ComponentService&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;NullReferenceException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"No &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TService&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;FullName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt; cound be created."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="n"&gt;_disposable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ComponentService&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;IDisposable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="n"&gt;_firstRender&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="c1"&gt;// Saving CPU Cycles - No Initialized/OnParametersSet run&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StateHasChanged&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompletedTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;ValueTask&lt;/span&gt; &lt;span class="nf"&gt;DisposeAsync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_disposable&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ComponentService&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;IAsyncDisposable&lt;/span&gt; &lt;span class="n"&gt;asyncDisposable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncDisposable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DisposeAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Saving CPU Cycles - No AfterRender Handling&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="n"&gt;IHandleAfterRender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnAfterRenderAsync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompletedTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Saving CPU Cycles - No automatic rendering&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="n"&gt;IHandleEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;HandleEventAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;EventCallbackWorkItem&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;InvokeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Demo Page
&lt;/h3&gt;

&lt;p&gt;The display page demonstrates using &lt;code&gt;CascadingComponentService&lt;/code&gt; and how to capture the service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;@page&lt;/span&gt; &lt;span class="s"&gt;"/"&lt;/span&gt;
&lt;span class="n"&gt;@implements&lt;/span&gt; &lt;span class="n"&gt;IDisposable&lt;/span&gt;
&lt;span class="n"&gt;@implements&lt;/span&gt; &lt;span class="n"&gt;IHandleAfterRender&lt;/span&gt;
&lt;span class="n"&gt;@implements&lt;/span&gt; &lt;span class="n"&gt;IHandleEvent&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PageTitle&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;PageTitle&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CascadingComponentService&lt;/span&gt; &lt;span class="n"&gt;TService&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"ITimeService"&lt;/span&gt; &lt;span class="n"&gt;@ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_service&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TimeStamp&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;CascadingComponentService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="err"&gt;="&lt;/span&gt;&lt;span class="nc"&gt;alert&lt;/span&gt; &lt;span class="n"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="s"&gt;"&amp;gt;
&lt;/span&gt;    &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_service&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;ComponentService&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="s"&gt;"No message set."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="n"&gt;@code&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;CascadingComponentService&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ITimeService&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;?&lt;/span&gt; &lt;span class="n"&gt;_service&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;OnInitializedAsync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Yield&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="c1"&gt;// Yields to let the UI do a first render and ensure _service is assigned&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_service&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ComponentService&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ComponentService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TimeChanged&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnTimeChanged&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnTimeChanged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EventArgs&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;StateHasChanged&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Dispose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_service&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ComponentService&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ComponentService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TimeChanged&lt;/span&gt; &lt;span class="p"&gt;-=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnTimeChanged&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Saving CPU Cycles - No AfterRender Handling&lt;/span&gt;
    &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="n"&gt;IHandleAfterRender&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OnAfterRenderAsync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CompletedTask&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can &lt;code&gt;unwrap&lt;/code&gt; the cascade and do it yourself within the root component.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;That's it, not rocket science or very orignial.&lt;/p&gt;

</description>
      <category>azure</category>
      <category>cloud</category>
      <category>database</category>
      <category>cosmosdb</category>
    </item>
    <item>
      <title>Controlling Routing and Navigation in Blazor Edit Forms</title>
      <dc:creator>Shaun Curtis</dc:creator>
      <pubDate>Wed, 15 Jul 2020 21:21:08 +0000</pubDate>
      <link>https://forem.com/shauncurtis/controlling-routing-and-navigation-in-blazor-edit-forms-bmh</link>
      <guid>https://forem.com/shauncurtis/controlling-routing-and-navigation-in-blazor-edit-forms-bmh</guid>
      <description>&lt;h1&gt;
  
  
  CEC.Routing
&lt;/h1&gt;

&lt;p&gt;Single Page Applications have several issues running as applications in a web browser. One such is navigation: the user can navigate away from the page in a variety of ways where the application has little control over what happens. Data loss often occurs in a less than satisfactory experience for the user.&lt;/p&gt;

&lt;p&gt;This library seeks to address the following navigation issues:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Intercepting and potentially cancelling intra-application routing when the page is dirty.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Intercepting and warning on a navigation event away from the application - there's no browser mechanism to prevent this. Entering a new URL in the URL bar, clicking on a favourite, ...&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  Library and Example Repositories
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;CEC.Routing&lt;/strong&gt; is an implementation of the standard Blazor router with functionality needed to control intra-application routing and the onbeforeunload browser event behaviour. It's released and available as a Nuget Package. The source code is available at &lt;a href="https://github.com/ShaunCurtis/CEC.Routing"&gt;https://github.com/ShaunCurtis/CEC.Routing&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;All the source code is available under the MIT license.&lt;/p&gt;

&lt;h2&gt;
  
  
  Intra-Application Routing
&lt;/h2&gt;

&lt;p&gt;Intercept routing on a dirty i.e. unsaved data page isn't possible with the out-of-the-box Blazor navigator/router. In fact we have no way to get to the internal routing decision making, so need to start from scratch.&lt;/p&gt;

&lt;p&gt;A quick digression on the basics of Blazor navigation/routing. &lt;/p&gt;

&lt;p&gt;DOM navigation events such as anchors, etc are captured by the Blazor JavaScript Interop code. They surface in the C# Blazor world through the NavigationManager. The user clicks on an HTML link or navlink in the browser, the NavigationManager service instance gets populated with the relevant URL data and the &lt;strong&gt;NavigationManager&lt;/strong&gt;.&lt;em&gt;LocationChanged&lt;/em&gt; event is fired. That's it for the NavigationManager. The heavy lifting is done by the Router. It gets initialized through app.razor on a page load, and wires itself into the &lt;strong&gt;NavigationManager&lt;/strong&gt;.&lt;em&gt;LocationChanged&lt;/em&gt; event. The developer has no access to its internal workings, so can't cancel anything.&lt;/p&gt;

&lt;p&gt;Fortunately, we can clone the standard router and add the necessary functionality. The new router is called RecordRouter. The key changes to the out-of-the-box router are as follows:&lt;/p&gt;

&lt;h3&gt;
  
  
  RouterSessionService
&lt;/h3&gt;

&lt;p&gt;Create a new scoped Service called RouterSessionService for controlling and interacting with the RecordRouter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt; public class RouterSessionService
{
/// &amp;lt;summary&amp;gt;
/// Property containing the currently loaded component if set
/// &amp;lt;/summary&amp;gt;
public IRecordRoutingComponent ActiveComponent { get; set; }

 /// &amp;lt;summary&amp;gt;
/// Boolean to check if the Router Should Navigate
 /// &amp;lt;/summary&amp;gt;

public bool IsGoodToNavigate =&amp;gt; this.ActiveComponent?.IsClean ?? true;

/// &amp;lt;summary&amp;gt;
/// Url of Current Page being navigated from
/// &amp;lt;/summary&amp;gt;
public string PageUrl =&amp;gt; this.ActiveComponent?.PageUrl ?? string.Empty;

 /// &amp;lt;summary&amp;gt;
 /// Url of the previous page
 /// &amp;lt;/summary&amp;gt;
public string LastPageUrl { get; set; }

/// &amp;lt;summary&amp;gt;
/// Url of the navigation cancelled page
 /// &amp;lt;/summary&amp;gt;

public string NavigationCancelledUrl { get; set; }

/// &amp;lt;summary&amp;gt;
/// Event to notify Navigation Cancellation
/// &amp;lt;/summary&amp;gt;

public event EventHandler NavigationCancelled;

/// &amp;lt;summary&amp;gt;
/// Event to notify that Intra Page Navigation has taken place
/// useful when using Querystring controlled pages
 /// &amp;lt;/summary&amp;gt;
public event EventHandler IntraPageNavigation;

private readonly IJSRuntime _js;

private bool _ExitShowState { get; set; }

public RouterSessionService(IJSRuntime js) =&amp;gt; _js = js;

/// &amp;lt;summary&amp;gt;
/// Method to trigger the NavigationCancelled Event
/// &amp;lt;/summary&amp;gt;
public void TriggerNavigationCancelledEvent() =&amp;gt; this.NavigationCancelled?.Invoke(this, EventArgs.Empty);

/// &amp;lt;summary&amp;gt;
/// Method to trigger the IntraPageNavigation Event
/// &amp;lt;/summary&amp;gt;
public void TriggerIntraPageNavigation() =&amp;gt; this.IntraPageNavigation?.Invoke(this, EventArgs.Empty);

/// &amp;lt;summary&amp;gt;
/// Method to set or unset the browser onbeforeexit challenge
/// &amp;lt;/summary&amp;gt;
/// &amp;lt;param name="action"&amp;gt;&amp;lt;/param&amp;gt;
/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;
public void SetPageExitCheck(bool show)
 {
   if (show != _ExitShowState) _js.InvokeAsync&amp;lt;bool&amp;gt;("cec_setEditorExitCheck", show);
    _ExitShowState = show;
 }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  RecordRouter
&lt;/h3&gt;

&lt;p&gt;This is a straight clone of the shipped router. The only changes are in the &lt;em&gt;OnLocationChanged&lt;/em&gt; event handler. It now looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private void OnLocationChanged(object sender, LocationChangedEventArgs args)
{

  // Get the Page Uri minus any query string
  var pageurl = this.NavigationManager.Uri.Contains("?") ? this.NavigationManager.Uri.Substring(0, this.NavigationManager.Uri.IndexOf("?")): this.NavigationManager.Uri ;
  _locationAbsolute = args.Location;

  // CEC ADDED - SessionState Check for Unsaved Page
  if (_renderHandle.IsInitialized &amp;amp;&amp;amp; Routes != null &amp;amp;&amp;amp; this.RouterSessionService.IsGoodToNavigate)
  {
    // Clear the Active Component - let the next page load itself into it if required
    this.RouterSessionService.ActiveComponent = null;
    this.RouterSessionService.NavigationCancelledUrl = null;
    Refresh(args.IsNavigationIntercepted);
  }
  else
  {
    // CEC ADDED - Trigger a Navigation Cancelled Event on the SessionStateService
    if (this.RouterSessionService.PageUrl.Equals(_locationAbsolute, StringComparison.CurrentCultureIgnoreCase))
      {
        // Cancel routing
        this.RouterSessionService.TriggerNavigationCancelledEvent();
      }
    else
    {
      // we're cancelling routing, but the Navigation Manager is current set to the aborted page
      // so we set the navigation cancelled url so the page can navigate to it if necessary
      // and do a dummy trip through the Navigation Manager again to set this back to the original page
      this.RouterSessionService.NavigationCancelledUrl = this.NavigationManager.Uri;
      this.NavigationManager.NavigateTo(this.RouterSessionService.PageUrl);
    }
  }
  if (RouterSessionService.LastPageUrl != null &amp;amp;&amp;amp; RouterSessionService.LastPageUrl.Equals(pageurl, StringComparison.CurrentCultureIgnoreCase)) RouterSessionService.TriggerIntraPageNavigation();
  RouterSessionService.LastPageUrl = pageurl;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;On a &lt;strong&gt;NavigationManager&lt;/strong&gt;.&lt;em&gt;LocationChanged&lt;/em&gt; event, the method:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Gets the page URL minus any query string, and checks if we are good to route. (I like to use a combination of routing and query strings - more flexible than all routing in many instances).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Yes&lt;/strong&gt; - clears out the relevant fields on the &lt;strong&gt;RouterSessionService&lt;/strong&gt; and routes through the &lt;em&gt;Refresh&lt;/em&gt; method.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No&lt;/strong&gt; - triggers a &lt;em&gt;NavigationCancelled&lt;/em&gt; event. We solve the displayed URL issue by making a second dummy run through navigation to reset the displayed URL. Anyone know a better way?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Check for Intra-Page Navigation and trigger the &lt;em&gt;IntraPageNavigation&lt;/em&gt; event if needed. Useful where only the query string changes.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The routing is controlled by the &lt;em&gt;IsGoodToNavigate&lt;/em&gt; property on the &lt;strong&gt;RouterSessionService&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public bool IsGoodToNavigate =&amp;gt; this.ActiveComponent?.IsClean ?? true;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This is only false when we cancel routing - i.e. the &lt;em&gt;ActiveComponent&lt;/em&gt; exists and is dirty. A lot of coding/refactoring to make a binary check!&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up you site to use the Router
&lt;/h2&gt;

&lt;p&gt;Install the Nuget Package&lt;/p&gt;

&lt;h4&gt;
  
  
  Startup.cs
&lt;/h4&gt;

&lt;p&gt;Add the CEC.Routing services&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using CEC.Routing;
 
public void ConfigureServices(IServiceCollection services)
{
....
services.AddCECRouting();
....
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  _Imports.razor
&lt;/h4&gt;

&lt;p&gt;Add the following namespace references&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@using CEC.Routing
@using CEC.Routing.Services
@using CEC.Routing.Router
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  App.razor
&lt;/h4&gt;

&lt;p&gt;Change the name of the Router to RecordRouter&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;RecordRouter AppAssembly="@typeof(Program).Assembly"&amp;gt;
......
&amp;lt;/RecordRouter&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Implementing the Router
&lt;/h3&gt;

&lt;p&gt;There's a sample site on the Github repository demonstrating the use of the library on a WeatherForecast editor.&lt;/p&gt;

&lt;p&gt;NOTE - Record routing only kicks in if you set up a page component to use it. Normal pages will route as normal: you don't need to configure them not to.&lt;/p&gt;

&lt;p&gt;You interact with the router through the &lt;strong&gt;RouterSessionService&lt;/strong&gt;. To configure a page to use the extra routing functionality:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Inject the service into any edit page.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implement the &lt;strong&gt;&lt;em&gt;IRecordRoutingComponent&lt;/em&gt;&lt;/strong&gt; Interface on the page&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Next you need to add an event handler for the navigation cancelled event. This should contain code to tell the user that navigation was cancelled and potentially ask them if they really want to leave the page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;protected virtual void OnNavigationCancelled(object sender, EventArgs e)
{
  this.NavigationCancelled = true;
  this.ShowExitConfirmation = true;
  this.AlertMessage.SetAlert("&amp;lt;b&amp;gt;THIS RECORD ISN'T SAVED&amp;lt;/b&amp;gt;. Either &amp;lt;i&amp;gt;Save&amp;lt;/i&amp;gt; or &amp;lt;i&amp;gt;Exit Without Saving&amp;lt;/i&amp;gt;.", Alert.AlertDanger);
  InvokeAsync(this.StateHasChanged);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This one (from the &lt;strong&gt;EditRecordComponentBase&lt;/strong&gt; boilerplate in the project):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Sets a couple of local properties used in controlling which buttons display when.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sets an alert box to display.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Calls &lt;em&gt;StateHasChanged&lt;/em&gt; to refresh the UI.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Add the following code to the component &lt;em&gt;OnInitialized&lt;/em&gt; or &lt;em&gt;OnInitializedAsync&lt;/em&gt; event&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  this.PageUrl = this.NavManager.Uri;
  this.RouterSessionService.ActiveComponent = this;
  this.RouterSessionService.NavigationCancelled += this.OnNavigationCancelled;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Sets the &lt;strong&gt;&lt;em&gt;PageURL&lt;/em&gt;&lt;/strong&gt; property to the current URL (pages names/directories and routing URLs are now very different).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sets the &lt;strong&gt;RouterSessionService&lt;/strong&gt; &lt;em&gt;ActiveComponent&lt;/em&gt; reference to the component.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Attaches the above event hander to the &lt;strong&gt;RouterSessionService&lt;/strong&gt; &lt;em&gt;NavigationCancelled&lt;/em&gt; Event.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The final bit of the jigsaw is connecting the &lt;strong&gt;IRecordRoutingComponent&lt;/strong&gt;.&lt;em&gt;IsClean&lt;/em&gt; property. Its important to get this right. The router uses this property to route/cancel routing.&lt;/p&gt;

&lt;p&gt;In my projects it's wired directly to the &lt;em&gt;IsClean&lt;/em&gt; property on the specific data service associated with the record. It gets set when the record in the service changes.&lt;/p&gt;

&lt;p&gt;In the CEC.Routing sample project it's set and unset in the &lt;em&gt;CheckForChanges&lt;/em&gt; method which is called whenever an edit control is changed.&lt;/p&gt;

&lt;p&gt;The following code shows how to override the cancel routing event - such as when the users wants to exit regardless.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;        protected void ConfirmExit()
        {
            this.IsClean = true;
            if (!string.IsNullOrEmpty(this.RouterSessionService.NavigationCancelledUrl)) this.NavManager.NavigateTo(this.RouterSessionService.NavigationCancelledUrl);
            else if (!string.IsNullOrEmpty(this.RouterSessionService.LastPageUrl)) this.NavManager.NavigateTo(this.RouterSessionService.LastPageUrl);
            else this.NavManager.NavigateTo("/");
        }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Note that the &lt;strong&gt;RouterSessionService&lt;/strong&gt; holds the cancelled URL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Intercepting/Warning on external Navigation
&lt;/h2&gt;

&lt;p&gt;You can't lock down the browser window to stop this - I wish we could. The only control browsers offer is the &lt;em&gt;onbeforeunload&lt;/em&gt; event. When a function is registered on this event, the browser displays a popup warning dialog, giving the user the option to cancel navigation. The degree of control, what appears in the box, and what you need the attached function to do differs across browsers.&lt;/p&gt;

&lt;p&gt;The sledgehammer approach is to add the following to your &lt;strong&gt;_Host.html&lt;/strong&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script&amp;gt;
  window.onbeforeunload = function () {
    return "Do you really want to leave?";
  };
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;It forces a popup exit box whenever the users tries to leave the application, like using a wrecking ball to crack a nut. There are many instances where you want to leave the application - authentication, print pages to name a couple. Having the exit popup box coming up every time is a pain.&lt;/p&gt;

&lt;p&gt;CEC.Routing implements a more nuanced and focused alternative. It still uses the &lt;em&gt;onbeforeunload&lt;/em&gt; event, but dynamically registers and unregisters with the event as needed. i.e. only when a form is dirty.&lt;/p&gt;

&lt;h4&gt;
  
  
  CEC.Routing.js
&lt;/h4&gt;

&lt;p&gt;The client side Javascript files looks like this (pretty self explanatory):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;window.cec_setEditorExitCheck = function (show) {
  if (show) {
    window.addEventListener("beforeunload", cec_showExitDialog);
  }
  else {
    window.removeEventListener("beforeunload", cec_showExitDialog);
  }
}

window.cec_showExitDialog = function (event) {
  event.preventDefault();
  event.returnValue = "There are unsaved changes on this page. Do you want to leave?";
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The JSInterop code is implemented as a method in &lt;strong&gt;RouteSessionService&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private bool _ExitShowState { get; set; }
public void SetPageExitCheck(bool show)
  {
    if (show != _ExitShowState) _js.InvokeAsync&amp;lt;bool&amp;gt;("cec_setEditorExitCheck", show);
   _ExitShowState = show;
  }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Add the following script reference to the _Host.html next to the blazor.server.js reference.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script src="_content/CEC.Routing/cec.routing.js"&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;In the CEC.Routing sample &lt;strong&gt;WeatherForcastEditor&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;protected void CheckClean(bool setclean = false)
{
    if (setclean) this.IsClean = true;
    if (this.IsClean)
    {
      this.Alert.ClearAlert();
      this.RouterSessionService.SetPageExitCheck(false);
    }
    else
    {
      this.Alert.SetAlert("Forecast Changed", Alert.AlertWarning);
      this.RouterSessionService.SetPageExitCheck(true);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This method is called by the &lt;em&gt;OnFieldChanged&lt;/em&gt; event handler, and the &lt;em&gt;Save&lt;/em&gt; and &lt;em&gt;ConfirmExit&lt;/em&gt; methods.&lt;/p&gt;

</description>
      <category>aspnetcore</category>
      <category>blazor</category>
      <category>routing</category>
      <category>navigation</category>
    </item>
  </channel>
</rss>
