Search code examples
c#listfilekeychain

C# Creating a Class List<>/Dictionary<>, writing to and reading from .txt file as a database without overwriting data


This is one of the most common questions you guys get here so forgive me but I cannot get past this no matter how hard I search.

I am building a keychain application for a project in Windows Forms Application, and for the purpose of understanding this concept as easily as possible I went with a List<> rather than Dictionary//iList

This is the Class I have been using:

public class Account
{
    public static List<Account> myAccountList = new List<Account>();
    public string Domain; //this is supposed to be google/skype/facebook
    public string Email;
    public string Username;
    public string Password;

    public Account (string domain, string email, string username, string password)
    {
        Domain = domain;
        Email = email;
        Username = username;
        Password = password;
        myAccountList.Add(new Account(domain, email, username, password)); //constructor calls the new list instance
    }
    private static void SaveToFile()
    {
        System.IO.File.WriteAllLines(@accountdb.txt, myAccountList);
    }
    private static void ReadFromFile() // this is meant to be used as authentication in my main form, it isn't necessary right now..
    {
        System.IO.File.ReadAllLines(@accountdb.txt);
    }
}

I have a couple issues with this:

  1. I cannot create a functional Save method to write to file, I have tried several methods in System.IO
  2. When I changed the list to a one-dimensional array, it would constantly overwrite it, I want to simulate MySQL to familiarize myself for later on.

The Button Click event that calls the Constructor:

private void buttonSave_Click(object sender, EventArgs e)
    {
        string domain, email, username, password;
        domain = comboboxDomain.Text;
        email = textboxEmail.Text;
        username = textboxUsername.Text;
        password = textboxPassword.Text;
        //Checking for correct EMAIL

        if (!textboxEmail.Text.Contains("@") && (!textboxEmail.Text.Contains(".")))
        {
            MessageBox.Show("Invalid Email Format");
        }
        else
        {
            Account account = new Account(domain, email, username, password);
        }
    }

Solution

  • WriteAllLines expects an string[] as the second parameter. So you need to create a way to represent a single Account object as a string. The easiest way is to override the ToString method. Then use the Linq Select method to select them.

    Also, it would be better if the Account class did not also contain the list of accounts. That should really be a separate class. Try something like this:

    void Main()
    {
        var accountList = new AccountList();
    
        //Save a few accounts to the file
        Account a1 = new Account("Google", "GoogleEmail", "GoogleUser", "GooglePass");
        accountList.Add(a1);
        Account a2 = new Account("Netflix", "NetflixEmail", "NetflixUser", "NetflixPass");
        accountList.Add(a2);
    
        AccountList.SaveToFile(@"g:\test\accounts.txt", accountList);
    
    
        //Load the accounts from the file
        AccountList aList2 = AccountList.ReadFromFile(@"g:\test\accounts.txt");
        aList2.Dump();
    }
    
    public class AccountList
    {
        private List<Account> accounts;
        public IEnumerable<Account> Accounts { get { return accounts;} }
    
        public AccountList()
        {
            this.accounts = new List<Account>();
        }
    
        public void Add(Account a)
        {
            accounts.Add(a);
        }
    
        public static void SaveToFile(string filename, AccountList accounts)
        {
            //Selects all the `Account` instances and creates a string[]
            System.IO.File.WriteAllLines(filename, accounts.Accounts.Select(a => $"{a}").ToArray());
        }
    
        public static AccountList ReadFromFile(string filename) // this is meant to be used as authentication in my main form, it isn't necessary right now..
        {
            var result = new AccountList();
    
            //Read each line from the file and create an Account instance.
            foreach (var line in System.IO.File.ReadAllLines(filename))
            {
                if (!string.IsNullOrWhiteSpace(line))
                {
                    var parts = line.Split(',');
                    if (parts.Length == 4)
                    {
                        Account a = new Account(parts[0], parts[1], parts[2], parts[3]);
                        result.Add(a);
                    }
                }
            }
    
            return result;
        }
    }
    
    public class Account
    {
        public string Domain; //this is supposed to be google/skype/facebook
        public string Email;
        public string Username;
        public string Password;
    
        public Account(string domain, string email, string username, string password)
        {
            Domain = domain;
            Email = email;
            Username = username;
            Password = password;
        }
    
        public override string ToString()
        {
            return $"{Domain},{Email},{Username},{Password}";
        }
    }