-Hello and welcome to
this talk on Building an ASP.NET Application with MongoDB. My name is Caleb Thompson and then I'm a Developer Educator
here at MongoDB. In this talk today, I'm going to
show you how we can access data in a MongoDB store with an ASP.NET
application using the C# driver. The first thing we'll
do is we'll set up an Atlas account and install
some sample data. Atlas is MongoDB in the cloud and it’s
a free and very convenient way to get up and running quickly without
having to install MongoDB locally. We'll do that first and then we will
switch over to Visual Studio. We'll create a new ASP.NET app. We'll look at the default
code that's in there. Then we'll switch back to the slides
and spend a little bit time looking at how you can use the driver to access
the data that's sitting in Atlas. We have a bunch of choices now,
we do that. We'll look at building what they called mapping classes to
access the data easily and then we'll do some
reading and updating of the data from the application. The first thing we want to
do is set up an account in Atlas. We'll create a new shared
cluster which is free. In that cluster,
we will then import some sample data that we will use
with our application. What is Atlas? Well,
Atlas is MongoDB running in the cloud. It allows us to easily have a MongoDB
instance running without having to worry about any server maintenance
and that can save us a lot of time. You can choose to run Atlas
as we'll see in just a moment in one of the three cloud providers AWS,
Azure, or Google Cloud. It is self-serving,
elastic, scalable, global. We have enterprise-grade security,
monitoring, managed backups. There's also a serverless platform
which we won’t to look at in this talk. For a lot of reasons,
Atlas is a great way to just get up and running
with MongoDB very quickly. Of course,
you can use a local installation of MongoDB if you already have one running for this talk, but I'm going
to show you how to do it with Atlas. Let's go ahead and take
a look at Atlas. I'm going to switch to my browser here. If you don't yet have an Atlas account,
go to cloud.mongodb.com and create
a new account. It's free. Once you're in, you’ll have the option
to build a new cluster. We're going to do that. I'm going to choose
this left-hand column. You'll notice the word free here. For our purposes,
a free shared cluster is just fine. Create a cluster. As you'll see,
you can choose what cloud you want your data to be stored in,
AWS, Google Cloud Platform or Azure. I'll just stay with AWS for now. I'm on the West Coast so I'm going to pick Oregon,
not that it really matters. You can see I haven't changed anything that would charge, so it's still free. If you want,
you can change your cluster name here. I'm just going to keep
it at Cluster0 for now. I'm going to click create cluster. Now,
this process takes one to three minutes so I'm going to pause
while that catches up. There we go. We now have a new cluster named
Cluster0 that's been created for us. We have three buttons,
connect, metrics, collections. Under the fourth one,
we have some other options that we're going to take
a look at in just a minute. Let's click on the connect
button first. You'll notice when we open this dialog,
it says, "You can't connect yet. Set up your firewall access and user
security permissions below." By default, our Atlas service
is turned off or rather it's turned on,
but nobody can actually get to the data. The first thing we need to
do is whitelist the IP address or addresses for the machines
that are going to access the data. You can easily do this just by clicking
the add your current IP address. In secure way to do this
also is add a different IP address and you can
enter the site or address 0.0.0.0/0 which means
every computer can access the data or at least
theoretically can. I'm just going to add my current IP
address and click on add address. Now, the second thing we need
to do is create a MongoDB user. Let's do live2020 is the user and live2020pass as the password. I'll make sure I typed that correctly. Yes. Then click on create MongoDB user. Of course,
if you're following along, you can create a username and password
or whatever you want. Your IP address obviously is going
to be different from mine. Click on create MongoDB user,
I don’t want to save it. That's it for now.
We're going to come back to this choose a connection method in a few minutes. Let's go ahead and close that. You'll notice now that Atlas
is updating with our IP restriction and our new user
it should be just a second. When that's done,
we will import some sample data that we'll use
for the rest of this talk. Okay, we have added our IP
address to the whitelist. We've created a MongoDB
user and with a password. Now,
let's go into this ellipsis and we're going to choose load sample dataset. Now, obviously, if you're using your
own data, you don't need to do this. This is really handy though if
you're giving a talk or demo. It just says, "Are you sure you want
to install this sample dataset?" We'll go ahead and load that. Now, we'll give another
minute or so to import all of that data and then we'll
explore it for just a minute. Okay, it is now loaded. Let's take a quick moment to
explore what that might look like. Let's go to collections. We can see that a bunch of data
has been installed for us. It looks like about one,
two, three, four, five, six, seven collections, sample airbnb,
sample analytics, et cetera. You can use any of this
data for your projects. There's a good diversity here. In particular for this talk,
I'm going to use under the sample
geospatial collection. There is a database called shipwrecks. If we take a look at that, we can see that there are 11,095
shipwrecks in this collection with some
fields like feature type, chart, latitude, longitude, water level. Might be some interesting stuff here. We're going to go ahead and use
this for the rest of our talk. Now that we have a data store
we can play with, let's go ahead and switch to Visual Studio
and get to work on building an app. We're going to create
a new ASP.NET Core Web App. We're going to use NuGet to
import the MongoDB driver. We'll initialize the MongoDB client. We'll instantiate
a database and collection object and then we'll read some data. Let's go ahead and switch
over to Visual Studio, and we will choose to
create a new project. We want to choose
an ASP.NET Core Web App. Next. We'll keep the default name. Create. Let's choose, let's see here, a React.js app. We don't have to do any
frontend work in this. I like React. It's handy so let's just go
ahead and choose that for now. We'll keep it simple. We will create. It has now created a new app for us. Without doing anything else,
let's go ahead and run it and just
see what it looks like. Now, the first time you run it,
you'll notice in the output console window there that
it is restoring dependencies using npm. That's all necessary
for the React frontend. Again, it's something we don't
need to worry about in this talk. It takes a minute the first
time you run the app and then after that,
we shouldn't even notice it. I'll also note while we're
waiting that I'm running this in Visual Studio 2019 Community
Edition on a Windows box. Everything I'm doing though
works just the same on a Mac in the MacOS with
Community Edition 2019. Here is our default application. Nice.
Hello, World page rendered in React. You'll notice that there is a link over in the upper-right
corner called fetch data. What this does is it loads
a React page that behind the scenes is calling
our controller and fetching data. If we look at the developer
console on this, make that a little bigger
and we fresh the page, you'll see that it is calling a get call here to our
localhost/weather forecast, which is the name
of our controller. I'm going to go ahead
and copy and paste that. What I’m doing here is I’m just
bypassing the front-end the stuff that makes it look pretty
and you can see that the controller is currently returning an array
of JSON objects or a collection rather that have a day,
the temperature, and a summary. If we go back and look at our code,
let's go ahead and stop this. We go to our weather forecast
controller right here. You can see that when
we call the GET method which is what's happening behind the scenes,
we are returning a random selection of these
summaries up here as well as generating some
random temperature ranges. Interesting but not all that
useful because it's all fake data. Obviously, what we want to do here
is use our sample data in MongoDB. Let's look at how we do that. I'm going to clean
up a little bit here. I'm going to get rid of this
static summary array of strings. I will leave the logger for now. In the get, I'm just going to get
rid of all of the code for a moment. None of that is useful to us other
than showing us that it works. I'm just going to add
a return null for the moment. What we need to do now
is add the MongoDB Driver to our project so we can
actually access some data. There are several ways
you can get to this. We're going to use NuGet. I like to right-click
on the dependencies and choose manage NuGet packages. If you're using Visual Studio
on the Mac your UI will look a little different but the idea is the same, you want to find manage NuGet packages. Look over there
and you need to switch to the browse tab and we're
going to type in MongoDB. A surprising number
of results come back. The one we're interested in is in my
case, the second one, MongoDB.driver. I'm going to choose that. Make sure it says,
“Latest stable version,” and install. It'll ask us to accept the license
and of course, we will. That's it,
we can switch back to the installed tab and see that
it has indeed been installed. We can also see it here
under dependencies in case you really want to believe it. Under packages, there it is. Let's switch back to
our controller and what we need to do now
is create a Mongo client which will define how
we connect to our datastore whether it's local
or in Atlas or wherever. If our client equals new Mongo client
and it's not going to recognize Mongo client until we use the namespace MongoDB.driver and there it is. Now, when you set up a client,
you have to pass in the connection string, the URI to where
wherever your datastore is, whether it's local or an Atlas. It's not something I have in the top
of my head but fortunately, we can go back to our Atlas page
and go to our clusters right here. You'll remember this connect
button from a few minutes ago where we set up the IP
whitelist and our database user. This time when I connect on it,
I'm going to choose the second option down,
connect your application. Make sure you select
under the driver the C# .NET driver and the latest version. Then you'll notice right
here the connection string has already
been generated for us. I'm just going to click copy,
switch back to Visual Studio and paste
it right in there. Now, you'll notice that
in the connection string, our username has been added but not
the password for security reasons. Let's type that in live2020pass. Now, we have a Mongo client. This is pointing to our Atlas store. What we now need to do is tell
our application which database and collection we actually want to use,
what data do we want to get. I'm going to switch back to
Atlas again for a moment. I'm going to close that screen
and go to collections again. We decided we're going
to use the sample geospatial database with
the shipwrecks collection. sample_geospatial. Here I can do
var database=client.GetDatabase. In there again we put in a string, sample_geospatial, like so. Then from that database object
we can create a collection. var collection=database.getCollection and it is called shipwrecks. Now, you'll notice that we've
got an error here in the IEE. getCollection expects us
to tell it what type of data we're going to be getting
and we don't know yet. If we look at our shipwrecks
data here in Atlas as we did a few minutes ago,
it's just data. It's a bunch of JSON documents
we're going to get back. We're going to need to
do something here to translate this JSON to something in C#
that makes sense to us. If we don't want to, if you don't know what's going on you just
want to have a quick and down and dirty way to get the data,
you can always pass in BSON
document object type. This is generic,
it always fills in automatically. This is a generic class that
defines any JSON object. You have to import
the MongoDB.BSON namespace. That will work,
but it might not be very useful for us. We now have a client, object
it is pointing to our Atlas cluster. Within that cluster we're
telling the driver that we want to use the sample
geospatial database. Within that database,
we're specifically looking at the shipwrecks collection. With that information,
we can then do something like collection.find and get
out the data we want. We can specify what we want. Before we go any further though,
I'm going to switch back to the slides so we can look at several different ways we can access the actual
data using the driver. There are four ways we'll
look at and then we'll switch back to our code
and make it happen. Let's spend a minute talking
about how we actually access data in MongoDB from the driver. There are four main approaches
we can take and here they are. You can use the MongoDB
query language MQL. You can use the BSON document object. The driver provides these things called builders and we'll
take a look at those. Then finally the most
robust way to do it is to build mapping classes and use the link. Hopefully, you'll see why
a little bit of time spent upfront using mapping classes will save
you a lot of time later on. Well, let's go ahead and take
a look at the four ways because these are all valid approaches
you can use depending on your needs. We're going to assume
for a minute here that we have a collection somewhere
in MongoDB of guitars. Each guitar has four properties,
an ID, make, model, and price. We're going to use this, we're going to pretend we're going to
query for all guitars over $400 and we'll look at those four different approaches
to doing just that. This is what the collection
would look like in MongoDB. We've got in this example four JSON
documents, each one has the same four properties _ID, make which is a string, model is a string, price is a double. Simple data here. If you're familiar with
the Mongo query language, you can continue to use
that by simply passing a string that contains
valid MQL into your find or any of the other
CRUD methods we've got. In this case, we're creating
a string property called filter. It contains a valid MQL
statement which is saying, “I want all of the documents where
the price is greater than 400,” and we're passing that in and we will
indeed get back what we expect. Now, if you've got something you want to do very quickly and you're familiar
with MQL and you're good at typing, this is a perfectly valid way to do it. It is not however at all type-safe. If you typed price wrong
or if price were capitalized in the database or something like that,
it's not going to work. You won't know it won't work,
you just won't get the data you expect. Your users won't get
the data they expect. Works, but not great. Now, you can also use a BSON
document object with the driver. What this does is it provides
a little more structure around your MQL,
but not a whole lot. As you can see in the bolded line here
we're creating a new BSON document. We're saying the field we want to
query on is price and the value is another BSON document,
which is greater than $400. It does exactly the same
thing as this previous slide right here,
but it's wrapping it in a BSON document. It provides a little bit of type safety,
but again, not very much. Now, if we had a slightly more
complex query, very slightly, like we want to get all the guitars
between $400 and $600 in MQL, you can see the line up here,
you create an and, $and and specify a price greater than
$400 and a price less than $600. Converted to the BSON document. Now we have to create a BSON array
for those various properties. It gets a little bit longer
and you can see that if you had a complex query using
the BSON document approach really can lead to some pretty long wordy
code verbose which is one of the reasons why the driver
provides this builders class. Now, a builders class, you specify
the type of document you want to build. You say you want to
build in this example a find filter called filter and you can see that the builder now has some
properties like GT for greater than. You can pass in an ampersand for and. It also has an LT for less than,
so that same query of all guitars, more than $400 but less than $600 can
be handled in a more graceful way. You don't have to
remember to do $GT or any of the other MQL specifics
to get this to work. It's also a lot shorter
than using a BSON document. This is definitely better. Still not my preferred approach. My preferred approach is much more .NET friendly and that
is using mapping classes. A mapping class is just a C# class that defines the properties
that we expect in the data and that we care about and that we want to use. We can name them whatever
we want and then we can tell the driver how to map those to
the actual data in the data store. As you can see here,
we have a class called a guitar. It has four properties in ID,
MAKE, MODEL, and PRICE. They're all capitalized
in this case because that's how we generally
define properties in C#. Here's a side-by-side comparison
of the mapping class on the right and the actual guitar object
as it's stored in MongoDB. Now, by convention a public
property named with a lowercase id,
or _id is always used as the identifier. You can specify a different field if you want by using
the BSON _id attribute. We'll look at attributes
in the next slide, but you can see that
otherwise this will work. We haven't told the driver
anything about how to map these, but because they have the same
names other than the initial capitalization, the driver
is smart enough to say, “You're looking for something called Make
with a capital M. Here's a make in the data store with a lower M.
I'm going to assume they're the same.” Now, assumptions can be dangerous. So the driver also
provides attributes that we can use to make
things very explicit. The BSON _id attribute
says even though I've named this property guitar _id,
it is the _id for the document and so it's going to be mapped to the _id in the data store. Likewise, I don't really like
the names MAKE, MODEL, and PRICE. I want them to be more user friendly. I'm going to call the manufacturer,
model name, and price in dollars. For each of these I'm going to
use the BSON element attribute, passing in the name of the element
as it exists in the data store. Now, we have a C# class called guitar,
it has property names that we like and we want
to use and we're telling the driver explicitly how to
deserialize and serialize between this and MongoDB
and the fields that are there. Now, that we have a mapping class,
we can use a link. Now, a link is the query language
within .NET framework that allows us to do structured querying against any number of different
kinds of data stores. Of course, the driver implemented
it to work with MongoDB. In this case,
we have expensive guitars variable reassigning,
which is the guitars collection where the guitar.price
is greater than 400 and we're going to
return that as a list. Very straightforward link
statement right here and you'll notice that we have 100% type-safety. We can't do G.Price with a lowercase P or misspell it,
P-R-C-E, something like that. The compiler will yell at us. Actually the _id will yell
out us before we even compile. Using a mapping class
and then using a link provides us with a great
type of security. Makes it really easy to write
complex statements using a link which most of us are probably
familiar with at this point. If you're not, it's time to be. Here's a more complete example where we have the collection you saw a minute
ago in Visual Studio, we Nu-ed up a collection and we can
now call on that collection the find method passing in the link statement,
specifying how we want to filter,
what we actually want to find, and then we're returning it as a list. Now that we have all of that,
let's go back to our app and do some refinement
and see if we can actually display data from MongoDB
in our what is currently a weather data app,
but we're going to use the shipwrecks. Here we are back in Visual Studio,
back in our controller and we've got our client,
our database, our collection. Right now the collection
is saying it's of type BSON document,
but that's not very helpful to us. We now know we want to
create a mapping class. Let's go ahead and do that. I’m going to right-click and choose
add class and we'll call it shipwreck. You can call it whatever you like,
but obviously a name that is sensible is always good. Now, I'm going to go look at our data in shipwreck again
for just a moment here. I’m going to clean
that up and shipwrecks. It looks like we have an _id
and a whole bunch of fields, some of them strings, some of them doubles
that may or may not be useful. Let's start with _id
feature type chart. We have public object
_id and it's called _id and we'll put
a getter setter on that. It doesn't seem to
recognize this object _id. We need to import MongoDB.Bson. Then we have a public string feature type, getter setter. We have a public stream chart. What else? Now,
this latdec and longdec are doubles or decimal values
of the latitude and longitude. Now, it seems really
important if you're doing some app that's showing
where shipwrecks are. Let's go ahead and include those too. Public double latitude. You'll notice that I'm not
using the names that are in MongoDB because latdec
and longdec mean nothing to me. I'm using something that
makes a lot more sense. If you don't know about latitude
and longitude, don't worry, that's just a way describing where something
is on a map or on the planet actually. Now, notice that I didn't
use the same names so we need to go back and we need to
do some BsonElement, attribute element and we pass
in the name of the element as it appears in MongoDB,
so that was called FeatureType. I'm going to go back
and double-check these and again we need to add a reference
here to BSON serialization. Okay, let's make sure we get the names feature _type chart, latdec, longdec. Let's go ahead and copy
and paste a little bit. We don't need to add one
for chart interestingly, I'm doing it anyway
for the sake of being thorough, but again,
the driver is smart enough to recognize that the only difference
is the capitalization. Now, this was latdec and longdec. I'm going to just double-check those,
latdec, longdec. We can specify here that
this is the BSON _id. Now, there are also a whole bunch of fields in the database
that for the time being,
at least we don't care about, okay, I don't know what this GP quality is. Depth might be interesting,
but for now we want to ignore a bunch of stuff and there
are two ways we can do that. We can create a public property of type object, an array of objects
called whatever you want. The bucket I call it,
once you get them set. If we put-- It would
help if I could type. If we put on this bucket a BSON or an attribute called
BsonExtraElements. Anything that the driver
finds when it's serializing or de-serializing that
it doesn't recognize as one of the properties we have already
specified, it will throw it into this object
array as a key-value pair. That's one way to do it. The problem with that is you end
up with an array of key-value pairs that you still don't
really care about so you're getting back more data
than you need and somewhere on the front end,
you're going to have to deal with that. Another option is to get
rid of that and on the class itself add a BSON ignore
extra elements tag. That just tells the driver to throw
away anything you don't recognize. We don't care about it,
for now, very handy and that's it. Now, we have a shipwreck mapping
class with names that we like, that means something a little
more useful than what's in Mongo. Let's go back to
our controller and here we're going to change
our get collection from a type of BSON document to a type of shipwreck because
we now can do that. Then let's do a find
and see what happens here. Let’s do--We want to return the collection.find
we'll add some link here. Let's see, let's find shipwrecks where
the feature type is wrecks visible. The first one that in that I see here. S.feature type equals wrecks visible. It's yelling at us because
we're supposed to be returning an innumerable,
so let's do a Tolist. Now, we're asking it to return
an innumerable of a type called weather forecast,
and that's no longer valid, so we'll change that to shipwreck
and we'll get rid of this return null line it's no longer
needed and that should do it. Let's see if we did it right. Build the app. We'll open up our console again,
we'll go to fetch data. Again, I'm going to go ahead
and bypass the front end altogether and just go straight to the API
call and call it the controller. Look at that, we have a page now
returning a whole bunch of JSON objects. They have an _id, feature type, which
again, we specified wrecks visible. If we go through this
entire list of hundreds of them,
they should all have wrecks visible. Indeed they do. I guess two, isn't proof, but there it is and we got our latitude
and longitude back. That's how we connect
our driver to the MongoDB store. Now, this is great. We've got it working
just like we want it to, but there's a little
bit of a problem here. That problem is that within the method of the controller,
within the GET method, we're creating a new Mongo client every
time, a new database object, a new collection every time and if you're
following MVC patterns, this probably smells a little to
you and that's because we don't need to do this every time. This controller,
which is still named our weather forecast controller, but our shipwreck controller probably is always going to be using the same
collection shipwrecks. There's no point in having all of this code in every method of the controller. In fact,
we may have multiple controllers in our project that are looking
at different collections within the sample geospatial database,
or maybe they're even still using
the same collection. There's gotta be a way
we can clean this up a little bit more and I want to take a quick look at that
because this is not a best practice the way
I've got it right now. In ASP.NET applications,
there is a Startup.cs file right here. This is where we do all
of our initialization code. It seems logical that
we would initialize our Mongo client here
and then have the ASP.NET framework, inject that client into our controller where
we can then use it. Let's go back to
our controller for a minute. What I'm saying is we want
to inject this client. We don't want it here anymore. I'm going to get rid
of this on this constructor right now,
it's expecting an ILogger object. I'm going to get rid
of that and what I really want to have come in here
is a Mongo client. We'll just name a client for now,
and I'm going to get rid of this property,
this private locker. Instead,
I'm going to do a private IMongo. Now,
let's assume and I think this is a safe assumption that every method we want to do in this controller
is always going to be using these
shipwrecks collection. It is after all
the shipwreck controller. Let's do an IMongo collection and we'll
name it, shipwreck collection. You can name it,
whatever you'd like Shipwreckcollection and it's better if
you spell things correctly. What we're going to want to do is set here the code that when we get the client in,
we create the collections. Let's take this code out of our method. These two lines,
I'm going to cut those, put them in our constructor, change this
now to shipwreckcollection as such. Now, when this controller
is initialized by the framework, we need it to pass
in an IMongo client object. We will use that client
to get the database. We will use the database
to Nu-up the collection, and then down here,
we get rid of the client line down here,
and in the method we just change collection to
shipwreckcollection. Now, it's yelling at something here. We haven't specified the type.
It is of type shipwreck. In our startup.cs in the configure
services method, we want to create a new Mongo client
that will get injected. Let's do services.addsingleton, specify the type of Mongo client. We want a Mongo client out of this deal,
and let's see here,
we'll use a little bit of link. What do we want to do? We want to return a new Mongo client like so. Here we would pass
in that same URI that we had in our controller to
go back to our controller. Do we still have it? No, I deleted it. That's okay. That's one way to do it. A slightly better way
to do it is ASP.NET projects come with
an appsettings.Json file. This is a convenient place
to store configuration information, for example, a MongoURI. You can create a new property
and you can set it to whatever you want. Let's go back to our Atlas and we'll go to our cluster. Go to connect.
Connect your application. Now,
if I hadn't have deleted it already out of my project,
I would just copy it from there. We'll copy that string one more time.
Paste it in here. Again, we need to put
in the password live2020pass. Make sure that's all saved correctly. Now, that we have that,
go back to our start up again and what we want to pass in here
is some URI and that URI. We want to do s.GetRequiredService. Is of Type IConfiguration
and we want to specifically ask for the MongoURI
property we just created. We got the variable there.
That should do it. I'm going to double-check
that I got MongoURI that matches the property name here,
MongoURI. Now, we're saying,
“Within the configuration service, get the MongoURI property,
set it to this string variable. Then Nu-up a Mongo client passing
in that connection string. At this point now,
we should have automatic injection of this client
into our controller. We'll then create the database. We will assign
the shipwrecks collection to the shipwreck collection global. In our get method now,
we should see exactly the same data. Let's fire it up
and see if did it right. There is our page.
We'll go to fetch data and we'll go directly to the backend and sure enough we're still
getting the data we expected. Except now, we're not doing it within the controller method
itself and we can reuse this shipwreck collection throughout the controller for all
of our different methods. Much cleaner, much better practice. With that,
let's switch back to the slides. In this talk,
I showed you how to set up a new Atlas account and a new
cluster within that. We imported some sample data
that we used for our app. We then switched to Visual Studio,
created a new ASP.NET app. From there, I showed you how to
import the MongoDB driver using NuGet. Once we had the driver installed,
we initialized a new Mongo client passing in the URI
to our Atlas data store. We created then a database and collection object
off of that client. We then switched and talked
about how to access the data and all
of the different choices. While all of them are valid choices,
it's generally a very good idea to create mapping classes for the objects
that are stored within MongoDB. We did that.
We created a shipwreck mapping class. Then finally,
we did a find specifying a particular subset of the data
that we wanted to get. We then did a little
bit more refinement of the app moving
the creation of the Mongo client out to the startup
file and adding the URI to the appsettings.json file. With that,
I would like to thank you very much for joining me today in this talk. I'd also like to give
a quick shoutout to a new course we released just
this spring called M220N. It's the .NET version of 220. It's a five-week course
where we dive into using the .NET Driver
in much greater detail. You get the opportunity
to work on a website called MFlix which is a movie database. We walk you through a lot more things
that you can do with the driver including some fairly complex work
with the aggregation pipeline. I encourage you to check that out. It's university.mongodb.com
and just search for M220N. Thank you very much
for attending this talk. I hope you enjoyed it. I hope you learned a lot and I look
forward to your questions.