Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Bo Jacobsen 606 posts 2404 karma points
    May 21, 2019 @ 11:18
    Bo Jacobsen
    0

    How to work with DI in .cshtml razor views

    Hi all.

    We are trying to migrate from Umbraco 7 to Umbraco 8. And in the process we learned about DI and IoC, and tried to convert it to DI. But we came across an issue we dunno how to handle correctly.

    Let's say, we have a layout .cshtml file for all our views. So the layout file do not have a controller assosiated and we wanna use an extension, that use code we already have in some services. What to do?

    @using {projectname}.CustomExtention
    @inherits Umbraco.Web.Mvc.UmbracoViewPage
    @{
        Layout = null;
    }
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Umbraco Rocks!</title>
        @Html.CustomFunction(Model.Id)
    </head>
    <body>
        @RenderBody()
    </body>
    </html>
    

    We made an Extension for the HtmlHelper to render some scripts. Inside this extension we wanna be able to use 2 custom services.

    namespace {projectname}.CustomExtention
    {
        public static class IPublishedContentExtension
        {
    
            // IUmbracoHelperService
            // IHostingEnvironmentService
    
            public static MvcHtmlString CustomFunction(this HtmlHelper helper, int id)
            {
                // Just for Fiction
                var content = UmbracoHelperService.TypedContent(id);
                var path = HostingEnvironmentService.MapPath(content.Url);
    
                ...
                return new MvcHtmlString(stylesheets);
            }
        }
    }
    

    We already create IHostingEnvironmentService, HostingEnvironmentService, to use in some components and controllers, so we are able to use the logger.

    public interface IHostingEnvironmentService
    {
        DirectoryInfo GetDirectory(string pathAndName);
    }
    
    public class HostingEnvironmentService : IHostingEnvironmentService
    {
        private readonly ILogger _logger;
    
        public HostingEnvironmentService(ILogger logger)
        {
            _logger = logger;
        }
    
        public string MapPath(string pathAndName, bool createIfNotExists = false)
        {
            try
            {
                ...
            }
            catch (Exception ex)
            {
                _logger.Error<HostingEnvironmentService>("Bla bla bla", ex);
            }
        }
    }
    

    We also created IUmbracoHelperService, UmbracoHelperService. To use in some components and controllers, so we were able to use the IUmbracoContextFactory.

    public interface IUmbracoHelperService
    {
        IPublishedContent TypedContent(int id);
    }
    
    public class UmbracoHelperService : IUmbracoHelperService
    {
        private readonly IUmbracoContextFactory _context;
    
        public UmbracoHelperService(IUmbracoContextFactory context)
        {
            _context = context;
        }
    
        public IPublishedContent TypedContent(int id)
        {
            using (var cref = _context.EnsureUmbracoContext())
            {
                ...
            }
        }
    }
    

    We already install these in an IUserComposer

    public class Installer : IUserComposer
    {
        public void Compose(Composition composition)
        {
            composition.Register<IHostingEnvironmentService, HostingEnvironmentService>();
            composition.Register<IUmbracoHelperService, UmbracoHelperService>();
        }
    }
    

    But are we able to use it in our CustomExtention or what would be best practice?

  • Marc Goodson 2155 posts 14408 karma points MVP 9x c-trib
    May 23, 2019 @ 08:14
    Marc Goodson
    100

    Hi Bo

    What I tend to do is create my own version of UmbracoViewPage

    There is some WIP documentation in progress here:

    https://github.com/umbraco/UmbracoDocs/compare/v8/ServicesPattern?short_path=cf2726d#diff-cf2726d2ea2b6f60e0225ce051876a55

    that has the following example of how to do this:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using Umbraco.Core;
    using Umbraco.Core.Cache;
    using Umbraco.Core.Composing;
    using Umbraco.Core.Services;
    using Umbraco.Web.Mvc;
    using Umbraco8.Services;
    using Current = Umbraco.Web.Composing.Current;
    
    namespace Umbraco8.ViewPages
    {
        public abstract class CustomViewPage<T> : UmbracoViewPage<T>
        {
            public readonly ISiteService SiteService;
            public CustomViewPage() : this(
                Current.Factory.GetInstance<ISiteService>(), 
                Current.Factory.GetInstance<ServiceContext>(),
                Current.Factory.GetInstance<AppCaches>()
                )
            {
            }
            public CustomViewPage(ISiteService siteService, ServiceContext services, AppCaches appCaches)
            {
                SiteService = siteService;
                Services = services;
                AppCaches = appCaches;
            }
    
            protected override void InitializePage()
            {
                base.InitializePage();
            }
        }
        public abstract class CustomViewPage : UmbracoViewPage
        {
            public readonly ISiteService _siteService;
            public CustomViewPage() : this(
                Current.Factory.GetInstance<ISiteService>(),
                Current.Factory.GetInstance<ServiceContext>(),
                Current.Factory.GetInstance<AppCaches>()
                )
            { }
    
                public CustomViewPage(ISiteService siteService, ServiceContext services, AppCaches appCaches)
            {
                _siteService = siteService;
                Services = services;
                AppCaches = appCaches;
            }
    
            protected override void InitializePage()
            {
                base.InitializePage();
            }
        }
    }
    

    and then in your view

    @using Umbraco8.ViewPages
    @inherits CustomViewPage
    @{
    
        Layout = "master.cshtml";
        IPublishedContent newsSection = SiteService.GetNewsSection();
    }
    <section class="section">
        <div class="container">
            <article>
    

    this might just be personal preference, but the outcome is that in the view there isn't anything 'getting' services etc - in current project I'm using this instead of the HtmlHelper ExtensionMethods... I'd used in V7.

    Anyway the key to it is anywhere you can get the current reference from a service that's registered with DI by using:

    using Current = Umbraco.Web.Composing.Current;
     Current.Factory.GetInstance<ISiteService>()
    

    (if you can't inject it via a constructor)

    Not sure if that answers your question though!

    regards

    Marc

  • Bo Jacobsen 606 posts 2404 karma points
    Jun 13, 2019 @ 12:43
    Bo Jacobsen
    1

    Hi Marc.

    Thats exactly what we gonna do.

    Thanks.

Please Sign in or register to post replies

Write your reply to:

Draft