C# Dependency Injection with Autofac

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
dependency injection can be a really confusing topic yet when done right dependency injection can be one of the best things you do for your application it allows you to disconnect pieces of your application from each other easily and it allows you to test the various parts of your application independently today I may get you started using dependency injection using a free tool called Auto FAQ we'll see a small sample application and first apply the dependency inversion principle that's the D in solid then we'll wire up Auto fact to handle connecting the various dependencies together if your newest channel my name is Tim quarry and it's my goal to make learning c-sharp easier this channel is full of videos explaining the various parts of c-sharp I also have a website where I provide full courses on c-sharp and sequel one of the things that I encourage is for you to practice what you've learned it in a real-world application as of this recording I have two different start to finish courses that do just that and more are on their way if you want to see how various things you learn fit together in a real application check out those courses at I am Tim quarry calm now let's get started in our example project as with all of my recent videos I have uploaded this source code as part of a blog post on the subject be sure to follow a link in the description to get that code now here I have a console application and a demo library which is a class library okay now I've I've just started us out it's a really simple application we have here is in the program CS we have this business logic class being instantiated so we say business logic equals new business logic then we say process data and that's it now if we go a business logic the process data does a few things first of all it news up two different instances one for logger one for data access this says log starting the process of data then it processes the data now notice has just a console.writeline this doesn't actually go out and do anything everything's just a console.writeline to simulate doing something so it says processing the data and then we load some data and then we save some data called processed info and then we log there were finished processing the data so that's it okay now let's look at the data access real simple if you call load data method it says loading data if you call a save data method it says saving whenever you pass in the longer just console.writeline x' logging whatever you message you passed in okay so real simple let's write real quick and I'll pull it over the other screen here so you can see it that's all we do so logging start at processing data processing the data loading the data saving processed info and then logging finished processing of the data okay so this application simulates do a lot of different actions that we do a actual application but it doesn't actually include the complexities of actually doing them so even this bare-bones skeleton of an application gives us a good idea of what an application might do okay it kind of boiled down to the simplistic version of it and the reason why I did a simplistic version not a real application is because in this tutorial is the tutorial and we're just focusing on how do we do dependency injection using Auto FAC that's our end goal and so we're just starting off with something that simulates what we would do normally we know right away that we have some problems if you subscribe to the dependency inversion principle that's the D in solid for example here in business logic we knew up two different instances one for logger one for data access that means we heavily depend on these two items okay so this process data method this business class logic class they are directly tied to these two utility classes okay we can't disconnect us in any way and if we wanted to replace for example logger we have to come in here and change this right here to something else okay so the Pennsy inversion principle says that's the wrong place to do it instead we should let bubble up a chain and have programmed ICS or one of its you know high-level objects control all this newing up now if you've watched that solid video I did a de Sala video for dip dependency inversion principle you understand that you know what we did in that video is we created a factory that when you ask for a business logic class would give you a new instance of business logic and so on and so forth we're gonna do something similar but not quite the same thing but let's start off first by setting ourselves up for getting ready for that stuff this is really easy okay so we need to create interfaces for all of our classes that way we can change them out later if we need to so what we're going to do is we're selecting the class name itself or clicking on it somewhere you hit control dot the last option is extract interface and it says I'm a create an ID to access and if gonna have a low load data and a save data method hit okay and now we have our public interface ID to access with load data and save data and both of those match up we re have and we now implement I data access interface okay so it's really simple you have to really do anything else there so let's go a logger and do the same thing or create interface for this and once again for business logic so we create an interface for each of our classes all right there I want to add to our our dependency inversion principle setup eventually our dependency injection system okay so that's step number one step number two is a stop depending on these being declared at the low level so we're on pass in our instances so create constructors ctor tab twice and then we'll create an ilogger and we'll call it logger and an i data access let's call it data access okay those are two parameters for a constructor and now we'll have an ilogger up here called underscore logger and we'll have an eye data access called underscore data access so the reason for the underscore is because these are very similar to a private backing field for a property okay so we're gonna treat them the same way and name them in a very similar manner which is these are being created externally we don't need to initialize these and we won't change them as far as reinitializing them okay so we're just say underscore logger equals logger and underscore data access equals data access that way we take our parameters and we put them into our class level variables private variables okay so now here we don't need these two instantiations instead we just use the underscore version for those two instances they're named the same therefore that works just fine so now we have this class the business logic class setup so that the constructor passes in the two instances it will need for this process data method that will allow us to pass up the chain the dependencies sort dependencies are now up here being passed down instead of our dependence in the bottom having the firm depends iam firm lock so now we have to pass those down in our constructor we could create a factory to do that for us but we know we want to use Auto fact so let's do that so we're over here in program CS so on references right click and say majnu get packages go browse and type Auto FAC the first one they are just Autofac that's got twelve point nine million downloads you'll probably you download it but this is the version of OTO that we're gonna use there's other dependency injection tools out there Beauty Castle Windsor there's a few others whichever one you're comfortable with that's fine they're all pretty similar as far as features and abilities I have found that Autofac seems to be the simplest one to use and yet still has all the power I like simple because I can remember it so that's we're gonna use so install I hit finish all right it it finishes so now I'm good and now I'm going to start wiring up this program CS to use auto FAC now some people will inline the code right here to a wire up Auto effect and the reason why is because the first thing you want to do in your application is you want to wire up your your container okay that's that's where all of the things that kind of the factory was in the the DV do the solid video for a dependency inversion principle so we had that factory that created the instances after the container does in Auto FAC and other dependency injection systems now you have to create that or you should create it right away that's the first thing you do so a lot of people just put the code right in static void main I prefer to keep it outside of code outside of right here because that way I have a class that has just one job and that's to set up the container and it's very easy to read it's very easy to understand and if you need to configure something you know exactly where to go so I'm not going to put it here actually I create a new class in console UI and let's call this class container config or made public static okay well we don't need to instantiate it you know have one method here called public static I contain er the control dot on that now there's two options here using system component model and using auto effect here's a little tip it's not system dot component model okay that has an eye container that's not what we want we want the one with auto FAC okay so it's an eye container and we'll say configure so it says one method in this class called configure and it's whole job is to configure the container so let's kind of start laying it out and then we'll talk through different pieces of it so we save our Builder equals new container builder okay it's gotta build the container for us the last thing I'd do is return builder dot build so was build it up build ooh well builder dot build builds the container okay so this is a container builder and it has one method we're gonna use called build and that builds the container what the container is is it's a place to store the definitions thinking like a key value pair list of all of the different classes we want to instantiate now right now it's empty so it's gonna return a list with nothing we're going to change that so we're doing here is after we'd set the builder we're going to tell it to register different pieces okay so we're just say builder dot register type alright and the types got a business logic I'll have take control dot here to add a using statement for my my demo library so business logic and the other essay as I business logic okay so what this does it's the the simplest version of a registration and what does it says that I'm going to register this class called business logic and whenever you and as this as here whatever you look for an eye business logic interface return an instance of business logic okay so registering business logic and saying respond whenever someone needs an eye business logic item okay pretty simple I mean a lot of the work most of the work is being done behind scenes but essentially all we're saying is when someone needs this give them this a new instance of this okay that's it now doing this one by one could get a little long okay especially if you've got a lot of little classes but there are things you can do to automate this process so I've already set up a little bit of automation prep work for us I've put these two classes the data access and logger inside the utilities folder okay so now we can do is I can say builder dot Register assembly types okay so register it some but that's only types where I say assembly I have to take control dot here where use reflection as we're going to do dot load so I'll load an assembly name of this is a new feature by the way name of and what does is I can put any object name in here and it will basically return the string of that Abbott name because I could just say this and that would work but the benefit of doing this way and say name of and the object is that now it's strongly typed and meaning I have it in intellisense and if I change the name of demo library to something else and say change everywhere it's used it will change this and it will know if demo library is not there look you've a red squiggly whereas if it's just a string none of those things happen okay so name I was a really powerful thing they've added recently that's just amazing okay simple and yet powerful all right so now I have I'm saying load this assembly I'm not done yet I'm gonna do some link where T equals greater then T dot name space dot contains we can do name of again well let's not though because I had to dive down to it no problem utilities it's alright okay so I've done here is I said okay in the demo library find where the namespace of whatever outta to find where the namespace contains the word utilities which since it's in the utilities folder notice the namespace is demo library dot util ''tis okay so where the namespace contains utilities then we're going to do is say as te t equals greater then T dot gets interface their faces and the first or default where I equals greater than I dot name equals I plus T dot name have I lost you yet probably and that's okay it's this one's a little more complicated okay but what we're doing here is Italy let me give you what we're trying to do and then I'll walk you through the actual link statement okay we're trying to do is say in the utilities folder give me all the classes register them and then link them up to the matching I interface and that matchup right there is right down here okay so saying get the interface where the name of the interface so I dot name that's interface that name equals I and then the name of the actual class that's a T okay so if you find a data access class then look for an eye data access interface okay and that's what you link together so here we have the actual class and we're linking up to the version of the interface implementation of it that's exact same thing we're doing here but for every class in the utilities folder okay it makes it a lot easier now I could have said I could have dropped off this this right here and just said basically all of the classes but that might be a problem if I have classes that don't have interfaces I don't want a map so I much prefer to see you do it in two subfolders that are in that namespace that way its Maps them properly another thing you can do is instead of looking at the namespace you could look at the name a TDOT name and see if it ends with a certain word okay so if you have a bunch of processors you know that every processor has an interface then look for in the root assembly look for you know T dot name contains processor or ends with processor okay you could do the same thing for controller or you know any other type of convention that you have now for me I find it easiest to have folders and just say folder name and that's it okay basically I can copy and paste this around change the assembly name of here and change the folder and that's it okay and now I've mapped up everything that's in that folder to each other okay interface to implementation so now I've mapped I did access to data access an ilogger to logger we're not quite done yet if we go back over to program CS we were newing up an instance right here we don't want to do that we can help it we'd rather have it so that we're getting this getting our instances from our container okay but we can't really do that as static class because notice in our business logic we get the the instances through our constructor or you will be okay well I'll show you how in a minute but that's a problem when it comes to a static class that has no constructor that's the house static classes work because you don't new them up so therefore we're going to do we're actually gonna create a new class in our console UI that's gonna be basically the start of our application so let's call this application when I get public not static sorry instantiate it and we're gonna say that it had oops not prop get confusing my snippets ctor for constructor we want to do is where I have our business logic so I business logic control dot at our using statement and we'll say business logic and then up here we'll have I business logic underscore business logic and then map those two together we have one method public void run okay this method right here is going the only thing we call over here so it's going to essentially call run that's it okay so in here Russ a business logic dot process data okay that's we were doing before over here and that was it alright and we can be done there I think that's that's good so let's let's call that good the last thing I do is control dot on our class to extract an interface my application with one and now we have one more thing to map so container config we're gonna do it manually so builder dot register type it's going to be an I application oops I'm sorry application we've got those backwards the type is the actual class itself now I could register this like this and what that does is it will say whenever you ask for an actual application I will give you a new instance it's essentially a say thing as saying as self okay as self and [Music] I've never actually even done that there yeah I think that's how you do it you don't have to do so this if you want register for as a self that's how you do it but we don't want to do that we wash actually want to have an interface as I application there we go so that's how you'd register the the application very simply why I was to up so when I add a new class I add this right here notice doesn't matter if the class is in console UI or in demo library does not matter and the reason why is because we are now working under the principle of the dependency inversion principle and so what's saying is the top-level object in this case the very start of program dot C S II is going to control all of our dependencies instead the bottom up it's the top down okay and so we're gonna register all of our dependencies of this application up here at the top now we can do that you know one at a time or multiple I prefer multiple if I can help it we don't like to type out every single time okay so now that we've got that wired up let's go back over to our program CS okay we can wipe out this logic here we don't need it instead we're going to do is save our container equals container config fan type dot configure now as I do well since we named it well it configures the container okay and that container is what holds all of our instantiation set up our design for how to instantiate something now a using statement VARs scope equals container Dodd's begin lifetime scope okay and so what this is doing is set a new scope for the instances being passed out typically we only need the lifetime scope but there may be instances where you have various scopes that you need to declare that's a more advanced case this video is going to focus more on the standard or typical uses of dependency injection so we're not gonna cover that on this video we will have a video later I will have a video later that will go into more depth of dependency injection but for now this is we're gonna focus on is the standard use of the 95% probably the uses that you're a need dependency injection for okay now with that being said if there's something that you have question on or or or think I've missed then definitely even the comment below because I'm gonna read those comments and then I'll try and answer as many of those questions as possible in the next video that covers it so it won't be like next week or anything but when I come back to dependency injection I'll try and cover the questions that you have okay so let's pick back up let's fix that now inside this scope we can do is save our app equals scope dot oops I'm sorry yep scope dot resolved and I need to add a user statement here for Autofac resolve I a patient not app set up an application there we go and then app run okay that's what I do here I said you know what I need an AI application object now normally you don't have to do this normally you just say in the constructor and we'll get to the different types of injection you can do but this is actually going the container which right here container dot begin lifes lifetime scope that scope object it's using that to say give me an AI application manually if you find yourself doing this a lot you probably have your application written wrong or not the best way I guess it's a better way of putting that okay typically you only have to do this at the start so here I say is give me an AI application now remember if we go back to the container config an AI application gives you an instance of application and was application having it that run method so back here at program CS this is now going to be an application instance and now yes a app dot run and was run do well it's going to where is that application it's going to call the business logic dot process data but wait a minute it's not calling business logic it's calling an AI business logic item that's being passed into the constructor so where did I handle setting us up well I don't have to anymore it's almost like magic but it's not okay I don't teach magic so I'm teaching you how it's gonna work so what happens is that when let's come back to programmed SCS let's close everything else out it bugs me to see multiple things open so when I say resolve I application it looks into the actual instance that's going to create and says does your constructor require any objects why yes it does it requires an I business logic so it looks up in the container and says oh that's down here and we needed a business logic instance does business logic require any objects why yes it does it requires two so that dives down into ilogger and says okay that's a longer instance does that require anything nope so knew that up and then it says I did access well that's a data access instance does that require anything nope so knew that up and now it can do up business logic and now it can pass that into the constructor for application and now can new up the application instance now it's all good everything's wired up and so now when I say app dot run it comes over here runs calls business logic which is an instance of the business logic class process data which goes down here and says I need a longer oh wait I already have one it's right here I call that I need you to access it's right there I call that twice and then call logger again so it's wire to everything up and just to prove that this in fact does work the way I'd say it does there you go it works the same exact way it used to so really that's all there is the dependency injection now of course like I said there is definitely a lot deeper than go into this but this is the basics this is the stuff that you'll use I guess that probably 95% of the time he is this stuff right here okay you have your container config which tells the system tells the container builder okay here's the instance you're going to create it and here is the actual interface to work against so interface the class to instantiate now right now what happens is whenever I ask for an eye application it creates a new instance of it there are ways just so you know to limit that to say okay will only give me one instance for the entire application think of it kind of like a a static class which you don't want to do static classes that way you don't want to have an instance that lasts the entire lifespan your application okay but this way again configured by default out of the box it's the way you want it for most cases which is a new instance every time you ask for one so this right here where we're passing in the constructor that's called constructor injection that is the preferred method of doing so you could create property injection which I'm not going to demonstrate here but that's I would rather you not use that okay there's less need for it there's less good use cases for it instead constructor injection is typically the way to go which does mean your classes have to be instantiate able mean they cannot be static okay so you want to make sure that you're instantiating all your classes that's the big the big thing as you say that's okay that's not a problem because the fancy inversion principle has already stated we want our classes to be instantiated so this just takes it to the next level all is really doing is auto wiring up our Factory and handling a lot of factory work for us that's probably why it's called Auto FAC I don't know for a certain but it sounds an awful lot like an auto factory okay so all is doing is wire you up and it's not even really automated but it handles a lot of stuff behind the scenes on how to instantiate and all the rest and it's just pass those instances in whenever we ask for them in a constructor okay so now we say hey you know new up a business logic so for example here I asked for a business logic okay and I business logic item this is okay why is it up why is this up creates the ilogger an idea to access items and sets us all up now you might ask yourself the question now let me say you probably should ask yourself the question why should I do this this seems like an awful lot of work for essentially what I had at the beginning which is I knew up a logger I did access and call them okay why would I ever have this disconnected type architecture in all this extra work which is not ton but some creating interfaces and then the container configuration bringing Autofac wire it up calling it and all the rest why would I do that well there's a number of reasons okay but there's a couple of big ones I think the biggest one out there is that this right here allows you to test your application in a way you never could if in here you instantiated those classes so for example let's say I want to test the process data method so I create a unit test where I say call this this process data method and then make sure that the results are what we expect what's gonna happen it's gonna log an entry to my logging database wherever that is it's going to load data from a database server and even more scarily it's going to save data to my sequel server wherever my database server is and that's gonna log another entry to my logging server so now I have to go back in and clean up every time I run a unit test I have to delete those entries or I have to write the unit tasks in a way that knows what the database and knows what the log in database and those had delete them automatically that's not a good thing especially since what happens when I try and test this environment right I really shouldn't touch the data well that's not gonna work a half a touch data so now I'm create a separate database that point my unit tests to and I try keep that in sync with my production database but that's a mess you know it has causes a problem that's because of all these hard could dependencies with this right here I can test now the business logic class very easily because I can pass in whatever I logger I want and whatever I do to access I want so I can mock these which means I create a dummy logger and dummy data access class that way when I test the processed data method I can see if my dummy logger was called with this message and with this message I can see the if the load data method was called and I can see if a save data method was called and with what information so I can verify that my method worked the way as opposed to without endangering myself by touching any real data any of the log files or in the database okay so that this right here this depends the inversion principle allows me to not depend on instances specific instances so I can pass in different ones for testing that's a big deal the other thing this architecture allows us to do is it allows us to change out the actual implementations down the road so say we no longer want to have our current logging system in place no problem we could just change that in our container configuration okay maybe a business logic want to swap the whole thing out so instead of we can still use the same I business logic but instead we can call it better business logic and have that class implement this interface and then our code doesn't change a bit we just changed our business logic over to be better business logic okay and change it right here in the registration and we're good to go in fact let's go ahead and do that so we had this business logic class here let's do this let's create a new class in the demo library and we'll call it better business logic make it public and we're going to implement the I business logic interface which I could hit control dot we're going to do is since this is a demo gonna copy and paste and make sure the constructor name is changed to better business logic add my using statement so now it's exactly the same as the business logic but let's make some changes let's add some blank lines I know this is really killer stuff right here around the logging statements okay so we've added three blank lines now let's just show you what it was okay nothing's changed right now that's what it was a town crunched together now let's go over to our container config and change this to better business logic no other changes in the entire system that's all we did let's run it again and there you go there your spacing okay so with one change in one spot I just changed how my application worked because it's not tightly coupled together I have a dependency on interfaces not on implementations and I have my dependencies inverted so that top-down I pass down here's what you're going to use instead of here's what you use so I passed down better business logic now instead of the business logic when you ask for an eye business logic item now with that one tweak I change my application obviously that's not something you know earth-shattering in itself just because it's a small little change but imagine if I used to write to a sequel database and with that one change I changed over to a sequel Lite database or a my sequel database or with that one change I change over to writing the text files okay so the tournament tracker application that's in the that c-sharp application from start to finish course that I have that tournament tracker application has something similar to that okay but we're not actually doing the pants injection this way we can even easier to change out which system I use very simply okay make that one change here and my entire application can go from using text files for data storage to sequel server massive difference one little tweak if your entire application is built this way then instead of your application being is one large monolith it's a bunch of little pieces that can be swapped out whenever necessary so you want to upgrade your application no problem one module at a time swap them out and you're good to go okay you can upgrade you can move systems say you're on sequel server and a company buys you out and you have to move to Oracle not a problem with this system you build the connection to Oracle and then one day come in here where it says I data access you change it from sequel server date access to Oracle data access and that's it from in you know a snap out of fingers it's now on Oracle now of course you had to write the Oracle class but that's a small piece not change it here change it down here change it over here change it over here you get my drift okay and said change it all over you change it one spot so dependency inversion principle is a really powerful one but the dependency injection system allows us to do that to Pantheon version very easily okay so this is essentially you're placing my manual Factory with an automated one that's much much better has a lot more features and now whenever I have a constructor and I need something not a problem so let's just say in my data access I need a logger not a problem I could just say ctor and give me an ilogger okay and now I can say logger equals longer and now I can start logging logger underscore logger dot log let's just say logging data all right so it's gonna say loading didn't have a log for loading to him and the same thing for Savi did okay now that's it I don't change anything else I don't have to go instantiate this somewhere out to make sure they hit get the right one nothing I start this thing and now loading data logging loading data saving date setting process info logging saving data okay so those two logging statements they're new that's because I just said hey any a logger no problem grab it okay this makes your system disconnected and yet so easy to bring things in okay so whenever you see the equals new instance of something that's when you think you know what I could probably do that via dependency injection there are some exceptions okay you will still knew a few things up the pride of the most common thing a new up still would be models you know the just data so that can sometimes still be nude up and your don't use your two pens the injection system for that but otherwise that's about it okay so that is dependency injection again it's different from dependency inversion dependency inversion is a principle dependency injection is how you make it work one of the ways you make it work okay so they are different but using auto effect especially as you can see it's really simple you add your new yet reference to references you create your container and that's where what this is just wire everything up so your wire out the thing you're gonna instantiate to your interface or grab me a whole bunch of those and doing it once you've done that then everywhere you need an instance of something you just ask for it okay that's pretty much it the big thing to look out for is if you fail to register something okay so I ain't registered then the better business logic for I business logic if you start you'll get an error and it gives us error right here which says an error occurred during the activation of particular registration see the inter exception for details see you a few details exception inter exception now that constructors found with auto fat on fat core to activate sorry reflection yeah yada and you keep reading and it says there's a problem with I business logic at the very end okay so it's saying essentially I can't find I business logic and the reason why is because you didn't register it so make sure you register it if you do you're good to go your application starts no problem alright I hope that made sense I hope you have a much better grasp on depen see injection and realize it's no big deal okay it's a big deal from the perspective of it makes your life a whole lot easier and makes me can do a whole lot more possible but it's not a big deal as far as being scary and overwhelming all the rest you can do this it's just a little bit of configuration right there and then it is wiring and having the interfaces for things that's it and then asking foreign constructors okay now when you get into asp.net MVC or Web API or dotnet core these things will take additional configuration because they sometime or they have some sort of dependency injection already so for example in MVC the controllers are already being injected so you have to wire up Autofac and a little bit different configuration but don't worry I'm going to cover that in a different video but also there's lots of great tutorials out there that cover it and it really is just a tweak to how you do this okay there's actually even nougat packages to help you out so those aren't really a big deal but they are additional configurations but for wind forms for console applications for de UPF these things they're all already set up for you to be ready to do this really simply okay with that being said if you have any questions please put them in the comments below I read every comment that's posted if I can every one so I might miss one especially if you comment on a comment I might miss that but I try really hard to read on every comment and if you have suggestion please put it down below let me know and I will try and get that into the next video might have to do two more videos and that's okay we'll do it until you get it okay so ask your questions I'll try to answer them if possible and I will try to put them in the next video if it makes sense okay also don't forget in the description below is a link to that blog post that has the starter code and also those finish code so you can try out yourself I always encourage you rebuild the stuff yourself because it gives you the hands-on experience of doing it yourself that's so much better than just watching me do it because if you just watch me do it it's gonna make sense probably or you're gonna yep yep got it got it but you actually go out do you have to wait wait how did I do that again that's where the experience of actually typing it really helps so try and type it out yourself create yourself make it work create a few different scenarios really simple ones but redo it two or three times okay so you get the the handle on how to do this then start putting in your applications because this stuff is really powerful I definitely recommend it for most applications okay now we are going to show in a future video I'm going to show mocking with unit tests so you can take this to the next level which is testing our application because of this disconnected architecture so definitely you get to know it because once you get into mocking you're gonna want to know how to do two pansy injection in order to keep up okay that's it for this video I hope you enjoy it I appreciate it give me a thumbs up if you enjoyed it as well I'll leave a comment down below any questions or any comments you have thank you very much as always I am Tim quarry [Music] you [Music]
Info
Channel: IAmTimCorey
Views: 251,113
Rating: undefined out of 5
Keywords: .net, C#, Visual Studio, code, programming, tutorial, training, how to, tim corey, C# training, C# tutorial, dependency injection, c# dependency injection, c# dependency injection tutorial, c# dependency injection example, c# dependency injection container, container, autofac, autofac ioc, ioc, di, s.o.l.i.d principles, s.o.l.i.d principles c#, autofac tutorial for beginners, autofac tutorial c#, autofac tutorial
Id: mCUNrRtVVWY
Channel Id: undefined
Length: 54min 44sec (3284 seconds)
Published: Mon Jul 30 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.