Inventory Database Example

This small inventory database program is building on the last example on the separation of concerns. I’ve added some try/catch and exceptions. Both normal and throwing new ones. This has shown me how the exceptions can be passed thru the layers of the program. And from there how to display the error, log the error, etc… Also you will notice the links enabling the user to view, add, and remove items from the database. You will be able to see how things are done in the code.

InventoryDbProgram InventoryDbSolution InventoryDbTable

 
Default.aspx HTML code

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Inventory.Presentation.Default" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div id="mainForm">
        <asp:GridView ID="inventoryGridView" runat="server" OnRowCommand="inventoryGridView_RowCommand" AutoGenerateColumns="False">
            <Columns>
                <asp:ButtonField Text="View" CommandName="ViewItem" />
                <asp:ButtonField Text="Remove" CommandName="RemoveItem" />
                <asp:ButtonField CommandName="EditItem" Text="Edit" />
                <asp:BoundField DataField="InventoryItemID" HeaderText="Item ID" />
                <asp:BoundField DataField="ItemManufacturer" HeaderText="Item Manufacturer" />
                <asp:BoundField DataField="ItemName" HeaderText="Item Name" />
                <asp:BoundField DataField="ItemModel" HeaderText="Item Model" />
                <asp:BoundField DataField="ItemSerialNumber" HeaderText="Serial Number" />
                <asp:BoundField DataField="ItemNotes" HeaderText="Notes" />
            </Columns>
        </asp:GridView>
        <br />
        <asp:Label ID="resultsLabel" runat="server"></asp:Label>
        <br />
        <p><strong>Add An Item To Inventory</strong></p>
        <div id="newTable">
        <p>
            <asp:Label ID="itemManufacturerLabel" runat="server" Text="Item Manufacturer: "></asp:Label>
            <br />
            <asp:TextBox ID="itemManufacturerTextBox" runat="server"></asp:TextBox>
            <br />
            <asp:Label ID="itemNameLabel" runat="server" Text="Item Name: "></asp:Label>
            <br />
            <asp:TextBox ID="itemNameTextBox" runat="server"></asp:TextBox>
            <br />
            <asp:Label ID="itemModelLabel" runat="server" Text="Item Model: "></asp:Label>
            <br />
            <asp:TextBox ID="itemModelTextBox" runat="server"></asp:TextBox>
            <br />
            <asp:Label ID="serialNumberLabel" runat="server" Text="Item Serial Number: "></asp:Label>
            <br />
            <asp:TextBox ID="itemSerialNumberTextBox" runat="server"></asp:TextBox>
            <br />
            <asp:Label ID="itemNotesLabel" runat="server" Text="Item Notes: "></asp:Label>
            <br />
            <asp:TextBox ID="itemNotesTextBox" runat="server" Width="223px" Height="55px" TextMode="MultiLine"></asp:TextBox>
            <br />
            <br />
            <asp:Button ID="saveDataButton" runat="server" Text="Save Data" OnClick="saveDataButton_Click" />
        </p>
        </div> 
    </div>
    </form>
</body>
</html>

A pretty basic page for this program. A gridview that will display the commands and table data from the database. And some labels and text boxes that are used to enter new data. Also the textboxes can be used when the edit link is clicked. These will populate with the data so it can be edited.

 
Default.aspx.cs code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Inventory.Presentation
{
    public partial class Default : System.Web.UI.Page
    {
        // Method that's used to update the gridview with the latest info from the database.
        private void refreshGridView()
        {
            try
            {
                // Calling a method in the Domain Layer to get the contents of the database.
                // Mainly used to display the complete contents of the Inventory\MainInventory database\table.
                List<DTO.InventoryDTO> inventory = Domain.InventoryManager.GetInventory();

                // Binding the gridview datasource.
                inventoryGridView.DataSource = inventory.ToList();
                inventoryGridView.DataBind();
            }
            catch (Exception ex)
            {
                resultsLabel.Text = ex.Message;
            }
        }

        // Method that can be used to clear any data from the text boxes.
        private void clearTextBoxes()
        {
            itemManufacturerTextBox.Text = String.Empty;
            itemNameTextBox.Text = String.Empty;
            itemModelTextBox.Text = String.Empty;
            itemSerialNumberTextBox.Text = String.Empty;
            itemNotesTextBox.Text = String.Empty;
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            if(!Page.IsPostBack)
            {
                refreshGridView();
            }
            else
            {
                resultsLabel.Text = String.Empty;
            }
        }

        protected void inventoryGridView_RowCommand(object sender, GridViewCommandEventArgs e)
        {
            // Determining the row number of the item the user clicked on.
            GridViewRow row = inventoryGridView.Rows[Convert.ToInt32(e.CommandArgument)];

            // Getting the inventory items GUID.
            Guid inventoryItemGuid = Guid.Parse(row.Cells[3].Text);
            
            // Determining what command was clicked on by the user.
            string commandName = e.CommandName;
            if (e.CommandName == "ViewItem")
            {
                try
                {
                    // Getting the item that was clicked on using its GUID and then
                    // displaying the items information.
                    DTO.InventoryDTO inventoryItemInfo = Domain.InventoryManager.ViewInventoryObjectInfo(inventoryItemGuid);
                    resultsLabel.Text = String.Format(
                        "<strong>Manufacturer:</strong> {0}<br><strong>Name:</strong> {1}<br><strong>Model:</strong> {2}<br><strong>Serial#:</strong> {3}<br><strong>Notes:</strong> {4}<br>",
                        inventoryItemInfo.ItemManufacturer,
                        inventoryItemInfo.ItemName,
                        inventoryItemInfo.ItemModel,
                        inventoryItemInfo.ItemSerialNumber,
                        inventoryItemInfo.ItemNotes);
                }
                catch (Exception ex)
                {
                    resultsLabel.Text = ex.Message;
                }
            }
            else if (e.CommandName == "RemoveItem")
            {
                try
                {
                    // Calling the domain layer method to remove the item from inventory.
                    Domain.InventoryManager.RemoveInventoryObject(inventoryItemGuid);
                    refreshGridView();
                }
                catch (Exception ex)
                {
                    resultsLabel.Text = ex.Message;
                }
            }
            else if (e.CommandName == "EditItem")
            {
                try
                {
                    // Calling the domain layer method to edit the item in inventory.
                    DTO.InventoryDTO inventoryItemToEdit = Domain.InventoryManager.EditInventoryObject(inventoryItemGuid);

                    // Adding the inventory items details to the text boxes to be edited.
                    itemManufacturerTextBox.Text = inventoryItemToEdit.ItemManufacturer;
                    itemNameTextBox.Text = inventoryItemToEdit.ItemName;
                    itemModelTextBox.Text = inventoryItemToEdit.ItemModel;
                    itemSerialNumberTextBox.Text = inventoryItemToEdit.ItemSerialNumber;
                    itemNotesTextBox.Text = inventoryItemToEdit.ItemNotes;

                    refreshGridView();
                }
                catch (Exception ex)
                {
                    resultsLabel.Text = ex.Message;
                }
            }
        }

        protected void saveDataButton_Click(object sender, EventArgs e)
        {
            // Create a DTO object for the new inventory item to be added.
            DTO.InventoryDTO newInventoryObject = new DTO.InventoryDTO();

            // Populating the necessary fields of the object with what the user types in.
            newInventoryObject.InventoryItemId = Guid.NewGuid();
            newInventoryObject.ItemManufacturer = itemManufacturerTextBox.Text;
            newInventoryObject.ItemName = itemNameTextBox.Text;
            newInventoryObject.ItemModel = itemModelTextBox.Text;
            newInventoryObject.ItemSerialNumber = itemSerialNumberTextBox.Text;
            newInventoryObject.ItemNotes = itemNotesTextBox.Text;

            try
            {
                // Calling the domain layer method to add the new inventory item to the database.
                Domain.InventoryManager.AddInventoryObject(newInventoryObject);
                refreshGridView();
                clearTextBoxes();
            }
            catch (Exception ex)
            {
                resultsLabel.Text = ex.Message;
            }
        }
    }
}

Let’s start in the presentation layer. The main page resides in this layer since it presents details to the user to add, edit, or remove data. I created a simple private method to display the data on initial page load or everytime another method calls on it. There is also a method that will clear the textboxes. This is so the data isn’t persistent during a page reload.

The gridview does have an event handler tied to it. This method determined if the data field is to be viewed, edited, or deleted. Each of these actions calls upon methods in the domain and persistence layers.

And finally there is an event handler for the Save button. This creates the database object based on what the user has entered in the text boxes. Then calls on methods in the domain and persistence layers.

If any exceptions are thrown the error messages will be displayed at this layer.

 
InventoryManager.cs code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Inventory.Domain
{
    public class InventoryManager
    {
        // Method that talks to the Persistence layer to get the database contents.
        // Mainly used to display the complete contents of the Inventory\MainInventory database\table.
        public static List<DTO.InventoryDTO> GetInventory()
        {
            try
            {
                List<DTO.InventoryDTO> dtoInventory = Persistence.InventoryRepository.GetInventory();
                return dtoInventory;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        // Method that talks to the Persistence layer to get the selected items
        // information from the database.
        public static DTO.InventoryDTO ViewInventoryObjectInfo(Guid inventoryItemGuid)
        {
            try
            {
                DTO.InventoryDTO inventoryItemInfo = Persistence.InventoryRepository.ViewInventoryObjectInfo(inventoryItemGuid);
                return inventoryItemInfo;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        // Method that talks to the Persistence layer to add a new item to the
        // database.
        public static void AddInventoryObject(DTO.InventoryDTO newInventoryObject)
        {
            try
            {
                Persistence.InventoryRepository.AddInventoryObject(newInventoryObject);
            }
            catch(Exception ex)
            {
                throw ex;
            }
        }

        // Method that takes care of removing the selected item from the inventory
        // database.
        public static void RemoveInventoryObject(Guid inventoryItemGuid)
        {
            try
            {
                Persistence.InventoryRepository.RemoveInventoryObject(inventoryItemGuid);
            }
            catch(Exception ex)
            {
                throw ex;
            }
        }

        // Method that takes care of editing the selected item from the inventory
        // database
        public static DTO.InventoryDTO EditInventoryObject(Guid inventoryItemGuid)
        {
            try
            {
                DTO.InventoryDTO inventoryItemToEdit = Persistence.InventoryRepository.EditInventoryObject(inventoryItemGuid);
                return inventoryItemToEdit;
            }
            catch(Exception ex)
            {
                throw ex;
            }
        }
    }
}

This class resides in the domain layer. It really doesn’t do much except field calls from the presentation layer and passes them to the persistence layer. If any exceptions are thrown they will be passed thru this layer.

 
InventoryDTO.cs code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;

namespace Inventory.DTO
{
    public class InventoryDTO
    {
        public System.Guid InventoryItemId { get; set; }
        public string ItemManufacturer { get; set; }
        public string ItemName { get; set; }
        public string ItemModel { get; set; }
        public string ItemSerialNumber { get; set; }
        public string ItemNotes { get; set; }
    }
}

This class just serves as a data transfer object. This gives the program the ability to create objects for the database without having to call on the Entity objects residing in the persistence layer.

 
InventoryRepository.cs code

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.Entity;

namespace Inventory.Persistence
{
    public class InventoryRepository
    {
        // Method that takes care of creating a main inventory entity object.
        private static MainInventoryEntities createMainInventoryEntity()
        {
            MainInventoryEntities db = new MainInventoryEntities();
            return db;
        }

        // Method that takes care of creating a DTO inventory object.
        private static DTO.InventoryDTO createInventoryDtoItem(MainInventory inventoryItemDbInfo)
        {
            DTO.InventoryDTO inventoryItemInfo = new DTO.InventoryDTO();
            inventoryItemInfo.InventoryItemId = inventoryItemDbInfo.InventoryItemId;
            inventoryItemInfo.ItemManufacturer = inventoryItemDbInfo.ItemManufacturer;
            inventoryItemInfo.ItemName = inventoryItemDbInfo.ItemName;
            inventoryItemInfo.ItemModel = inventoryItemDbInfo.ItemModel;
            inventoryItemInfo.ItemSerialNumber = inventoryItemDbInfo.ItemSerialNumber;
            inventoryItemInfo.ItemNotes = inventoryItemDbInfo.ItemNotes;
            return inventoryItemInfo;
        }

        // Method that when called upon will get the contents of the database and return
        // an object type from the DTO layer.
        // Mainly used to display the complete contents of the Inventory\MainInventory database table.
        public static List<DTO.InventoryDTO> GetInventory()
        {
            // Try/Catch in the event the Entity cannot be created.
            // In the future add some logging to catch all of the exceptions and information.
            try
            {
                MainInventoryEntities db = createMainInventoryEntity();
                List<MainInventory> databaseInventoryItems = db.MainInventories.ToList();

                List<DTO.InventoryDTO> dtoInventory = new List<DTO.InventoryDTO>();
                foreach (MainInventory inventoryItem in databaseInventoryItems)
                {
                    // Calling a private method to create the DTO object.
                    DTO.InventoryDTO dtoInventoryObjects = createInventoryDtoItem(inventoryItem);

                    // Adding the created DTO object to the list.
                    dtoInventory.Add(dtoInventoryObjects);
                }
                return dtoInventory;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        // Method that when called upon will get the information from the selected 
        // inventory item.
        public static DTO.InventoryDTO ViewInventoryObjectInfo(Guid inventoryItemGuid)
        {
            MainInventoryEntities db = createMainInventoryEntity();
            
            // Try/Catch in the event an item cannot be found in the database.
            // In the future add some logging to catch all of the exceptions and information
            try
            {
                MainInventory inventoryItemDbInfo = db.MainInventories.Find(inventoryItemGuid);
                DTO.InventoryDTO inventoryItemInfo = createInventoryDtoItem(inventoryItemDbInfo);
                return inventoryItemInfo;
            }
            catch (NullReferenceException ex)
            {
                throw new Exception("The selected item cannot be viewed. Refresh the web page.");
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        // Method used when adding a new item to the inventory list.
        public static void AddInventoryObject(DTO.InventoryDTO newInventoryObject)
        {
            MainInventoryEntities db = createMainInventoryEntity();
            DbSet<MainInventory> dbInventory = db.MainInventories;

            // Performing user input validation on the required textboxes.
            if (newInventoryObject.ItemManufacturer.Trim().Length == 0)
                throw new Exception("Item Manufacturer is a required field.");
            if (newInventoryObject.ItemName.Trim().Length == 0)
                throw new Exception("Item Name is a required field");
            if (newInventoryObject.ItemModel.Trim().Length == 0)
                throw new Exception("Item Model is a required field");
            if (newInventoryObject.ItemSerialNumber.Trim().Length == 0)
                throw new Exception("Item Serial Number is a required field");

            // Getting the new inventory items information, building object, and putting the
            // info into the database.
            MainInventory inventoryItem = new MainInventory();
            inventoryItem.InventoryItemId = newInventoryObject.InventoryItemId;
            inventoryItem.ItemManufacturer = newInventoryObject.ItemManufacturer;
            inventoryItem.ItemName = newInventoryObject.ItemName;
            inventoryItem.ItemModel = newInventoryObject.ItemModel;
            inventoryItem.ItemSerialNumber = newInventoryObject.ItemSerialNumber;
            inventoryItem.ItemNotes = newInventoryObject.ItemNotes;

            // Try/Catch in the event something cannot be added to the database.
            // In the future add some logging to catch all of the exceptions and information
            try
            {
                // Adding the item to tha database.
                dbInventory.Add(inventoryItem);
                db.SaveChanges();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        // Method used when removing/deleting and item from the inventory list.
        // Locating the inventory object by it's guid ID to remove it.
        public static void RemoveInventoryObject(Guid inventoryItemGuid)
        {
            // Try/Catch in the event an item cannot be removed from the database.
            // In the future add some logging to catch all of the exceptions and information.
            try
            {
                MainInventoryEntities db = createMainInventoryEntity();
                MainInventory inventoryItemToRemove = db.MainInventories.Find(inventoryItemGuid);

                DbSet<MainInventory> dbInventory = db.MainInventories;
                dbInventory.Remove(inventoryItemToRemove);
                db.SaveChanges();
            }
            catch (ArgumentNullException ex)
            {
                throw new Exception("The selected item cannot be removed. Refresh the web page.");
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        // Method used when editing an item from the inventory list.
        public static DTO.InventoryDTO EditInventoryObject(Guid inventoryItemGuid)
        {
            // Try/Catch in the event an item cannot be edited.
            // In the future add some logging to catch all of the exceptions and information.
            try
            {
                // Creating the Entity object and locating the item to be edited.
                MainInventoryEntities db = createMainInventoryEntity();
                DbSet<MainInventory> dbInventory = db.MainInventories;
                MainInventory inventoryItemDbInfo = db.MainInventories.Find(inventoryItemGuid);

                // After finding the item build the DTO object and return the information.
                // Calling the private method to create the DTO object.
                DTO.InventoryDTO inventoryItemInfo = createInventoryDtoItem(inventoryItemDbInfo);

                // Removing the item from the database to eliminate duplicate entries.
                dbInventory.Remove(inventoryItemDbInfo);
                db.SaveChanges();
                return inventoryItemInfo;
            }
            catch (NullReferenceException ex)
            {
                throw new Exception("The selected item cannot be edited. Refresh the web page.");
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
    }
}

This is the workhorse class. Anytime an object is added, viewed, edited, or removed the method requests will eventually make their way here. Work will be done on the object or parameter then the data passed back up. Not really much to explain that isn’t in the code comments other than the addition of exception handling. Even though I’m passing the regular and custom exception messages back thru the program layers in the future I’ll add some logging capability to the program. Like ELMAH or something along those lines.

Leave a comment