Entity Framework Migrations Explained

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
>> Tune in to this week's On Dot Net, where I have my good friend Brice on, who's going to demystify entity framework core migrations, so tune in.\ [MUSIC] >> Hey, everyone, I'm James Montemagno and I'm back with another On.Net episode this time probably one of my favorite topics of all time because I know nothing about it, which is all about database migrations with entity framework core. I brought none other than my best friend in the entire world, Brice Lambson. How's it going Brice? >> Howdy, it's surreal being here. I remember when this show premiered in 2016, I've been a fan ever since. I'm honored to be on. >> I'm super excited because everyone on the EF core team is just like, you got to have Brice on, you got to have Brice on because I'm like, I don't know anything about anything. You're on the EF core team, correct? >> Yes. >> Yes. How long was it? How long have you been on the EF core team and what all do you do there? >> Oh, man. I've been on there almost 12 years now and I've been working on migrations almost since the beginning. I work on reverse engineering, I work on a lot of cycle light stuff. Those are the things I remember right now. >> What would you say, now, if people are just turning in, they're like, I'm in database migrations. I love that. What is the pitch for EF Core in general? Why are people using the EF Core and why is it so important? >> Yeah, EF Core is just one of many options. Database migrations have been around, specifically database schema migrations in other word migrations is very overloaded. We're talking here about your database schema, which is all the tables like columns, the foreign keys, all that database stuff. Migrate data schema migrations is a pattern for really evolving that schema as your application evolves, as the requirements change. You can think of it a lot like version control, which is for your source code. You record what changes you make to your source code so you can share those with other developers. Schema migrations are the steps to evolve your schema as your application evolves. They definitely weren't invented by EF, they existed long before. If you've ever used Ruby active record had migrations before us. Even way back in the day I remember manually writing little tiny bits of sequel and checking them in the repository. That's the pattern, it's recording the changes to the schema. >> I have a database, I have a person, for example, and I have a first name, last name and I deploy that application and then I'm like, oh, I forgot to add a middle name. Is that the scenario that I need a migration or is it a scenario where I'm like, oh, actually, I put it as a string, but it should really be an integer or something. Are those both examples that I just listed as migrations? >> Yeah, definitely. I think there's two big use cases. One is for everyday development, just like virtual control. Like I'm working on a feature, my teammate's working on a feature and we're both evolving the database schema and I don't want him to break me while I'm trying to work on my feature and so it's just everyone being on the same page in terms of their local database that they're testing with. Then you also mentioned deployment and that's another things like, oh, we need to ship Version 2, all the databases are still in Version 1. How are we going to not delete all their data and yet update their databases? Those are the two big scenarios. >> Got it. Now, how come it just doesn't magically happen for free? Because I come from the mobile world where I use SQLite Dash Net and I'm just slang in Pogo objects everywhere, adding stuff, removing stuff. It just does it automatically, I think. How come that just can happen? >> Yeah. Frank Kruger is a ninja. SQLite Dash Net, it hides all those stuff from you. It's really good at additive changes. In fact, it's so good you don't even know it's doing it. You just hand it data and it says, oh, I need to put this data somewhere. I'm going to create a column for you. I'm going to create a table for you. Additive changes, you've probably never even encountered those database schema. It works. It gets a little tricky when you want to split the name column into a first name and a last name. That SQLite Dash Net will then create two new columns and the old one is just there. Yeah, you need to grow up into a migrations framework. If you're using SQLite Dash Net, there's a really great library called Fluent Migrator, I think is what they call it and same with Dapper, you're not using EF. You probably don't want to use EF just for migrations. There's another library that encapsulates this pattern of recording the steps to your database and you write it all in C-sharp codes. You don't really need to know SQL. That's the real beauty of a migrations framework. >> Got it. That's super cool. I've been getting into the web world where EF Core seems like just the de facto as a donor developer that I'm going to use, it's built into templates and beautiful scaffolding. I think the very first time I was going through an MS Learn module in the Kamro, it was I'll create this thing and run a migration. I was like I have no idea what's happening right now. I clicked a button and things happened. I was hoping that you could demystify that in a logical step. I'm brand new, which I am, by the way, to this. What am I actually doing or what is happening and how does the tooling going to help me? >> Yeah, before we get there. As with any tool, you need to stop and ask yourself, is migrations even the right tool for me? There's a lot of people who really love writing SQL, they like to craft their database by hand. They might have a SQL server project and they add tables to it. It's the source of truth, if you will, for the model. Everything flows from the database. You want to add something to the database, you go change the database and then it flows back into your application. The SQL server project tools have a really cool thing called schema. Therefore, you model your table and then you say, okay, I want to make these changes to this database and it will go out and make those changes. We like to call this a database first workflow on entity framework. If you love doing this, if this is how you work, migrations are for you. >> Okay. >> Continue writing your little SQL scripts or using the database project deploy tools. In fact, there's a really cool project called DB app, which is associated with Octopus deployment, you can use it outside of that. All it is is a little console app that you add your SQL scripts to and then it can just apply those to a database. It takes care of the tedious part of running those one by one and making sure they run in order. Yeah, that's the tool if that's your workflow. >> Okay. >> Where migrations really shine is on the other end of the spectrum, you want your code to be the source of truth. You want to add a feature, the first place you think to go is go change my app code and change it there and so we call this code first, although you don't have to start with the code first, but we like to call this code first because it's the code is the source of truth and then everything flows from there, whether that's you change your domain model in your application and then you go write a migration or you have it generated for you in the case of EF. That's the flow there. >> Got it. That makes a lot of sense where it's like I see where you're coming from is like, hey, this database is controlling the scheme and that is the source of truth or the code, like you said, code for database first code first make sense. The code and that's what I'm used to is like, here's my person in class, here's the thing. I'm like, database, please be what I want you to be basically instead of the vice versa which is database you are this and my code will react to that. Did I summarize that correct? >> Yeah, and if your database is the source of truth, we have something called reverse engineering or scaffolding. You would make those changes to the database and then you just re scaffold your model and it would bring in all those changes for you. That's a tool for that. But if we want to look at what it looks like. >> Yeah, it's all right. You want to bring up your monitor and we'll give it a look. Cool, I see it. >> Here's my super simple application. It's just a console app. I'm not fancy. EF works anywhere, you can imagine it works in.NET MAUI projects. You are an Android app and we work on Blazor WebAssembly. If you want to write a little SQL database on the client side of the browser, which is like mind blowing, you can do that. As you mentioned, it's very popular with server side applications if you have a web API. A lot of times you go through. Just a console out today just to keep things simple for this demo. >> Okay. >> Here I have a blog context and this is the EF's entry point into the database and then on it. Right here is just an if statement, if I don't have any posts, add some sample ones just for the sake of having it in our database, and then go through every post in the database and just write them out. >> Looks super duper simple. That blog context, that is, something that's important. >> Yeah. Let's have a look at that. >> Okay. >> I have it over here in my model. It is just a class that inherits from the DB context and this is just entity framework magic. You don't really need to know what it even does. Then you have these things called DB sets and these map, one to one to a table. >> Okay. >> Gross table. >> DB set is like a table and then that table is going to have columns with different items in it, basically, like what is a post? Is that correct? >> Yeah, and if you've ever heard the term like a unit of work, I'm creating a new blog context is like saying, I'm starting a unit of work. I'm going to make some changes to the database or I'm going to get some stuff out of the database. You create a new one of these to go back to program, you create a new one of these, make the changes, and then commit that unit of works persist everything to the database. >> Is that what we would call a transaction basically at that point? >> Yeah, every time you call save changes there, it wraps all these changes right here. It's adding three rows to the database in a single transaction. If it fails, you end up somewhere in the middle. >> See, I know a little bit of the lingo, but yes, Frank has hidden most of it from me. I get it. Perfect. >> Then just in here, I'm using SQL lite. EF call works with all databases, PostgreSQL, we work with MySQL, SQL Server obviously, Azure SQL, you name it. There's lots of providers. I like SQL 8 because it's simple. Here I'm just writing to a file called Blogs.db. Then here I have my plain old sale, or object, my pocker for my model. This could flow all the way into your views if you are writing like a client side up. Here's a post it has an ID. Lots of database is just use surrogate primary keys they call them just to keep track. Because there's no such thing as like an instance. You can't just take an instance out of the database and shove it in a C-sharp, this identifies this piece of data and it has a title and it has some content. Super simple. >> Nice. >> I've got my app written, my super cool app. I'm ready to run it. Let's see what that looks like. >> At this point, you haven't really done any these usual code basically. >> Yeah. This is all just plain old. Immediately I had an error. >> Oh my goodness. It's ah. But what's going on because I thought you are all about, EF on the EF team, how could this happen. >> It says there's no such table posts. I don't have a database. I never created the database. I wrote on my code and I went to query it and it threw it because it couldn't find any data. I couldn't even find the database. Let's look at what it looks like to actually create the database. The first step is to create a new migration. For this we use the NuGet package manager console. Is a little tool here, and this is an odd little thing. You can think of it as like a console that knows more about Visual Studio. You can actually use those to install packages. The first step, when you want to get started, you need to install the tools. >> You're bump that up for me on that little 100 down there. >> It works. >> Yeah keep it going a little bit more. >> Here we go. >> Perfect. >> It's really big. I'm going to use this to install the tools as an airbag. You could also right-click on your Solution of managing the packages. I just put it in the console here to get used to it, because you're going to see it here in a minute when we start using migrations. >> I need this, so even though it's just like initializing the database, I'm still creating a migration that gets an initial migration. >> Yeah, your very first migration is going to go from nothing to something. You're going to create the schema as it is right now in your account. >> Got it, I think that was always my confusion because I'm like, I'm not really migrating anything, but that makes sense. You are migrating from you had nothing to this is the thing that you want in v1. V0 to v1. >> I'm going to install this tools package. All it does is just add a package to my CS project. Well, that's really big. I think those are linked together. I'm not going to zoom out yet, so I'm going to keep working here. >> Okay. >> Now I have these tools installed and they add a few commands you don't have to use. This is the Visual Studio way of doing it. There's another way if you're working in like Visual Studio or outside of Visual Studio, like Visual Studio from Mac or of your code or if you like, Emacs or VM or whatever. You can do all this stuff on the console and the way you do that is just go in that tool install. I like to install it globally, so that's available everywhere. It's just a tool called.Net EF. >> Cool. >> This is the command line way. If you're a console junkie and you like black and colorful text. Then it has similar commands to what you're going to see in a minute. You just invoke it with.NET EF. You get to see this cool little unicorn. >> You can't be [inaudible] I mean, come on. >> For sure. I actually created that. I put it in as a joke with the team. Let me keep it, so I was excited. It has migrations, commands. It has commands for working with the database or the data context. This is a command-line way of doing it. I'm not going to show that because I like using Visual Studio. I like to stay in Visual Studio. There's PowerShell command for the same thing. Right now we're going to add a migration. We're going to just call it InitialCreate. Now, this is like a get republic or I have like my first command where I add all my code. I'm going to add all my schema as it is now so I can start evolving it. >> Is that initial create, is that just a tag that you set or is that actually a keyword that's of importance? >> This is the name of the migration. You can think of it as like a commit message, and the way it comes out is it actually becomes the class name here. >> Interesting. I'm not going to spoil it, you're going to do much cool stuff. I'm not going to stop [inaudible]. >> Yeah, you see over here, by default, it creates a migrations folder. Then you can see the initial create there, and it puts this big old time stamp in there. The reason it uses the time stamp is to help keep everything ordered correctly, because at the end of the day, they're just files on the file system. But if you can see like the sequence of changes and if your team's working on something else and you merge in their changes, you see when they made their change. >> Got it. That makes sense. >> Let's look at the anatomy of a migration. You get this class that has an up method and it has a down method. The up methods bring the database up to date with this migration where your model was at, in this migration. It has this funny little DSL, looks like SQL, but it's very C Sharp as well. We're going to create a table, so the post table has an Id, has a Title, has Content, it's got a PrimaryKey, that Id column. One thing here, though the title here says that it's knowable true. That was like a mistake, like, oops, I'm not ready to share this with the team. I'll show you in a minute. We're going to get to that. Let me finish going through the anatomy. >> Okay, got you. >> I getting ahead of myself, so excited. All right, the down method obviously if you unapply this database, you go back to nothing and so we drop the only table we created. >> A drop is like a deletion? >> Yeah. It'll delete the table and all of its data from the schema in the database. >> These are just helper methods, no one's calling this yet. These are methods that you could call. I guess you could new up an initial create and then call those methods. >> Interesting point. When this method right here is called, it doesn't actually create a table. This tells EF to generate SQL in order to create a table. That is a very important distinction. This is defining the script. This isn't actually just finding like execute this code procedurally. It's a little language that EF uses to represent a migration. It's abstracted away from the specific database. Like you can see traces of, oh you're using SQLite, we're going to throw in an Autoincrement there. But in general like whether you're using MySQL, PostgreSQL server, all the migrations use the same language here. >> Got it. Cool that makes sense. It knew that the idea to autoincrement that was the thing, just EF magic? >> Pure EF magic, yeah. You can type every little thing about your model saying this is the primary key and I want to use this database type. EF has just a ton of conventions. By default, it will look for an integer or I don't even think I was doing integer. It just has to be a column named Id, and that will be your primary key. So if I wanted to call that key, suddenly EF wouldn't know what the primary key was and you'd have to go and tell it. >> Got it. >> We call it convention over configuration. You don't want to go configure everything, so we have these conventions and if you follow them you can write less code. If you don't follow them, you can tell us what we don't know, tell EF how to do it. >> Cool. >> Back to the anatomy of a migration. Underneath this little initial create with the up and down, there's this designer file. Peel back the curtain and look behind and see what's here. You don't really need to know about this. All of this is a snapshot of your model and it's almost never used. It's just here in case when we're generating that SQL we need additional information and a look here. One cool example is on SQLite. You can't actually alter a column, they don't support that. One of the things we have to do is we have to rebuild the entire table, like recreate it in a new place, copy all the data over and delete it. All just for a little alter column. The alter column clearly isn't enough to do all of that, so we'll actually peek back here and say, okay, when we recreate this table, it's going to need all this additional information. This is like a last resort though. >> Got you. >> Because we can, we use the stuff that's right here. >> Okay, cool. It's cool that it's there. >> Yeah. Good to know it's there. Don't delete it. It might be used. The other thing we get as a model snapshot and it's exactly the same thing. It is another snapshot of your model at this point in time. This one has a very different purpose. This is so that the next time you call on migration, we know where we're coming from. Because if you're just changing your apps model like we don't know. All we have is an up and down method. This tells us create an up and down method for the changes between the snapshot and then when you generate a new migration, this snapshot will be overwritten with the next current model on the next current model. >> Got it. What you're saying is like this is the current snapshot in time and then the migrations would build up all the changes that you made do it. >> Yeah. >> Okay. >> All right, so that's the anatomy of a migration. Like I said, I don't like that you can add a post without a title. That doesn't feel right to me. I want to go back and I want to make that required. The good news is I haven't shared this migration with anyone, so I can just go and actually delete all this stuff. But again like if you've added two migrations you don't want to just delete the snapshot because that it will be completely lost. >> Got it. >> There's another command to sort of keep all this stuff in sync and it's removed migration. >> This is kind of you're playing around with it. You're a developer. You think that you did it but actually you didn't do it. You're like I messed up, rollback. I don't want to go into Git and undo everything because you don't want to mess up all your code changes basically. >> Yeah. I really like thinking about version control in this context. Like, oh I thought I was ready to commit my changes and push and send a PR. Whoops, I'm not quite ready, so let me go back and sort of amend that last commit and Git lingo. >> Got it. >> I'm going to undo this migration. I want to take this one away. I'm not ready to share this with anyone. It's not in the way I want it to be. Remove migration just sort of removes the last migration. >> Okay, just like the last one in general? >> Yeah. While keeping the snapshot in sync and everything like that. Of course it was my first one, so I just deleted it. All right, let's go and make that column actually required. I'm just going to use data annotations here because they're simple to understand. >> This is part of EF Core or? >> Yeah, this is part of EF Core. There's two ways to do things. One is with these annotations, the other is to override on model creating. It's a lot more code but some people really prefer that. Then you could say modelBuilder.entity Post. This property, oops I forgot my parenthesis. The Property Post. You can see it's a lot more code. >> Yeah. >> The dot is required. >> Oh, wow. Okay. >> If you really want to write it this way you can. One of the reasons people don't like these annotations is that they pollute your POCOs. It's not really a POCO anymore. It knows more about how it's resisted. I think required doesn't really fit that definition because your UI layer might use this for validation. >> Yeah, sure. >> It's just a flavor. There is no right or wrong way. We just allow different ways of doing things. >> Yeah. I've definitely seen people that I really, really want my model to be in a completely separate library and it should do nothing about anything. That's cool that there are those two options. >> Yeah. Let's look at that one again. All right, and now we can see that the title column is not nullable. You must specify one of these. >> Got you. You needed to do it there. You couldn't just go on and change. You should really never change these files basically. You shouldn't touch them. >> I wouldn't say that. You may want to change these but don't change them in a way that is out of sync with your entity framework model. You could say now it's required in my domain model but actually, I want to allow some bad data in there. You want to relax some constraints. My application should never insert this data but if some bulk update happens to import data without null then maybe it does. I don't know. But yeah, in general you probably don't want to edit these. You may want to tweak them a little bit. But not in the way that disagrees with what you already wrote in your classes. That's kind of the definition of insanity. Your model is not sane. >> Got you. Cool. >> Now I have this migration. It's the way I want it to be. I'm going to go ahead and actually create my database now. I'm going to apply this migration to the database. There's a simple command called Update Database. If I do dash verbose, we're going to see a bit more output here just so you can go wow. You can see a bit more of what's going on here. >> This is like creating it on your local machine? >> Yeah, it's creating it actually right here in my directory. In fact, let's look at the database here. Here I'm using the SQLite toolbox by Erik Jensen, a good friend of the EF team. It's just a simple way of inspecting SQLite databases. Here's the post table created an ID with a primary key. It's got a title column, not nullable. It's got a content. In here there's this other little table. It has double underscore, so you know it's special, don't touch it. >> Yeah, that's super special. Not just one underscore but two underscores. >> Two underscores, yeah. This is how EF keep track of which migrations have been applied to the database. Again, like you're working in a team environment, you pull. You need to apply your teammates migrations to your database, needs to keep track of which ones. If we actually look at this data, you can just see there's that initial create, it's been applied. >> Oh, cool. >> You can even keep track of what version you used to apply it. Just for fun. >> Nice. >> Now when we run our app everything should work because we have a database, has a schema. We can actually put data in the database. Let's go ahead and run it. >> Got it. There's nothing that you need to do. You don't need to call the migration in code because you've executed it here, so your database is up to date with the schema. It's executed the SQL commands to generate the tables. >> Oh yeah. Let's go back to that verbose output. See right here, you can see the create table post. That update database command, what it does, is it looks for the database. If it's not there, it will create the database if it can. It will see which migrations have been applied. It will generate the scripts for those migrations, the scripts being this create table right here and then run those against the database. >> Got it. >> This is very much like a developer time thing here, like a gesture. We'll look at other ways of getting migrations applied. Let's run our app and see if it actually works now. Did that work? Did it already run? >> It already run. >> Let's delete that database. Let's try all that again. It shouldn't actually have data in there. I'm surprised. Maybe I kept one around on accident. Update database. Now we run. There you go. Adding sample data, and now I was supposed to run it and it doesn't add the sample data, there we go the second time I "run" it, I doesn't have the data enlisted there. Let's look at making another change to the database here. For this one, let's add an author, so each blog post should have an author here. Now I've just changed my application code and that's enough for any framework to know what to do. Let's add another migration. >> Okay. >> We'll just call it AddPostAuthor. Something descriptive to help me know what's going on here. >> I saw that it autosave the file for you. >> Yes, that's really the beauty of using the package manager console commands as opposed to the command line interface, but it doesn't know anything about Visual Studio. >> Got it. >> You'll also notice it will pop up the file. Is that little, little things like that, that make it more integrated, which is why I still prefer it. Here we go. We're just going to add a Column, an Author Column to the Posts table and it's text, and on a level. >> Yeah, and I see you've just added it right below it, so the very first one and then it just goes down below. Because date, times stands basically. >> Yeah, definitely. Again, I haven't applied this to my database, so let's do that. Let's update database. It will look in the database, and say okay, I only need to apply this AddPostAuthor, migration has already been applied. Then if I refresh the database over here, it should have that author column. I expand everything again. There it is. >> How cool. >> Though that's not a super simple overview of the workflow of using migrations. Another interesting thing, now that I've applied this, if I actually try to remove it, it will warn you and say, you've already applied this to the database. You need to think about this a little more. because if have you share this with your teammates, it's like rewriting Git history. If you do a forced push, everyone's machine is going to be out of sync and it's going to be very hard to get it back. Before you just reverb and change your migration, you need to stop and think about, how does this migration then apply to other databases. If it has, don't remove it, just add another migration on top of it so that everyone can keep rolling forward. >> Got it, so in that case, would you then remove the author from your code and then run another migration? Then actually it would disappear. It would be like create it, add it, remove it. But then everyone's in sync, so it's okay? >> Yeah, definitely. >> Interesting, okay. >> Again, I have that version control mentality. This is very key to me here. I think it really helps you use Migrations correctly. >> Now I have a question for you and maybe I'm jumping the gun here, but you have a database that's here in your source code, right, that you would check in magically and to get or not in to get, maybe you would ignore that, so everyone's running it. What if that database doesn't exists on a server. How does it know when you start up your app that you need to run Migrations or do I need a VPN into a machine and run these commands in prod? >> Yeah, and the answer is it depends. Think about node development. There are thousands of databases on everyone's phone. You can't just VPN and run. You can't just change that. I hope you can't anyway that would be a total loss. You can't just change their databases. It needs to be part of an upgrade experience. Like, they've downloaded Version two of my app, their database is still at Version one. You need to roll it forward, and so in that case, where there's, a phone accessing a local database, it's absolutely safe to write here during app start to apply the Migrations, db.database.Migrate [inaudible] how you using here. There we go. This does basically the same thing as our update database, and only when your app is running it will find the local database. It will see which migrations need to be applied. If the database isn't there, it will create the database. This is super handy for client development. But if you think about this on a server where you're scale out to 10 nodes, all accessing the same database, they're going to be stepping over each other's feet. It's going to be a nightmare, and so we really don't recommend this on a server scenario unless you have a single server accessing a single database. >> Got it. That makes sense. >> [inaudible]. >> Yeah. >> When you are in that server scenario, the one where we do recommend it, is the traditional way you generate a sequel script and there's a command for that too. It's just called script-migration. >> Okay. >> This is like an artifact that you can then go and hand Your DBAs who know everything about databases, they can review it, they can say, "Well actually you need an index here, when you create this table." They can review it. This can become part of your deployment process. >> Got it. That's really good, it's really nifty. There's all of these different commands built in, including, here's the sequel script. That's really cool. >> Yep. >> Nice. Anything else you want to demo through or it's covered? >> No, I could go on forever about this stuff, but I don't want to overload viewers. This is merely an introduction to how do I start using migrations? >> I think this is great because I honestly, I feel like sometimes I can walk through the tutorial, but I'm not actually getting the sense of exactly why or where all these files are out. To me, this is actually perfect. On that note episode I'm going to rewatch probably like 100 times is that every time I create a new EF Code database and I'm using anything, I'm going to go through all that. Brice this is absolutely perfect in every regard. I love it. >> I'm glad, and if you want to know more, are the docs, the docs.Microsoft/ef. There's all kinds of great docs in there about, Well I want to use SQL Server and SQL items in my application or what are some other tips you have for team environments? We have these things called bundles. What's a bundle? They were new in the episode. All kinds of more information, if you go and look at the docs. >> That's awesome and we will put links down in the show notes below regardless of where you're at or if you're on Docs right now or you're on YouTube, go down there. I'm going to get all those cool fancy extensions that Brice had as well, so everyone can install those. You're using Visual Studio and of course, also the Command-line stuff too. Brice, thank you so much for coming on and walking through this. This is absolutely delightful. >> Yeah, my pleasure. >> Awesome. Thanks, everyone, for tuning in. If you have any questions at all about anything but specifically, probably EF Core and Database Migrations, leave them in the comments below. If you're over on YouTube, I have Brice, go and check those out and comment back as well and I'll check on them too. If you're over on YouTube, don't forget to like this video and ring that notification bell. Hit subscribe so you get notified whenever we put out new YouTube videos here. Brice, thanks again for coming on. Thanks everyone for tuning in and have a great day. [MUSIC]
Info
Channel: dotnet
Views: 29,020
Rating: undefined out of 5
Keywords: .NET
Id: fl6r-9rQjns
Channel Id: undefined
Length: 36min 52sec (2212 seconds)
Published: Wed May 04 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.