There’s a phrase about ‘famous last words’ I think. 6 months ago I mentioned I was aiming to have my site rebuild of an Umbraco 7 site brought into Umbraco 9 largely complete by the time of the release candidate. Well ignoring the fact they’d actually released the RC a day before that blog post and I’d just missed it, as is often the case in development the whole project took me much longer than I’d intended. I’d totally forgotten just how much other background code I have in this project, and despite planning on mostly lifting the existing front end code I decided to iron out lots of little niggling issues and improve some long running accessibility issues for all users. But despite the long haul this site finally went live on Umbraco 9.2 in January 2022. There’s still a few bits and pieces I need to tweak or tidy, but with the core site being up now I thought it worth taking a quick roundup of things found in the months since incase it helps anyone else doing this sort of an upgrade.
When bringing content from old versions of Umbraco, make sure your pickers have actually migrated properly.
In the first parts of this blog series one of the first big jobs I talked about was converting picker data, as pre-7.6 versions of Umbraco stored nodes as integers internally rather than the UDIs (effectively guids) used in later versions. I’d originally used the Our.Umbraco.Migration package to do this and it all appeared to have worked smoothly with all the pickers switching over to their new types as expected. Well as I was building out views, it turned out it had only migrated some of the data inside, for reasons I’ve not been able to get to the bottom of. This didn’t show up in Umbraco 8 immediately as 8 seems to still support the integers being mixed in for legacy reasons. However it quickly started to throw up some interesting errors in Umbraco 9 back office about deserialization as v9 seems a bit more sensitive to data being in only one format. In the end whatever the problem was though I had to re-run the same Our.Umbraco.Migration task again anyway due to more content being added over time, and this second time all the same code migrated everything perfectly fine. But if it helps save other people even a bit of time my best advice is once you get the data into Umbraco 8 before going to 9, switch a few of your pickers (single and multi-node) to ‘labels’ in Umbraco. This is non-destructive and allows you to easily see the stored underlying data so you can confirm there are actually UDIs in a good enough number of places. Only once you’re happy with that you can switch it back to the proper pickers and continue the Umbraco 9 upgrade.
The pain you may get if you don’t watch your picker data…
Relations are great
Oddly this isn’t a feature I’ve seen people make a big deal of, but for some time now data about relationships back from any documents selected in content pickers to the source picker has been stored by default. In Umbraco 7 I have huge numbers of nodes which I needed to relate back to a parent page, and was doing this by building up a huge lookup table of child IDs to pages on startup. In Umbraco 8 and 9 you can inject an ‘IRelationService’ into the constructor (or if you do want to use it in a view, which some purists will hate, you can use the @inject line with a view too in .net core), chuck the child nod ID into the cunningly named ‘GetByChildID(x)’ method and get back the parent page quite easily, and importantly it’s still blazingly fast even on a huge content tree of thousands of nodes like mine. I’m actually using this approach on the frontend, though it’s worth noting there are always warnings in the docs about using some of these services on the frontend without being very careful around caching. To mitigate this, I am using output cache on any views which actually call them so it wouldn’t be accessed every time, but even when they weren’t coming from the cache the relation service seemed very fast.
Performance is faaaaast
Which brings me nicely on to this point actually. How much difference in performance might be noticed from 8 to 9 is harder for me to say, but from 7 to 9 it’s night and day. Using the back office on huge collections of nodes and images is just a joy, though you should probably still practice better content housekeeping than I have in some places to avoid this problem in the first place. On the frontend in the Umbraco 7 world, I actually used to build up a massive in-memory cache manually on startup. Partly to store the parent page relationships mentioned above, partly to store quicker image ID > url lookups, and partly to store a list of the latest 100 nodes for purposes elsewhere in the site. This manual cache load took 2-3 minutes to build every time I had to restart the site (or something crashed) as it was doing a lot of iterating, but meant it could run fast the rest of the time after that. In Umbraco 9 (and indeed this would have worked in 8) performance of the built in nucache and relations is so fast, I’ve dispensed with all of this manual work (yay no 2 minute restart!). Direct Linq querying is just being used throughout. In most cases these queries are called from inside partial views which all have an Output Cache on of between an minute and an hour (very easy to do just by loading your partial views in with an Html.CachedPartial call). But even with the outputcache disabled by setting debug=true in appsettings.json, the native lookups would barely register any processor load in my testing.
CachedPartials are your friend… just remember to test with the cache turned on!
Usync can allow you to fix so many bad doctype architecture decisions.
Projects change and decisions made on doctypes (or content types if you prefer the other terminology) which seemed sensible when made at the start can later become a problem you seem stuck with. In the Umbraco 7 site over time I’d ended up with two types of node using a lot of the same fields on each doc, one for ‘Video Clip’ and one for ‘Teletext Collection’. As I wasn’t using strong models in Umbraco 7 this was less of an issue as the field name would be the same in the view anyway via the ‘GetPropertyValue(“x”)’ approach. But when deciding to move to strongly typed models in Umbraco 9, having these common fields in a composition you could inherit from becomes much more important as it allows you to use the base type instead of having loads of if statements or having to revert to dynamics. Making those sorts of changes on an empty system is easy, but on one with a huge amount of existing content in, less so… or in theory. Here’s where the excellent uSync from Kevin Jump comes into play. Set up your new compositions, albeit deliberately left largely devoid of many fields for now as you mainly want these for the skeleton structure, and then set the derived types to inherit from them. Then export all the current doctypes and content/media using uSync’s dashboard. This will give you a huge number of .config files in the uSync > v9 > ContentTypes folder. You can manually copy/paste the individual field definitions inside of these files around and into the composition types to have them exactly where you want and then do a full reimport of settings in back office. This will move your fields round, though will initially wipe out all the content too of course… but then uSync has you covered there too. As the uSync content format only stores the name of the field on each item and not any more specific identifiers, the fact you’ve moved that field from the derived document to a composition makes no difference to uSync – it’ll match it back up in the new place and replace the content when you do a content reimport. The only minor issue I ran into was having originally exported and refactored a lot of my doctypes from one of the earlier Umbraco 9 releases, by the time I was bringing this back into 9.2 they’d added tabs in to back office and this won’t import if missing extra tab data from your uSync .config files. But it was easily fixable by adding in some alias lines to the groups, and wouldn’t be an issue for most people now who are unlikely to hit these ‘inbetween’ beta and RC versions of 9.
EntityFramework does not lazy load by default!
This isn’t actually an Umbraco thing, but for those used to .net Framework more than .net Core could save some head scratching. I had to bring in EntityFramework Core to this project, due to some additional database tables I need to use that wasn’t possible with the built in NPoco stuff. In the .net Framework world, when you issue a Linq query against EF, related tables are lazy loaded in by default when you first access them. In EF Core, this doesn’t happen unless you explicitely tell it to. And you won’t get any warning of this, instead you’ll just get empty related collections. To do this, you either need to Eager Load each ‘table’ in the query with ‘Include’ by doing a query like:-
entities.News.Include(news => news.Categories)
Or in startup.cs you can get similar lazy loading behaviour to prior .net versions by adding the Microsoft.EntityFrameworkCore.Proxies package via nuget then including a line in startup similar to :-
services.AddDbContext(options => options.UseLazyLoadingProxies().UseSqlServer(connection))
Both are these approaches are detailed more on this Microsoft page.
ADDENDUM (22/1) – Watch your page names
One extra one I forgot to mention earlier, but probably useful as it can cause some changes to your page URLs if not careful. In Umbraco 7, I had a few pages named with a year on the end in brackets, along the lines of ‘Page Name (2020)’, which would then have been published on a URL of page-name-2020 as Umbraco strips brackets from the URL slugs. At some point in the releases since, a change seems to have come about with the naming behaviour. Documents named like this will initially appear to port over fine to 8 and 9 with the same name. However the next time the document is published on 9 the year part seems to be interpreted as part of the duplicate name handling (where you’d normally see (1) added to the end). As a result on publish your document may get renamed ‘Page Name (2020) (1)’ and your URL now changes to page-name-2020-1. The easy solution in my case was to just remove the brackets from the original name manually and republish, so I ended up with ‘Page Name 2020’ which also produces the same original URL slug.
Overall it’s a been a long trek getting to this point, but definitely one I’d recommend. The performance improvements alone in going from Umbraco 7 to Umbraco 9 are immense (and 7 wasn’t exactly slow). But in addition to that, I have picked so many little useful tips and tricks around changes to the newer ways of doing things in the process. Many of these actually applied in Umbraco 8 too so can be backported on sites there too. And if you were already using a lot of those on an Umbraco 8 build then any upgrade from that should be much more straightforward.
For those who want to see the site involved, it is live here. Though I must caution you I am a developer and not a designer! 😉