Lesson 13 - Diary with a database in C# .NET

C# .NET OOP Diary with a database in C# .NET

In the previous lesson, Lists in C# .NET, we learned about the List collection in C# .NET. We now know that it allows us to add new items at run-time so we don't have to worry about its size. In today's tutorial, we're going to make a program that involves storing objects in a List.

At first, I was going to have us create a user database, but we've already dealt with users several times in this course. Since we now know how to deal with dates and times, we're going to make a digital diary/journal. We'll store entries into a database and print today's and tomorrow's entries. This database won't be an "actual" database, we will cover databases later on. All it will be is a List in computer memory which will allow the user to add entries, search for them by date and remove them by specifying a date and time.

Let's create a new console application and name it Diary.

The entry

First of all, we'll have to create a class whose instances we will store. Let's name it Entry. Diary entries will be related to a certain date and time. It will also contain text, e.g.: January 12, 2016 - Walk the dog. Our class might look something like this:

class Entry
{
        public DateTime Occurs { get; set; }
        public string Text { get; set; }

        public Entry (DateTime occurs, string text)
        {
                Occurs = occurs;
                Text = text;
        }

        public override string ToString()
        {
                return Occurs + " " + Text;
        }
}

All this class is meant to do is store data, so it has no methods, other than the constructor and ToString(). Notice that in the constructor, there's no need to use this as we were used to doing with attributes because now we are using capitalized properties.

The database

Since our program will be a bit more complex, we'll use multiple objects to keep things nice and neat. We've already made the entry class, now let's create a Database object in which our entries will be stored. There will be a private list as there was on the Lottery application in the previous lesson. The List will be of the Entry data type this time. Our diary will allow us to add, delete and search entries by date. Let's add the Database class to the project. It will be very similar to the Lottery app from last time:

class Database
{
        private List<Entry> entries;

        public Database()
        {
                entries = new List<Entry>();
        }

}

The class will only be used for data manipulation. It will contain an internal collection of entries that will be initialized in the constructor. We could also initialize it directly without a constructor in the attribute declaration:

private List<Entry> entries = new List<Entry>();

Now let's add some methods that will add, delete and search an entry.

Adding an entry is very simple and straightforward:

public void AddEntry(DateTime occurs, string text)
{
        entries.Add(new Entry(occurs, text));
}

As for the second method, we'll allow the user to search for entries by day. The method will return the List of found entries since there could be multiple entries per day in the database. We'll be able to search for entries both by date and time or just by date. This way, we can find entries on a particular day no matter what time they occur. We'll specify our search settings using a byTime bool parameter. If it is false, our search will be by date only. First, we'll create a List and add entries that match the given date. We'll match either full date and time if the bool parameter is true, or just the Date component of it in case the bool parameter is false. Lastly, we'll return the list containing all related entries.

public List<Entry> FindEntries(DateTime date, bool byTime)
{
        List<Entry> found = new List<Entry>();
        foreach (Entry entry in entries)
        {
                if (((byTime) && (entry.Occurs == date)) // filtered by time and date
                ||
                ((!byTime) && (entry.Occurs.Date == date.Date))) // filtered by date only
                        found.Add(entry);
        }
        return found;
}

We'll finish up the class by adding a method that deletes entries based on a given time. We'll perform it using the FindEntries() method, iterating over its result and removing found entries from the list. We'll be deleting entries of a specific date and time so the second parameter of the FindEntries() method will be true:

public void DeleteEntries(DateTime date)
{
        List<Entry> found = FindEntries(date, true);
        foreach (Entry entry in found)
                entries.Remove(entry);
}

Diary

Now let's add the last class to our project, which will represent the diary itself. We'll name it Diary to keep things simple and clear. It will include methods for interacting with the user. Notice how we divide our application and encapsulate its individual parts. The List is encapsulated in the Database class which provides various methods that help handle its contents safely. Let's create a Database instance in our diary. This way, we separate the data logic from the user communication logic, and even that from other program inputs/outputs. The Diary class is supposed to interact with the user and pass the entered data to the database.

Let's add a private Database instance and initialize it in the constructor:

class Diary
{

        private Database database;

        public Diary()
        {
                database = new Database();
        }

}

Next, we'll add a ReadDateTime() utility method which prompts the user to enter a date and time and returns the DateTime instance set to the entered value. The only problem here is validating the user's input (date formats depend on your OS regional settings):

private DateTime ReadDateTime()
{
        Console.WriteLine("Enter date and time as e.g. [01/13/2016 14:00]:");
        DateTime dateTime;
        while (! DateTime.TryParse(Console.ReadLine(), out dateTime))
                Console.WriteLine("Error. Please try again: ");
        return dateTime;
}

Let's add a PrintEntries() method that will find entries for the given day and print them:

public void PrintEntries(DateTime day)
{
        List<Entry> entries = database.FindEntries(day, false);
        foreach (Entry entry in entries)
                Console.WriteLine(entry);
}

We will also need a method for prompting the user to enter a new entry and adding it to the database:

public void AddEntry()
{
        DateTime dateTime = ReadDateTime();
        Console.WriteLine("Enter the entry text:");
        string text = Console.ReadLine();
        database.AddEntry(dateTime, text);
}

Also, we still have to add methods for searching and deleting entries. The searching method will return a List of found entries (we will only search by date, time doesn't matter). We'll prompt the user to enter the date and pass it to the database. Then we'll display the result.

public void SearchEntries()
{
        // Entering the date
        DateTime dateTime = ReadDateTime();
        // Searching for entries
        List<Entry> entries = database.FindEntries(dateTime, false);
        // Printing entries
        if (entries.Count() > 0)
        {
                Console.WriteLine("Entries found: ");
                foreach (Entry entry in entries)
                        Console.WriteLine(entry);
        }
        else
                // Nothing found
                Console.WriteLine("No entries were found.");
}

Deleting entries is trivial:

public void DeleteEntries()
{
        Console.WriteLine("Entries with the same exact date and time will be deleted");
        DateTime dateTime = ReadDateTime();
        database.DeleteEntries(dateTime);
}

Last of all, we'll add a method for printing a home screen, showing the current date and time and entries for today and tomorrow.

public void PrintHomeScreen()
{
        Console.Clear();
        Console.WriteLine("Welcome to your virtual diary!");
        Console.WriteLine("Today is: {0}", DateTime.Now);
        Console.WriteLine();
        // printing the home screen
        Console.WriteLine("Today:\n------");
        PrintEntries(DateTime.Today);
        Console.WriteLine();
        Console.WriteLine("Tomorrow:\n---------");
        PrintEntries(DateTime.Now.AddDays(1));
        Console.WriteLine();
}

Now we can proudly move to Program.cs and create an instance of the diary, knowing that everything will work as expected. We will add the main loop here including a response to the user's choice:

static void Main(string[] args)
{
        // diary instance
        Diary diary = new Diary();
        char choice = '0';
        // main loop
        while (choice != '4')
        {
                diary.PrintHomeScreen();
                Console.WriteLine();
                Console.WriteLine("Choose an action:");
                Console.WriteLine("1 - Add an entry");
                Console.WriteLine("2 - Search for entries");
                Console.WriteLine("3 - Delete entries");
                Console.WriteLine("4 - End");
                choice = Console.ReadKey().KeyChar;
                Console.WriteLine();
                // reaction to the choice
                switch (choice)
                {
                        case '1':
                                diary.AddEntry();
                                break;
                        case '2':
                                diary.SearchEntries();
                                break;
                        case '3':
                                diary.DeleteEntries();
                                break;
                        case '4':
                                Console.WriteLine("Press any key to quit the program...");
                                break;
                        default:
                                Console.WriteLine("Error. Press any key to choose another action.");
                                break;
                }
                Console.ReadKey();
        }
}

The code above isn't complicated at this point since we've already made similar applications in previous lessons. I gave the finalized app to my girlfriend as a present, here's what it looks like :)

Console application
Welcome to diary!
Today is: 5/12/2016 11:34:55

Today:
-----
5/12/2016 10:00:00 Shopping - Pankrac Arcade
5/12/2016 7:30:00 PM - Pet my Yorkshire Terrier Fred

Tomorrow:
--------
5/13/2016 2:00:00 PM Go jogging


Choose an action:
1 - Add an entry
2 - Search for entries
3 - Delete entries
4 - End
2
Enter date and time as e.g. [1/13/2016 10:00]
5/15/2016
Found entries:
5/15/2016 9:30:00 Economy exam

Now you know how to use List and rest assured that it will suffice as data storage container for a quite a long time. In conclusion, I'd like to congratulate you in now being able to create in-memory databases. Use it for anything you want! You could, for example, use it on the User class from the Properties lesson or pretty much anything else. You can store articles, tasks, elephants, anything you want to manage in the database. What's up next, you may ask? Next time, in Interfaces in C# .NET, we'll be talking about interfaces. There's still a lot to learn about OOP :)


 

Download

Downloaded 25x (46.22 kB)
Application includes source codes in language C#

 

 

Article has been written for you by David Capka
Avatar
Do you like this article?
1 votes
The author is a programmer, who likes web technologies and being the lead/chief article writer at ICT.social. He shares his knowledge with the community and is always looking to improve. He believes that anyone can do what they set their mind to.
Unicorn College The author learned IT at the Unicorn College - a prestigious college providing education on IT and economics.
Activities (14)

 

 

Comments

Avatar
Lennart Völler:29. November 8:18

Another very nice and deep article. Let my ask you some questions for clarification.

public List<Entry> FindEntries(DateTime date, bool byTime)
{
        List<Entry> found = new List<Entry>();
        foreach (Entry entry in entries)
        {
                if (((byTime) && (entry.Occurs == date)) // filtered by time and date
                ||
                ((!byTime) && (entry.Occurs.Date == date.Date))) // filtered by date only
                        found.Add(entry);
        }
        return found;
}

First
Why do you create a new list before entering the foreach loop? Shouldn't it just be an Entry object?

Second
Speaking of which: Why do we need to create a new List / new object at all? My understanding is that we are searching an existing List of Entry objects for one that fits our search criteria, so why do we need an initialization?

Third
Why do we need to put (byTime) in brackets by it's own? I tried it like this:

if ((byTime && (entry.Occurs == date)) //filtered by time and date
                    || ((!byTime && (entry.Occurs.Date == date.Date)))) // filteresby date only
                            found.Add(entry);
 
Reply 29. November 8:18
Avatar
David Capka
ICT.social team
Avatar
Replies to Lennart Völler
David Capka:29. November 15:14
  1. If we created a new list in the foreach loop, in the end there'd be multiple lists cointaining only 1 item each.
  2. The method doesn't search for a single entry. There can be multiple entries happening on the same day. Therefore we need to return multiple items using a List of these items. First, we create an empty list. Then we fill it with suitable items. And finally we return this list.
  3. The parentheses don't have to be there, I probably added them just because of the negation to make it more clear and then copied them to the line above as well.
Edited 29. November 18:18
Reply  +1 29. November 15:14
You can walk through a storm and feel the wind but you know you are not the wind.
To maintain the quality of discussion, we only allow registered members to comment. Sign in. If you're new, Sign up, it's free.

2 messages from 2 displayed.