:::: MENU ::::

Saturday, February 10, 2018

I feel like the job of software developer in the last 20 years has been to decouple. Whether it's dependency injection or building modular systems, or even the new trend of micro-services; coupling has been the killer of everything good in software development (maybe).

In many small ways, I find that trying to fit in small disconnected sets of functionality into the ASP.NET MVC Controller to View mechanism can be overwhelming. In some cases I'll need something that is completely separate from the logic of the controller. Luckily ASP.NET Core comes to the rescue.

The ServiceContainer in ASP.NET Core is great, but we tend to worry about constructor injection in most cases. But once I realized I could inject directly into Razor, I realized I had a good solution for my needs.

I've been hesitant to explain this too much as it has the capability to be abused, so please don't assume everything goes into individual services and razor-injection.

So let's see how a really simple case can work. In order to help pay for my blog, I've decided to have some small inline ads. It's not a lot of money, but its enough to make a difference. To do this, I created a simple service that allows me to inject these ads into a Razor page:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  public class AdService
  {
    private IConfiguration _config;
 
    public AdService(IConfiguration config)
    {
      _config = config;
    }
 
    public HtmlString InlineAdd()
    {
      return new HtmlString($@"<div>
  <!-- Inline Links -->
  <ins class=""adsbygoogle""
       style=""display:inline-block;width:468px;height:15px""
       data-ad-client=""{_config["AdService:Client"]}""
       data-ad-slot=""{_config["AdService:Slot"]}""></ins>
</div>");
    }
  }

And because I needed to use it, I registered it with the service container:

1
svcs.AddTransient<AdService>();

So I could have just gone to the controller and injected it into the constructor:

1
2
3
4
5
6
public RootController(IMailService mailService,
                      IWilderRepository repo,
                      IMemoryCache memoryCache,
                      ILogger<RootController> logger,
                      AdService adService) // -- Ick
{

But then I'd need to pass it down to each view. It felt icky. But in ASP.NET Core, you can add a dependency to the Razor code itself. You do this by using the @inject command in Razor:

1
2
// Razor Page
@inject WilderBlog.Services.AdService ads

Once you have that, the service container will inject that into the page (as a variable called 'ads' in this case). Then you can just use the instance (instead of relying on extension methods or static classes):

1
2
3
<hr />
@ads.InlineAdd()
<hr />

Simple, really? But you might be taking the next stop to assuming that you could just do that with a DbContext class and skip the controller complete. You could, but it makes testing impossible. Please don't do that. This works because the ad in my case is completely separate from the job of the page itself. I want to have the Controller to test...and even test the AdService, but once I inject real logic into the Razor page, you're done for (IMO).

More