Exercise 1: Listing and modifying products¶
You can earn 7 points with the completion of this exercise.
This exercise will implement CRUD (create, retrieve, update, delete) operations for Product entities.
Open the Visual Studio solution¶
Open the Visual Studio solution (the .sln) file in the checked-out repository. If Visual Studio tells you that the project is not supported, you need to install a missing component (see here).
Do NOT upgrade any version
Do not upgrade the project, the .NET Core version, or any Nuget package! If you see such a question, always choose no!
You will need to work in class mongolab.DAL.Repository! You can make changes to this class as long as the source code complies, the repository implements interface mongolab.DAL.IRepository, and the constructor accepts a single IMongoDatabase parameter.
The database access is configured in class mongolab.DAL.MongoConnectionConfig. If needed, you can change the database name in this file.
Other parts of the application should NOT be modified!
The web application is a so-called Razor Pages ASP.NET Core project. It includes a presentation layer rendered on the server using C# code and the Razor engine. (You do not need to concern yourself with the UI.)
Start the web app¶
Check if the web application starts.
-
Compile the code and start in Visual Studio.
-
Open URL http://localhost:5000/ in a browser.
If everything was successful, you should see a page with links where you will be able to test your code. (The links will not work as the data access layer is not implemented yet.)
Display the Neptun code on the web page¶
You will need to create screenshots that display your Neptun code.
-
Open file
Pages\Shared\_Layout.cshtml. In the middle of the file, find the following section, and edit your Neptun code.<div class="container body-content"> @RenderBody() <hr /> <footer> <p>@ViewData["Title"] - NEPTUN</p> </footer> </div> -
Compile the code and start the app again, then check the starting page. You should see the Neptune code at the bottom of the page.

IMPORTANT
The Neptun code is a mandatory requirement in the footer!
Listing¶
-
First, you will need a way to access the
productscollection from C#. Create and initialize a new variable that represents the collection in classRepository. Use the injectedIMongoDatabasevariable to get the collection:private readonly IMongoCollection<Entities.Product> productCollection; public Repository(IMongoDatabase database) { this.productCollection = database.GetCollection<Entities.Product>("products"); } -
You can use
productCollectionto access the database's product records from now on. Let us start by implementingListProducts. This will require two steps: first, to query the data from the database, then transform each record to an instance ofModels.Product.The query is as follows:
var dbProducts = productCollection .Find(_ => true) // listing all products hence an empty filter .ToList();All items are then transformed.
return dbProducts .Select(t => new Product { ID = t.ID.ToString(), Name = t.Name, Price = t.Price, Stock = t.Stock }) .ToList(); -
The implementation of
FindProduct(string id)is similar, except for querying a single record by matching theID. Pay attention to the fact that theIDis received as a string, but it needs converting toObjectId.The transformation to the model remains identical. However, we should also handle when there is no matching record found and return a
nullvalue in this case (without converting anything to a model).The query is as follows:
var dbProduct = productCollection .Find(t => t.ID == ObjectId.Parse(id)) .SingleOrDefault(); // ... model conversionNote how the filter expression looks like! Also, note how the
ToListis replaced with aSingleOrDefaultcall. This returns either the first (and single) element in the result set ornullwhen there is none. This is a generic way of querying a single record from the database. You will need to write a similar code in further exercises.The conversion/transformation code is already given; however, we should prepare to handle when
dbProductisnull. Instead of conversion, we should returnnullthen. -
Test the behavior of these queries! Start the web application and go to http://localhost:5000 in a browser. Click
Productsto list the data from the database. If you click onDetailsit will show the details of the selected product.
If you do not see any product
If you see no items on this page, but there is no error, it is most likely due to a misconfigured database access. MongoDB will not return an error if the specified database does not exist. See the instructions for changing the connection details above.
Creation¶
-
Implement the method
InsertProduct(Product product). The input is an instance ofModels.Productthat collects the information specified on the UI. -
To create a new product, we will first create a new database entity (in memory first). This is an instance of class
Entities.Product. There is no need to set theID- the database will generate it.Name,PriceandStockare provided by the user. What is left isVATandCategoryID. We should hard-code values here: create a new VAT entity and find a random category using Robo3T and copy the_idvalue.var dbProduct = new Entities.Product { Name = product.Name, Price = product.Price, Stock = product.Stock, VAT = new Entities.VAT { Name = "General", Percentage = 20 }, CategoryID = ObjectId.Parse("5d7e4370cffa8e1030fd2d99"), }; // ... insertionOnce the database entity is ready, use
InsertOneto add it to the database. -
To test your code, start the application and click the
Add new productlink on the products page. You will need to fill in the necessary data, and then the presentation layer will call your code.
Delete¶
-
Implement method
DeleteProduct(string id). UseDeleteOneon the collection to delete the record. You will need a filter expression here to find the matching record similarly to how it was done inFindProduct(string id). -
Test the functionality using the web application by clicking the
Deletelink next to a product.
Modification¶
-
We will implement the method
bool SellProduct(string id, int amount)as a modification operation. The method shall returntrueif a record with a matchingidis found, and there are at leastamountpieces in stock. If the product is not found or there is not enough in stock returnfalse. -
Using the atomicity guarantees of MongoDB, we will perform the changes in a single step. A filter will be used to find both the
idand check if there are enough items in stock. A modification will decrease the stock only if the filter is matched.var result = productCollection.UpdateOne( filter: t => t.ID == ObjectId.Parse(id) && t.Stock >= amount, update: Builders<Entities.Product>.Update.Inc(t => t.Stock, -amount), options: new UpdateOptions { IsUpsert = false });Note that the
UpdateOptionsis used to signal that we do NOT want as upsert operation; instead, we want the operation to do nothing when the filter is not matched.The modification is assembled using
UpdateinBuilders. Here we want to decrease the stock value withamount(which is, effectively, an increase with-amount).We can determine what happened based on the
resultreturned by the update operation. If the result indicates that the filter matched a record and the modification was performed, returntrue. Otherwise, returnfalse.return result.MatchedCount > 0; -
Test the functionality using the web application by clicking the
Buylink next to a product. Verify the behavior when you enter a too large amount!
SUBMISSION
Create a screenshot of the web page listing the products after successfully adding at least one new product. Save the screenshot as f1.png and submit it with the other files of the solution. The screenshot shall display the list of products. Verify that your Neptun code is visible on the image at the bottom of the page! The screenshot is required to earn the points.