View transcript
I can't really see the notes. Check. Go. Okay. So, welcome, ladies and gentlemen. This is the Je suis Core session. And the reason for that session is mostly because of all the times we go on hour and sessions, people saying, I'm trying to do this, and I have built my own cache, and I'm trying to pass this and that, and we're like, we've already done it. It's all in the core. So usually the question is, where is the doc? There is no documentation. None. So there are a lot of things in core that are quite interesting that we love and we're very proud of, but nobody knows about it. So the idea today is to present as many things as we can, so it's going to be quite fast, giving hints. So just you know, oh, if I need to do this, there's a lot of things. But it's already in core. So we're not going into details for everything, but just giving you an overview. The way we're going to do this is with Demo Roulette. It's never been done before. It's a proprietary software made from the ground up from scratch yesterday. Yeah. That patent pending. So can't copy this. And we'll explain a bit about how this works in a second. You might see this screen a few times. Yeah, we there will be codes. There will be a lot of weird things being done. So if you've been hurt by bugs in your childhood and you're sensitive to that type of things, it's a trigger warning. You want to leave the room now because. Okay, so shall we launch the roulette? Yeah, absolutely. Okay. So the roulette is there. Here is the roulette. So the first topic today will be. Shannon is going to talk to you about caching. So what we haven't figured out is how to get that to show the slide when it stops. But this is caching. Mostly I'm going to just introduce this slide deck and then pass it over to Stephen. Caching. Okay. So you guys have all done caching and everyone implements their own stuff. So you have HDB runtime cache. You have your own static caches. You got all sorts of different caches. But in the core, we do it one way and we do it one way every time. And there's probably a few cache types in the core that you might not be aware of that you can absolutely use and you should absolutely use it because there's a lot of really handy methods in there to make sure that you're not re-implementing the same thing that we do over and over and over again. Okay. You want me to keep going? Oh, okay. What does dry stand for again? Sorry? Dry? No, no, no. Do it. Dry is don't repeat yourself. So stop reinventing cache all the time. There is something which is called the application context current application cache. And it's a cache helper that we use everywhere in core. And it's giving you access to cool methods to do cache. When you cache, you have to manage locks, make sure you have to manage expiration, et cetera, et cetera. How do you do that? Well, we've done it already. The cache helper actually contains four types of cache. The first one being static cache. Static cache meaning doesn't expire. If you put it in there, it will never go away. So it's a giant memory leak if you want it to be. Don't make it that thing. Don't use this unless you really know what you're doing. But it can come in handy. If you know that the application needs something forever and you never want it to go away, you can use the static cache. Here's an example of using it. For all the cache helper methods, we just say get cache item and we pass in a delegate or lambda. So if that cache item doesn't exist already, it will call that callback to pause the cache. And then we have a more clever cache, which is the runtime cache, which is practically the same, but it is based on top of the ASP.NET runtime cache. So it knows how to do expiration, sliding expiration, dependencies. You can set a callback to be notified when the item gets removed from the cache. So that's probably the one you want to use when you want a long cache to be able to run. So you can set a callback for something like you want to cache, I don't know, a bit of data for 10 minutes. You put in there the time span from minutes 10 through if you want it to be sliding. And it will be there for 10 minutes. And after 10 minutes, it will be recreated automatically. And it is all managing the locks and the issues that you don't want to hear about. And it works. Great. I think we need to speed this up. It's supposed to be four minutes per section at most. Otherwise, we'll never get through this. We have request cache. It's as simple as chucking something into http context.current.items. You don't want to use singletons. You don't actually know what the http context is. You can access sort of abstracted request cache this way. It just puts something into the request for the lifetime of the request. And then when the request ends, it goes away. Easy. And the last one, we're going very fast over that one, is the isolated cache. The only reason for that one is to put certain types of objects. If you put everything in the same cache and you want to remove all objects of a certain type, we're going to look for the keys comparing strings to remove them. And it's expensive. So if you have a lot of objects of a given type, like all our repositories, we use the isolated cache to make sure that it only contains objects of one type. So you can clear the cache and get rid of them all at once. And it's cheap. So just reiterate on that. That's because if you put everything in runtime cache, it's a giant dictionary. Let's say you had 100,000 items in there and you wanted to clear something that starts with a certain word. Well, it has to actually check every key. That's 100,000 keys to check. It's inefficient. So it's an isolated cache. It's its own dictionary. Well, I'm pretty lucky. Again, passing it over to Stefan. Oh, you need to press the secret button. Yeah. Okay. Okay. Strings. Not that type of strings. All the strings that we manipulate in a normal website. And all the type of things that we keep doing and redoing. Like this thing. I don't know how many of you keep writing that type of things. I convert everything to uppercase and then I compare because it's... I want to be sure that it's case insensitive. Or maybe I do everything to upper or I do the big thing equals with a string comparison thing. This is complicated. In core, you have extensions methods such as invariant equals. So it will compare using the best and fastest invariant way. So this is a very simple thing. But you can use it everywhere. And it's free and it works. We have tons of extensions. So if you do string and a dot, you will have them in IntelliSense. You can do invariant equals, invariant starts with, end with. You can encrypt and decrypt using the machine key. You can convert it to JavaScript, to base 64, to MD5. You can replace many tokens in the same string. So you pass an array of start to, start to, start to. And it will replace. They are all in core. And it's the type of things that you usually recreate in every project. And they're basically all unit tested as well. And they are all unit tested so we know that they work. I think there's like a hundred of them. There's tons. Have a look. So it's safe. The next one is cleaning strings. So you probably always do that. Like you upload a file and you want to be sure that the name is safe. So it can be part of a URL. Or it can be part of a file on disk. So it doesn't contain stars or things like that. That would be legal in Windows. So we have the toSafe file name method that will convert your file name into something that is universal and safe. We have the toSafe alias. Which is going to transform your string into some sort of alias that is safe for JavaScript. And for C sharp. So it could be used as a token in JavaScript. Or C sharp. It's safe for URLs. It's safe for almost everything. And we do it once with very efficient methods. So it's better than recreate your own parsing. A few years ago we had, I don't know, probably ten different ways of doing that type of things in Umbraco. Everywhere in the core. Each time we're cleaning up files. So it's all done in one place. Which again is unit tested a lot, a lot, a lot. So you know you can trust it. And it all works. It all works by using the last one. Which is toCleanString. Which uses a clean string type. And it can do very clever things. So it can convert a string to Pascal case, camel case, lower case, upper case. It can do encoding changes. So you can convert UTF-8 to ASCII. And we have a huge table of characters. So if you have a funny Danish O with a slash it will translate that into an O. All that type of things. It will create a U with the umlauts. It will create a U normal. So for those of you who used to add the bigger, the many lines in Umbraco settings. I replaced this by that. It's all built in. And so it knows how to create URL segments, alias, etc. So just look at that method because it's... Okay, we don't have time for that. Okay. Anyway. Look at that method because the whole logic of cutting the string into tokens. Then changing the case. Rebuilding a nice alias. It's all in there already. Unit tested. I'll do this one. You want that one? Okay. Why not? We can't cheat all the time. I need to find it. We need to have hold music for this part. Where is it? Profiling. Go. Profiling. So this is... Built into the core. Now you can do all sorts of things with profiling. I know a lot of people do their own methodologies for profiling. We have something called mini profiler built into the core. It's also abstracted. If you really wanted, you could replace it. I don't know why you would, but it's possible. So this is, you know, timing your code. But mini profiler built into the core also times your view rendering times, times your SQL statement times. It also profiles your SQL statements to see if you have duplicate SQL statements executing and how long they take. That's all in the core for free. Now, if you want to add your own profiling to your own code, which will show up in mini profiler, you can use something called the profiling logger. This is part of the application context. And you just say debug duration. So there's also methods like, I think, trace duration, which will go to the info. Okay. So things like that. So you say do some work. That's going to be the start string. And then done will be the end string. And it will put how long it took in there. Now, if you want to render the profiler in your view, I'd like to note that this is already done for you. And we'll do a quick demo after this. But if you wanted to render it in your view permanently, there's an HTML helper for that, render profiler, which will then end up looking something like this in your view. And if your profiling time took longer than the minimum amount of time in the threshold, it will show up there. So that's really cool. That's all built into the core. The other cool thing about that using statement is it will show up in your log output. So if you say debug duration or trace duration, this is the sort of thing you'll get in your logs. So you can easily profile your code. It works really well. And this will be our first quick demo. We've not done this before. Let's see what happens. Yeah. Hey. All right. So this is, I'm just going to bump the web config so you can see it start up from scratch. So this is really cool. If you just put that in your query strings, it will show up in your log output. It's also required that you have debug equals true in your web.config. If you don't have that, this isn't going to do anything. But if you put this in your query string and then refresh, you get the mini profiler showing for free. So that's really cool. And you don't have to do that render profiler thing because 95% of you will probably be using Umbraco view page for your MVC pages, and that just does it for free. So here you can see what happened during startup. And it gives you all the profiling information. I would like to just reiterate on this. This doesn't actually time the entire startup process yet. But in 7.6, it will. So this will give you the time to first byte type thing from start to finish on the client side. But it won't actually output all the durations of the actual startup process. That's the end of the demo. Yep. Right on time. God. You did hack this, so it's just me, right? No. I tried to find a way to hack it but did not. Want me to do that one? Yep. I'm going to do that one. So it's a quick one. And it's near the end, I think. You know, we'll have to skip it. Ah, I heard it. We didn't think this through. Okay. Concurrency. This is about doing concurrent things in core. This is really core, not very useful probably in your code, but it's good news if you're creating websites. At the moment, the way we do concurrency is by hoping that everything is all right. So if you have two editors saving at the same time, with a bit of luck, one will save and then the other. So we're safe. Then what was introduced after a while is a C-sharp level lock. So there would be a lock at the core application on one instance, making sure that no two editors can do it at the same time. This is fine if you only have one server, but as soon as you do load balancing, you can have editors on two servers modifying things at the same time. And it can become... Which is why you can only have one master server. Which is why in the load balancing scenario today, we said we don't support if the back office runs on every server. So we were looking at what is shared... That's just one of the reasons. Sorry? I said that's just one of the reasons. Yeah, one of the reasons. So what's shared by all editors? So in all instances, the database. So we're moving to using a new table, which is called umbraco-lock, and we are locking rows in that table within repeatable read transactions. Within that type of transaction, if you read something, you get a shared lock on that thing, and nobody else can write to it. And if you write to that row, you get a write lock on that row, and nobody else can write nor read to it unless you've committed the transaction. So slowly in core, we are moving everything to be protected by that type of locks. At the moment, it's coarse-grained, so we are locking the content tree, or the content types, all of them. But at least you're sure that you can have collisions and weird things happening, so it's much safer. And as time goes by, we will do finer-grained things. And it's worth noting that this is mostly in V8, right? This will be in V8. It's not V7. Because it needs to be tested. We can't just release that and pray that it works. So this is the type of scenario you will see in V8 code, where we create a unit of work, which is our equivalent of a transaction, and we say, please, read lock the content tree. So make sure that no one else can write to the content tree, because maybe I want to read all the content items, and I want to be sure that no one else is, I don't know, removing things while I'm reading them. And in the end, you complete to say, commit, done. You probably never have seen any lock-related issues. Though maybe one day you had a weird corruption in the database, and you were like, I don't know where it comes from. It can come from that type of... And can you reproduce it? Well, the hard part is that it's very hard to reproduce. It's... no. Yep. So you just blame the Internet and move on. Yeah. Oh! That one is... I propose we do that one last, because it may take a bit of time. So let's see. Okay. What else do we have? Oh! Ooh, this one's a great one. Yep. You wanted that one. Uh-oh. I know you have slides. There's some slides for that. Oh. I don't know. It might just be one. I don't know. Hold on. You go on PowerPoint. You drive. Oh. That's... Good. So, LinkPad. LinkPad's an awesome tool. Some of you are probably already aware that we made a LinkPad driver for Umbraco quite some time ago. I haven't really worked on it too much, but it kind of just works. You don't really need to do too much more with it. I will talk about the next step of it someday when I get this magical thing called time. But let's do a quick demo of it. So, what can it do? If you ever wanted to query Umbraco outside of the web, maybe console app style type thing, or maybe you just want to run some scripts against Umbraco, the thing to realize, though, is that this runs outside of the website. So, it's not really good for anything to do with web-based operations like publishing. The cool thing, though, is in 7.3+, we got this instructions table, which is how we do flexible load balancing. And this is fully untested, but I think it would work. I think you could actually use the sort of console of LinkPad to publish documents. Maybe one of you guys can try? I'm not sure. But if you want to check out the LinkPad provider, it's on my GitHub under Umbraco LinkPad provider. And you can just download the actual thing from the releases tab there. So, you want to download the LPX file. It could be IPX. I'm not sure. I think it's LPX. And I'll just do a quick demo. All right. Let's head on over to LinkPad. We'll do this. What am I looking for? View more drivers. I'll browse. There it is. I'll install it. Cool. Everything's good. Umbraco driver. That's pretty good. What does it want? All it wants is the installation path of your website. CG16. Let's use this website. So, this is just the root folder of a website. Okay, cool. Wait till that to load up. And now we have LinkPad. So, this is going to list, currently, just this content and media. And if I expand these, we'll get content and media types. So, let's see if I want to just query all of the text pages on the site. Fano's installed. So, that'll just go get all your data. That's pretty cool. Super easy. I mean, I can go, like, skip take and do all the cool, fun link commands. I also have a couple other things to show you. So, I wanted to just use the services. I mean, this is just using the things in the tree. And that's really cool. It means I can query against specific content of specific content types. I could probably do joins on them, too, but it would all be in memory. But if I actually just want to do some scripting, sure. Why not? I mean, this gives you direct access to the application context, which you have access to in everything. So, I can use a content service. I can use a media service. I can do anything that you can do. I can do scripting against the application context. It's just fully exposed. Super awesome. Let's give you another demonstration here. What if I wanted to create some content or something? A famous one is members. Well, how do I know if Umbraco works with a million and a half members? How am I going to go creating a million and a half members? Well, you can use this. I'm not going to say it's going to take two seconds. It's going to take a little bit of time. But I can show you how long it is. It's going to take, create 10 members. So, here's the code. I'll change this to a statement. Create 10 members. I'm going to get the default member type. I'm going to write some logging information. I'll iterate over 0 to 10. I'm going to generate a name. And then I'm just going to call the normal Umbraco services that you guys know from LinkPad. And then just log it. There you go. Cool. We just made 10 members outside of Umbraco and LinkPad. Super awesome. Woo. All right. I would like to point out that... Oh, sorry. I think that might be the next slide, but I'm not sure. iQueryable is not currently supported in this provider, but it's in that branch. I started working on it. And it would be very cool to make an iQueryable provider against the things in the chat that are in the tree so that you're not returning everything in memory. It's not going to make much difference to you unless you're really using LinkPad for all it's worth. But for scripting and stuff like that, amazing. It's you again. Oh, what do I got? Ooh. Okay. I can't do that one. Cool. I've done this demo a couple of times. I think I failed a couple of times at it too, but we'll get it right this time, hopefully. So OAuth and sort of authentication. Since 7.3, we have ASP.NET Identity in there for the users. And you get all the things you get with ASP.NET Identity. You can add OAuth providers. You can do custom authentication. You can do all the things that Microsoft say you can do on the front end. You can also do that on the back end. During startup, you can replace our back office. User manager, store, anything that you wanted if you really wanted to do that. So with that, what is OAuth? The easiest way to explain it is when you go to any of those sites that say log in with Facebook or Google or, you know, that's OAuth. So it's kind of the new single sign-on, right? Single sign-on isn't really a thing because you need everything under the same domain, but this is as close as you get to real single sign-on. So you have a thing. You have a third-party hosted authentication system outside of your service. And we support that in Umbraco as well. If you want two-factor authentication, currently in Umbraco we don't have that built in. You could implement it by extending the services, but you could also say if you wanted to use a Google provider or an AD provider or something that supports two-factor auth, then you just implement the OAuth provider and that does the two-factor auth for you. And then Umbraco is not responsible for it. So how do I get this? Well, there's a few packages. I'll show you a demo of the Google one, but install identity extensions. It's on NuGet. That'll install a bunch of README files, some documentation to get you started. This isn't on the docs. Docs, docs. There's docs telling you just to install this package and then read the READMEs. But it installs a bunch of snippets and scripts to get you started because that's probably the easiest way if you want to get your hands dirty. We have then some other helper methods for just the standard OAuth providers like Facebook, Google, Microsoft. And then there's Azure AD provider as well. So we'll do a quick demo with the Google provider. Okay. So we'll go to the back office. It looks normal, right? Now I've already gone ahead and installed those two NuGet packages. And in the READMEs, it tells you that you need to modify some OWIN stuff. Also in the READMEs, it gives you links to learn about OWIN stuff. It's very hard to explain. I've got some messages coming up. Cool. OWIN is kind of the startup heart and soul of ASP.NET Identity, a lot of other things, and even ASP.NET Core. If you saw the previous session, OWIN is sort of how ASP.NET Core came to be. So anyway, in Embraco, we have a default OWIN startup. You'll see this key in your web config. We're just going to replace that with this. This is a custom one. So you can see standard OWIN startup. Identity Extensions actually installed this for you. So it's just here. And it's got a whole bunch of comments and documentation on how to get started. But what I've gone ahead and done is I've just implemented this Google auth provider. Now go ahead and copy these keys because I'm going to delete them in like two minutes. But go ahead if you want. It's fine. Remember my password is testtest. So, yeah. So we're going to configure this and save that. There you go. That's pretty cool. Automatic. Now you can customize the color and the icon and do all sorts of fun things with these things. So if I click on this, it's going to do the standard Google thing that you know. Yeah, I'll allow that. Awesome. The thing is, Google is a public provider, right? You don't want anyone with a Google account to be able to log in. You want to log into your back office. That would be insane. If you have a custom AD provider, we have this special thing on that startup script to say auto link account. And then you could do that. But if you enable that for the Google one, that means everyone on Google has access to your back office. That's not cool. So I'm going to log in. But let's say I did have a Google one. Maybe it's Google under your own domain. That makes the most sense. Or maybe I just want to enable people to log in with their own domain. Maybe I want people to log in with their Google accounts, but I don't want to auto link them. That's fine. That's why this is here. So I can say, well, next time I just want to use my Google account. So I'll link it. And everything's good. So I can unlink it there. I can log out. And just to prove that it works, again, log in with Google. And I'm logged in because I've linked it. So it's super easy. Awesome. OAuth might not be easy, depending on your provider. But we've sort of given you the utilities to just enable them in the back office. And all the UI magic kind of happens for you. Was there anything else? No. No. That's it. Cool. Good question. Yeah. Have you had any plans of ? Yep. Good question. I think there was more docs. Or more slides. So let's continue with it. Yeah. I'll get to you in a sec. So Identity Server is another cool third party auth provider. We're not going to go through a whole demo of that yet. But if you wanted to see how to integrate that sort of OAuth flow with Umbraco back office and Identity Server, I have sort of a test case demo thing up on GitHub as well. But you can go have a look. In theory, if you run it, it should actually install Identity Server, get it all up and running with test data, and then also launch Umbraco with all the providers built in. So it's kind of like a Control-F5 when you load. And it should launch three websites. And you can go from there. At least it's got snippets to get you started with if you want Identity Server implementation. So the custom authenticator. What if I don't want this OAuth thing? What if I have a custom Active Directory that I just want to use? What if I want to authenticate against it? It doesn't have public APIs and it's old or whatever. That's cool. We use this thing called iBackofficeUserPasswordChecker. You can implement this. It's got one method. I'm not going to go into details about this. The method is just CheckPasswordAsync. It does mean that your user has to exist in the back office already. Or you can provide a callback to auto link it. And you register this on startup. It is documented. So if you're interested in that, you can go ahead and do that. That's where you find it. To answer your question, yes. We do plan on, say, having only OAuth providers listed. If that's sort of what you're getting at. I don't want any local login. I just want, say, Identity Server. So you can have a flag to hide all of that. It just won't show. And you can only login with a third party OAuth service. Is that sort of answering your question? Cool. Perfect. All right. Yes. Okay. There we are. We only have 15 minutes. Okay. I'm going full speed. So on record trust log, you all know that file. Though we see some people on our reporting arrows. And we tell them, go look at the log file and what it is. So it's in AppData.org. Logs. Umbraco. TraceLog. And it contains everything that happens in Umbraco. It has this weird format. So just if somebody has wondered, just to explain what's in between brackets. The first one is the process identifier. So it's the W3WP process. So if it changes in your log, it means the entire app pool has restarted. Probably it crashed or I don't know. The second one is the app domain ID. So it's the current application domain. If it changes, it means that the application on your website has restarted. So the process is still running. But it's like if you change a file in a PPCode or you change a DLL, it restarts the app. It will recycle the number like D4. You see D6, D7, D8, et cetera, et cetera. And the last one is the thread ID. So that one is a bit random. But basically everything that belongs to a thread is probably one request being processed. So that's very useful to know what's happening on the world. And then you have, okay, debug is it is we're debugging. And then you have the rest. So usually it ships with info. So you don't see the debug thing until we tell you please activate debug. So you activate debug. And then we start writing debug statements in the log. And then your site crashes because the whole disk is used by hundreds and hundreds of lines and you don't understand what's happening. Because at the moment, the only thing we can tell you is replace info by debug if you want to see the debug info. But we are debugging quite a lot of things and it's a nightmare to understand. And that's all we told people up to today. Actually log is much clever. That part that you see, umbraco.web.routine, in fact I think I have a slide. Yeah. That part you see here is the thing that you put in the generic debug and generate something debug umbraco module or something like that. It appears in the log. So it tells you what class is currently logging. And log4net allows you to override class by class the level of logging. So you can activate debug for your entire dev site. And you're like, actually I want to know what the umbraco module is doing. So I will leave info as a default level. But for umbraco module, I will say debug because I want to understand what's happening. And it works the same in your own projects. Like if you write a small module or a small something, whatever, you can activate debug only for your own thing and not for everything. So you can see. You can see only the parts that's relevant to you. Yeah. This has been, I mean, it's been enabled in the core since four or something. Yeah. But nobody knows it. And unfortunately, I think even Neil's pointed this out to me one day. He's like, what is this weird thing in the logs? It says something about in hibernate. I'm like, well, that's kind of a demo of this, except the node above it is totally misleading and doesn't make any sense. That's why you guys actually don't know that this existed. We'll fix that. Seven, six. And this allows you, for example, if you have one of your module that is using log for net or the log helper to log, and that is doing weird things, you can activate debug for it on the production server and you're not going to generate all of Umbraco huge logs on production. Ready? Yep. Nice. Okay. That's what they came for. Okay. I need to find it. You got four minutes. Yeah. So that's why this is going to be. Okay. New cache is, I can't explain right now, so I'm just going to do a bit of teasing so you see what's possible with new cache. That's all I'm going to do because we can't go into all the details and everything. So what I'm going to do is I have a V8 website here and I have, I'm going to log into the website. It's admin at admin and admin. Okay. And I have a page that I just created which is cgroot and I'm going to preview that page. Okay. Can you read or is it too small? Maybe you can read. I take it you can read. Okay. So right now, this is the preview of this. It was instant and it was pretty fast. Okay. So I changed the title like title, title, title, and I go down here and I save the document. It has updated the preview. Instant and news. So you don't have instant preview and reliable for the entire site. Woohoo. That's cool. Okay. Let's do more. And I'm going to go to there and I'm going to change that one. Adding some content. Save. And I'm going to change the page because otherwise it would be a nightmare. It only updates if you modify the page that is being displayed. But if I refresh it, you see it has changed. And I'm going to change that one too. And I'm going to save it. And if I refresh, it has changed too. And this is pretty fast. Instant. Instant preview. Like preview you can use compared to the previous one which is preview that... You almost can't. You might want to use. And now because of that, some people say, yeah, but I'm actually building a campaign and CG2 is the thing I'm working on. And I know that my coworker Shannon is working on CG1, but I don't want to see his changes because it's... I just want to see my part. What the website looks like with my changes. And this is where we enter Visual Studio. So I have no idea what's going to happen. But anyway. This is one block. It's a bit of new cache. Okay? So I'm going to remove the comments. So it's probably ten lines of code just because I made it super verbose so that I could understand what's happening. And then I'm going to compile. Why isn't my... Okay. I succeeded. Okay. Too bad. There was supposed to be some sort of music playing, but it's not. Okay. The music is not working. Never mind. And then I need to do something before. Go. Okay. So I go back to this site. And I'm going to refresh the preview. So, of course, the website is restarting. And we get a nice error. This is because of Shannon. That's my fault. It's... The line is broken in V8 at the moment. Which is version 2. So it doesn't want to restart. So you have to press restart. And after a while, it works. It's smart. This will be fixed. Nothing to do with new cache. And as you can see here, it's not the one. The CG1 alpha is not showing anymore. So we're not showing the preview value. So we're only previewing one branch. One small branch. So it was ten lines of code. It's not supported by DL. It's not supported by new cache. But what it means is it's very easy to do. So you could think of some people, well, could I have a property on my content saying I want to preview or not? So I could preview campaigns. I have different campaigns. And I can preview the one I want to see. Everything is possible. And the code is all nice and pretty in one place. So it's code that you can look at. And like, oh, I know. I'm going to do this. And I'm going to do that. So one of the huge benefits of new cache is that the community should be able to change things. Things that are just not possible with XML before and start having these type of crazy ideas. Next. Next. Well, tell us about surfing in Australia today. But I don't want to have to, okay. Do you want to? No? Okay. It's good. Yeah. Okay, XML document. Because obviously, new cache is going to not be very XML document friendly. So that's what you use to see, mbrackwood.config with the bxml thing in it. And this is how you used to work in the past. Like, content.instance.xmlcontent, and you get the big XML document, which is the whole content. And then you could get an element by ID, et cetera, et cetera, et cetera. And then you could select a single node, and it's XPath, and get the inner text, et cetera. That was the XML document way. Trouble is, in new cache, or at least in the abstraction, there is no XML document anymore, because maybe we don't use XML. So how do you do that? You use IXpathNavigator. This is something that Microsoft has invented like 10 years ago, and it gives you a way to navigate XML. But without accessing the document. So this is the other way to do it. So you get the navigator, and you move to ID whatever, and then you move to the first child, and then you move to the next child, and you can move to the parent, and et cetera, et cetera. So you can do basically everything you did on an XML document, but it's not an XML document. The good thing is that for new cache, we have top layer on top of new cache that exposes the entire content as an IXpathNavigator. It can navigate new cache as if it were XML. And also it just execute XPath statements. Well, it turns out that Microsoft XPath wants an IXpathNavigator to work. So it means that new cache will support XPath, because we have a layer in between that will just reroute the XPath statement to a new cache, and it works. Someone asked that at the pub last night. Definitely possible. And there is, in core, something interesting, which is the navigable navigator, which is how you take objects and expose them on an IXpathNavigator, so it can be reused for other projects if you want that. It works. I like navigable navigator. Yeah. Because it navigates navigable things. Obviously what you might think is, okay, so we're adding a layer on top of that, so it's going to be slow compared to what we had before, before we were accessing the real XML document and now we are going through a layer and then we're going through objects. So we are obviously going to lose in performance. What we did is a bit of benchmarking. You can't talk while I'm talking. So because I wasn't sure, so just before yesterday I did some benchmarking. It's not scientific, but it turns out that running an XPath query on XML document is like this number, and finding the same document without using XPath and just navigating using a link, or a new implementation, it's that number. So it turns out that probably the XML document from Microsoft is a huge thing that contains many, many, many sub-objects, and it's not that powerful. And we're probably not 10 times as fast, but we're probably not degrading performance at all with what we're doing. But I think super important is looking at the memory usage. And the memory usage is divided by 10, and I don't know if you follow, I don't know if you follow, all the craze at the moment on Twitter about the garbage collection and make Kestrel super performant and how .NET can perform, and it all comes down to allocate less memory, garbage collection less, because this is very expensive. So we're trying to reduce the allocation, so that's super cool. I think we got time for one more. Yeah, that has to be. I mean, unless everyone wants to just stay until we're done, but it could be a while. Should we do that one? Or do you want another one? Clearing front-end content. Is there another one that you really want to do? No. Did we do all the demos? I think we did all the demos. I think so. We haven't done attempt, but it's not important. If you want to pick another one, go. Otherwise. I think all the good ones are done. Oh, language files. That one's pretty interesting. OK. Let's do that one. You want to do that one? Do that one. Sure. So language files. Something that we put into the core, I think in 7.3 something. Maybe 4. Most of this is actually documented, but maybe people aren't aware of it. A lot of times, people will have to modify the language files in slash umbraco slash config slash lang something or other. And you modify the core files. And then you upgrade, and then we overwrite your files. And that's not really cool. So we've gone ahead and fixed that conundrum. So these aren't my slides, by the way. I'm navigating blind. Language files, Bracket Master. Yep. So that's where they're normally located. And a lot of people just overwrite these. But they get re-overwritten by our upgrades. So we have user language files. Nice. It's weird timing. We ship with all these files, actually, in all new versions. So if you go to config slash lang, you'll see this. You'll see all of the language files listed there. And they're just empty. But any key values that you put in there will actually override the default ones. So there's been some questions. Can we change the quirky awesome messages on the login screen? I don't know why you would. It's so happy. But you could change them to angry ones if you wanted, or corporate ones, I suppose, by just modifying this file. You could also just change any wording in the Umbraco back office by just modifying this file. And when we deployed, we had to do this. We're not going to overwrite these ones. We only overwrite our own. So your overrides will still be in place. As a package developer, thanks. It's the same story. If you want to just distribute language files in your packages, you put them in app plugins, your package folder, slash lang, slash the file name. You'll notice these file names, they have the, well, they're not in this example. But they're the dash. They're the real way that you can name a language file. So it would be en-au for Australia, unlike the ones in our core. So these ones are actually the normal way you'd name it, not the old way. But if you want to ship language files in your packages, this is how you do it. You just put your own key values in there, and they'll all be merged in at runtime. And we won't go overwriting all your keys. So that's super easy. Any other good ones? Maybe I want to do the other one. I want to do the querying content friend content. Oh, yeah. That one is fun. OK. Cool. So that is when you want to render content on the front end. And you're like, I need to find the parent, the children, all the news items. You want to navigate the tree when rendering views or in a controller. OK. I don't know what. This one should have been hidden. OK. So this one is, I want to start from the current node. And I want to find the Twitter handle for that content. And I want to do it recursively, walking up the tree. And it's only the content that are composed of, like a composition of global sharing, that have a Twitter handle. It's a bit weird example. Probably I could do a recursive property. But this is a demo, so you have to accept that. OK. So what I'm going to do is walk over all the ancestors of the node. And if the content is composed of global sharing, so this is, I don't know if you knew about it, but you can check whether it is composed of that composition. Then I'm going to get the property value Twitter. And if it's not no, I will break the loop. And then I have the Twitter handle by walking up the tree. The idea is if you had a text node that could contain other text nodes, other text nodes, et cetera, you could assign, say, a Twitter handle for that specific node. And maybe when you publish, it'll tweet from that Twitter account. But if you don't fill it out, you don't want the Twitter account to be a Twitter account. It should be used from the top text page. You want it to be used from your root page. Maybe you have multiple root pages. So it'll just keep falling back until something is composed of the global sharing one instead of the local sharing one. But there are a lot of magical strings in that example that we hate because it's error prone. So we think the model's builder approach is better. It's basically, if you have a global sharing composition, it will be created as a global sharing interface. So you can check. Whether your content is of that type. And if yes, you can retrieve the Twitter account property because model's builder has created the Twitter account property for you. So at least there are no more magical strings in there. It is still not very, very pretty. So we have the last one, which is the one we like. It's basically we're going to navigate the ancestors. We're going to get them that are of type iGlobalSharing because there are actual classes, real types, where the Twitter account is not null. And then we pick the Twitter account, and we want the first one that is not null, or null by default. This will actually walk the tree, apply some conditions on each node, pick some values from the tree in one very nice link statement. I would say this is the core recommended way of navigating the tree upwards. So of course you want to do it downwards. You want to do it. I want my three most recently published news items. So I'm going to start from the root. Don't take photos of this. I'm going to go through all the descendants of my root node, those that are of type news item. And I'm going to order them by, it should not be sort order, it should be like create a date. And I'm going to take only three of them. Everyone's taking photos of this. Don't take photos! What's going to happen, we're going to go through the entire tree, all the content items, and run the off type, et cetera, for all of them, because we are ordering. So we need to check all of them. So if you have 10,000 nodes, we're going to go through 10,000 nodes. This is crazy. This is very expensive. This is going to kill your website, especially if you do that on every home page. So don't do this. We see this a lot. Descendants and self for everything. And then first or default if something equals this. Yeah. Yeah. So what can we do? Well, the solutions that we use is either you build some sort of index, like whenever a news item is published, you maintain a list somewhere in the static cache that we've demoed, and you insert that recently published item at the right position. So the three most recent news items are always there. It works quite well. Or if you really need to do something like that, you cache. So you run that query, but then you cache it for 10 minutes using the nice methods that we've shown you. So at least your home page will be slow once every 10 minutes. But for all other users, you will get the data pretty quickly. But descendant is dangerous. Or you do examine if you want to create an index in examine. So the message here is that we have plenty of very useful ways of navigating the tree, .children, .parent, .ancestor. But think about what you're doing, because that .descendants is like going everywhere. And this has to be expensive. Descendants is fine if you start with the root, a node that you know doesn't have a lot of children. So how do you get to that node? That's the question. Examine and leasing is not the answer to everything. It's fast, but what we also see as an anti-pattern is people making examine queries for every single thing, for every single page load. Can you imagine on one page load if you have 400 examine queries running to render your page? Yeah. That's still 400 examine queries running. Examine is not like the greatest thing ever, and it will just do magical things. You're still running 400 queries against it. That's kind of like running 400 queries against a really fast database. Maybe not the best. But if you can find that node then to call descendants on, that's the fastest way to do it. That thing, you can use X-pass to navigate the tree if you want. For V7 benchmarking would be required. I haven't done it to figure out whether it's slower or faster. It needs you to understand how to write an X-pass query, which can be a bit weird. And in V8, it will be slower than using what I've shown before, the traditional link query. X-pass would work, but it's going to be slower. With the continent cache, if you say, I know the root node or I know the node that I'm on, and I want to select these subnodes. Well, I start here. I know the type, the first type. OK, that's an easy query. And then I can get this one. And then I can look up the descendants there. If you do it in stages, it keeps faster than saying, all root nodes don't select many descendants. Your site's not going to be very happy. Do we have one minute? No, I think we're done. But we only have one left, I think. Yeah, I wanted to talk about that one. Because it's an interesting one. How do you update content? So this is not front end. This is back end. How do you update a content item? OK, the right way to do it is this. So you take photos. You get the content by ID using the service. And you set a value. And you save. This is the official right way. At the moment, there are probably 12 other ways to do it in core, in version 7. And they will all be gone with version 8. So that's the only way to do it. So if you do it today like that in version 7, it will be fine with version 8. Everything else, like in Node Factory or the CMS.businesslogic dot whatever dot document. That's CMS.businesslogic. That's CMS.businesslogic. All this is gone. You can use that also to upload files. So if you have a file in a stream, you can set value. It would be upload file name or whatever and the stream. And it would create the media file for you, put it in the right place, and link it to the property. So it's all automatic. So that's what you want to use. And so you have this bright ID. I'm going to count how many times. How many times my content item is viewed. So each time it's displayed, I'm going to get the content. I'm going to get the view count value, do plus 1, save the value. And I'm going to save and republish my content. You didn't add any comments for additioning 1? Yeah. Obviously, best practice that we enforce in core would be that after plus equals 1, there would be slash slash incrementing count. That's coding standard. Anyway, this is very clever. And actually, we've seen it more than once. Yeah. OK. This is a bad idea. OK. Why? Because you keep republishing things, which means creating new versions in the database, so rows and rows and rows. So you have millions of rows in CMS property data, and you're killing a record. So this is a bad idea. Actually, it is a very bad idea. Concur. You want me to continue? You want to work? Yeah. How do I do it? How can I have a counter on my content if I can't put it in the content item? Is everyone still good for us to keep going? Yeah. You put it in your own database table. There's no choice. I mean, Umbraco doesn't do that, so you have to create your own database table. But you want to use what we use. You want to make it simple. So you're going to create a DTO object, which is basically a description of your table, but in C sharp code first. Do you want to explain the DTO briefly? Or? Well, I mean, it's a model, right? It's a model that describes the data that you're going to build your table with. There's all sorts of different attributes that you can use, primary key, where it references to and references from, et cetera. It's fairly straightforward. If you want to know more about it, if you jump in the core, there are tons of examples. And then you access, sorry? Yep, as put a set of POCO. And within POCO and V8, it's basically, the same thing. I think it's actually the same. And POCO is put a POCO. Just put a POCO is sort of abundant, so we're moving to a POCO. But it's a fork. And that is how you use the database. So you create a new DTO, and you insert it. Or you fetch the thing. And that's actually, I think I have a nicer syntax right after that one. Yeah. Put a POCO or end POCO allows you to write SQL by not writing SQL. Like from something, where something, et cetera. And it will deal with the fact that maybe you're running on SQL server or on MySQL. So if you create a package, you support multiple databases by doing this, instead of supporting only the one that you're testing on. So see, you don't have to manage database connection, weird things. You reuse what's in core. And you don't even need to create the table, because we, OK, the database is, this is how you access. The database is somewhere in the application context. And you don't even need to create the table. This is how you create the table. You create a schema helper, and you say, create the table for me. And it will create the table and create the indexes. And depending on whether it's MySQL or SQL CE or SQL server, we'll create the proper indexes and use the proper types. What's missing here is the check. Is the table already existing? But we have that. So I think you can do schema helper dot exist table. Whatever. And then you create it. So you're not writing dirty SQL. You're just reusing all this. And then I'm absolutely, I think, I got one more. One more. Just one more. Because I remember this one's actually pretty cool. How do I get back to that other screen? You get out of here. Which one are you looking for? The IOC one. They want the cheese one. Oh, the cheese one. OK. One more. And then he can talk about cheese all he wants. Yeah. So we talk quickly about IOC. IOC's in V8. LightInject is our container of choice. As I said yesterday, you can use it. You can add to it. What's really cool, though, is all the plugin architecture that we use, we use something called Resolvers, use the container now. So I'll show you some quick code about a very common plugin that you would create, which is like a property editor. So the property editor, you can now do this. You can get everything that you want, and inject it into it. So in V8, any time you use a singleton, think to yourself, why am I using a singleton? Even in V7 now, why am I doing this? We expose almost all these services on all the base classes that you generally always use. In, I would say, 90% of cases, you should never be accessing singletons. It makes your code totally untestable. And we are already giving most of this stuff to you. There's very few cases where you need to access a singleton. OK. But in V8, we inject everything. So all the plugins are injectable. So anything that you require in your class service, you just add your constructor parameters. It'll be automatically injected. Yay. So how do I add stuff to the container if I want? Again, you can use your own container. Bring your own container. Do whatever you want. We'll have some extra methods to make sure that you're adding all the stuff we need to it. But if you just want to use the built-in one, First thing you need to do is build a custom boot manager. And because we normally run on the web, we'll inherit from web boot manager. And there's one method, configure application services. Here, it gives you access to the container to add anything that you want. You can register singletons, request-based lifetime ones, anything transient. So that's how you can add stuff to the container. So how do you register your own boot manager? OK. Well, you need to make your own global ASAX class. So global ASAX is an Umbraco application. So you create your own application. You override get boot manager, and you return your own. Then the last step to this is to just wire that up in your global ASAX. So use my application instead of the default Umbraco one. And that's how you just add stuff to the container if you want to do that in V8. That's it. I think that's it. I think that's it. Yeah. Oh. Well. So probably we should have just continued roulette, but whatever. Oh, we have that one? Oh, yeah. Yeah. Yeah. Uh. Go. Questions? Yeah, plenty. Oh, this is pretty good. All right. Thank you. I don't know how to turn it on. I think it's on. Yeah. The example you were using about creating a table. You have to speak louder. The example you were using about creating a table using the schema helper. Is it not better to use the migration runner for that? Yep. The question is, is the Petapoco create table syntax reason? That was just a demonstration to say how to create a table. But when would you create a table? Right now, there's a post up by Submit. By Sebastian on how to run migrations manually. It's not the best way to do it, but there's no other way to do it. But what I was talking about yesterday is the package migration format. Yes, that's exactly when you would do it. So if package migrations are a thing, the installer would run for your package. And then that's when it would execute a migration. And in there, you would say, yep, create table. But even better, we have a nicer way in the package migrations to create that table. So hopefully that answers your question. Nope, nope. Way back here. Casey. Come on up, buddy. I have this barrier. With the injections, what do you do if you have a third -party package that's doing injections as well and you want to do your own injections? So the question is, with ILC, the container, if you have another package that's adding to the container, I guess how do you handle conflicts? Is that right? Yeah, very good point. I guess we haven't figured that one out yet. We will. The last thing I want to do is add an event to add stuff to the container. I just don't like that concept because when we introduce that, we're already breaking compatibility with going to ASP.NET Core. Because in ASP.NET Core, it's up to the developer to configure your application. So in that regard, if another package requires you to add stuff to the container, maybe they give you some instructions on how to do that. With ASP.NET Core, the idea is you're in charge of your app. You don't install magic, which is what ASP.NET is. Maybe we'll make it slightly magical, but I guess the story is we haven't got that far yet. More questions? There was hands up here, but no, not anymore. Okay. That's amazing. Okay. Whoa, whoa, whoa. Hang on. Hang on. We got one more. Oh, jeez. Oh. Oh, jeez. Oh, jeez. Oh, jeez. Ow. Oh, jeez. Thank you. Thank you. Thank you.