0

I am doing a task called , Expense Manager in linqpad using c#,asp.net core,npgsql, and PostgreSQL as database The objective of the task is to perform different operations on the database.
For Example: GetTransactions()-- to get all transactions performed.

like this there are 5 methods:
GetTransactionInfo(5); -- to get complete transactions information based on transaction id
GetIncome(startDate, endDate); -- total amount earned as income between dates GetExpense(startDate,endDate);
GetFilteredTransactionsList(startDate,endDate);

The database consists of 2 tables namely: Transaction, Account shown below in classes below with same names and columns.

These methods are all working perfectly as shown in the code here:

    void Main()
{
    DateTime startDate = new DateTime(2022, 01, 01);
    DateTime endDate = new DateTime(2022, 01, 30);
    TransactionAccess.MakeTransaction(new Transaction
    {
        Amount = 500,
        Date = startDate,
        Note = "Travel ticket",
    }, new Account
    {
        Account_Name = "shopping",
        Type = "expense"
    });

    TransactionAccess.CreateAccount(new Account
    {
        Account_Name = "shopping",
        Type = "expense"
    });

        TransactionAccess.GetTransactions();
        TransactionAccess.GetTransactionInfo(5);
        TransactionAccess.GetIncome(startDate, endDate);
        TransactionAccess.GetExpense(startDate,endDate);
        TransactionAccess.GetFilteredTransactionsList(startDate,endDate);
}


//------------------------------------------------------
class TransactionAccess
{
    private const string connString = "Host=localhost;Username=postgres;Password=1234;Database=ExpenseManagerDB";
    
    public static void GetTransactions()
    {
        using (var connection = new NpgsqlConnection(connString))
        {
            var transactions = connection.Query<TransactionView>(@"SELECT t.transaction_id,t.account_id,a.account_name, a.type,t.note, t.amount, t.date
                                                               FROM account AS a
                                                               INNER JOIN transaction AS t ON a.account_id = t.account_id");
            transactions.Dump();
        }
    }

    public static void GetTransactionInfo(int id)
    {
        using (var connection = new NpgsqlConnection(connString))
        {
            var transactionInfo = connection.Query<TransactionView>(@"SELECT a.account_name, a.type, DATE(t.date), t.amount, t.note, t.transaction_id 
                                                                  FROM transaction AS t 
                                                                  INNER JOIN account AS a ON t.account_id = a.account_id 
                                                                  WHERE t.transaction_id = @id", new { id });
            transactionInfo.Dump();
        }
    }

    public static void MakeTransaction(Transaction transaction, Account account)
    {
        using (var connection = new NpgsqlConnection(connString))
        {
            connection.Execute(@"INSERT INTO transaction(account_id,amount,date,note)
                                         SELECT a.account_id,@amount, @date, @note
                                         FROM account AS a
                                         WHERE a.account_name=@account_name", new { transaction.Amount, transaction.Date, transaction.Note, account.Account_Name });
        }
    }

    public static void CreateAccount(Account account)
    {
        using (var connection = new NpgsqlConnection(connString))
        {
            connection.Execute(@"INSERT INTO account(account_name, type)
                             VALUES(@account_name, @type)", new { account.Account_Name, account.Type });
        }
    }

    public static void GetIncome(DateTime startDate, DateTime endDate)
    {
        using (var connection = new NpgsqlConnection(connString))
        {
            var Income = connection.Query<TransactionView>(@"SELECT SUM(t.amount) as Income 
                                                        FROM transaction AS t 
                                                        INNER JOIN account AS a ON t.account_id = a.account_id 
                                                        WHERE t.date BETWEEN @startDate AND @endDate AND a.type = 'income'", new { startDate, endDate });
            Income.Dump();
        }
    }

    public static void GetExpense(DateTime startDate, DateTime endDate)
    {
        using (var connection = new NpgsqlConnection(connString))
        {
            var Expense = connection.Query<TransactionView>(@"SELECT SUM(t.amount) as Expense 
                                                        FROM transaction AS t 
                                                        INNER JOIN account AS a ON t.account_id = a.account_id 
                                                        WHERE t.date BETWEEN @startDate AND @endDate AND a.type = 'expense'", new { startDate, endDate });
            Expense.Dump();
        }
    }

    public static void GetFilteredTransactionsList(DateTime startDate, DateTime endDate)
    {
        using (var connection = new NpgsqlConnection(connString))
        {
            var filteredTransactions = connection.Query<TransactionView>(@"SELECT a.account_name, a.account_id, a.type, DATE(t.date), t.transaction_id, t.amount, t.note 
                                                                       FROM transaction AS t 
                                                                       INNER JOIN account AS a ON t.account_id = a.account_id 
                                                                       WHERE t.date BETWEEN @startDate AND @endDate
                                                                       ORDER BY t.date", new { startDate, endDate });
            filteredTransactions.Dump();
        }
    }

}


//-----------------------------------------------------------------------------------
public class Transaction
{
    public int Transaction_Id { get; set; }
    public int Account_Id { get; set; }
    public decimal Amount { get; set; }
    public string Note { get; set; }
    public DateTime Date { get; set; }
}

public class Account
{
    public int Account_Id { get; set; }
    public string Account_Name { get; set; }
    public string Type { get; set; }

}

public class TransactionView
{
    public int Transaction_Id { get; set; }
    public int Account_Id { get; set; }
    public decimal Amount { get; set; }
    public string Note { get; set; }
    public DateTime Date { get; set; }
    public string Account_Name { get; set; }
    public string Type { get; set; }
    public int Income { get; set; }
    public int Expense { get; set; }
}

so based on the above you might have understood I am calling every method from the Main() method

but I have two questions here:

  1. I was asked to separate the access layer from business object. I Know that from access layer(here it is class Transaction access).I don't understand what business object is here and how to separate it.

  2. when I am calling each and every method I am making new connections i.e., var connection = new NpgsqlConnection(connString), but instead I want to use only a single connection for every query, I am excuting. How can I do that.

Please, I am so tired of thinking these two questions. can someone please help me?

Isaac
  • 3,240
  • 2
  • 24
  • 31
  • Linqpad is amazing but writing an app in it will be horrible. I suggest using VS Community (free) edition. Regarding connections see https://stackoverflow.com/a/44272654/43846 – stuartd Feb 08 '22 at 00:50
  • **code to interface not implementation**. 1. think how to accomplish flexibility if you want to use another persistent storage, such as sqlite, or even others instead of database. 2. making new connection in each call is best practice, no need change. – Lei Yang Feb 08 '22 at 01:01
  • IMHO `TransactionAccess` should have instance methods, not static methods. `connection` should be an instance variable populated by the constructor. The caller (or DI container) should be responsible for the connection lifetime and any transaction scope. Those methods that `.Query` should return something. Though `Main` can `.Dump()` for testing purposes. – Jeremy Lakeman Feb 08 '22 at 01:49
  • You should also return / populate any database generated key values. So the caller can insert multiple objects in one transaction without needing to call subsequent methods. – Jeremy Lakeman Feb 08 '22 at 01:51
  • About using "only a single connection for every query", you should probably not do that - read up on [Npgsql's connection pooling](https://www.npgsql.org/doc/basic-usage.html#pooling). Note this same question has recently been asked in https://stackoverflow.com/questions/71013862/how-to-use-only-single-connection-to-execute-multiple-queries-in-npgsql, https://stackoverflow.com/questions/71014898/how-to-use-only-one-connection-to-execute-multiple-commands-and-queries, and many other times. – Shay Rojansky Feb 08 '22 at 08:53

0 Answers0