Console Task App
This tutorial provides a step-by-step walkthrough on how to build a task list app with a standard C# console app start project.
To set up your project for Ditto:
Open Visual Studio for Windows or for Mac.
Create a New Project and select Console Application.
![Document image Document image](https://images.archbee.com/qoRkNxW5fJ81r_NqVpc8C/CkWDholZXeE8Hq7GcB4z3_image.png?format=webp)
When prompted to select a target framework, select .NET 6.0.
Select a name for your project; for example, "Tasks."
We've deployed Ditto for C# on the standard NuGet package repository. We will need to add Ditto to this project.
Right click the project's Dependencies folder, and click _Manage NuGet Dependencies.
Search for "Ditto" in the search bar and add the package called "Ditto" by "Ditto". Ensure not to mistake it for another package.
![Document image Document image](https://images.archbee.com/qoRkNxW5fJ81r_NqVpc8C/1AlcCMJgIcLECYE3tU1lm_image.png?format=webp)
If you prefer a different way of installation, feel free to take a look at the alternative ways to install or reference the NuGet page here.
In your Program.cs file, add using DittoSDK and using System.Collections.Generic to the top of the file like so:
Now we'll need to hold a reference to our Ditto instance as a static variable and also add a static DittoLiveQuery and static DittoSubscription variable.
These variables must be static because the console program's Main function is also static.
Instantiate the ditto static variable by constructing it with a development identity with an appID: "live.ditto.tasks". If you want to sync with other tutorial app types like iOS or Android, you'll need to match the appID to enable sync.
Ditto documents have a flexible structure. Oftentimes, in strongly-typed languages like C#, we will create a data structure give more definition to the app. Create a new C# file named "Task.cs" in your project.
In the new file, you'll need to reference System, System.Collections.Generic and DittoSDK.
Add the matching variables string _id, string body, and bool isCompleted to the struct. We will use this to match the document values to to the struct.
Add an constructor to Task that takes in a DittoDocument. In the constructor, parse out the document's keys with Ditto's type safe value accessors. This will safely map all the document's values to the struct's variables that we created in step 2. In addition we will add a couple of other constructor overloads for easier creation of data.
Override the ToString() method of the struct. We will later use this to easily print out a more readable string that we can use in Console.WriteLine back in the main Program.cs.
Add a function to the struct called ToDictionary which will serialize the values into a Dictionary<string, object>.
This will assist us later when we need to insert a new document into Ditto.
Unlike many UI applications, Console or Command Line applications have limitations to user interactions. For example, console applications typically read text commands during a while loop as a standard design pattern. This section will outline the command line design and set up the loop to allow the user to give continual commands.
Our Tasks Console app will have five commands that map to Ditto and general console commands. We will need:
--insert to allow the user to .insert a new document into the ditto.collection('tasks") collection
--toggle to allow the user to .update a new document's bool isCompleted property by its _id.
--delete to allow the user to .remove a new document by its _id.
--list will print every current Task that we have in the collection. In addition, we will always call this method before every item.
--exit will quit the console app.
As a best practice, long-running console applications should continuously print the primary set of commands as long as they are succinct. We'll create a utility method called ListCommands() which, will Console.WriteLine each of the commands.
As we insert, update, and delete our tasks, we will update the Tasks collection. To sync changes coming from other devices, we will need create a Live Query by calling .Observe.
Remember Ditto will only sync with devices by calling .Observe on queries. The .Observe method will return a DittoLiveQuery object. As long as the DittoLiveQuery object stays in scope and is not garbage collected, the Live Query will fire for any changes to the tasks collection.
Remember, the .Observe callback will fire for both local changes and remote changes.
In the context of our console application, we need to:
Store a List<Task> as a static variable so that we can print it upon command.
.Observe all the document in the tasks collection. Take care to store the DittoLiveQuery as a static variable as well.
In the .Observe callback, convert all the List<DittoDocument> docs into List<Task> and assign them to both variables detailed in step 1 and 2.
We have all the basic building blocks for syncing tasks to a locally stored List<Task> variable. In the following section, we will go over how to map the user commands to actual Ditto live query, insert, update and delete methods.
To determine whether or not the while loop should run, we need an addition static bool isAskedToExit = false. If the user turns this to true via the --exit command, the while loop will stop and the application will exit.
In each iteration of the while loop, we will need read the command from the user. In C#, we can use the Console.ReadLine API, which will prompt the user for a string entry. We can store this into string command.
We can add a switch statement which will parse the correct command and react to the command.
If the user types in --insert, we will parse out the string without the --insert command. We assume this string is the body for a new document. So we will call the .upsert command with ditto via:
Check for a switch case for --toggle. We will parse out the string without --toggle and assume the user's input is a Task document's _id. We can then find the document by its _id and call .update.
Check for a switch case for --delete. We will parse out the string without --delete and assume the user's input is a Task document's _id. We can then find the document by its _id and call .update.
Finally we will add a command to look for --list, which will print out all the tasks that we've synced.
Our application is complete! Our Program.cs file should look like the following. Now you can run the example in your terminal, command line or right within the run command in Visual Studio.