2

I am trying to retrieve all the columns from a table using ExecuteReader in C# application.Db is postgre.To test i created a console application following a tutorial which did showed how to query using a Function but not with passing parameters.The console application function for test

    static void Main(string[] args)
    {
        // Connect to a PostgreSQL database
        NpgsqlConnection conn = new NpgsqlConnection("Server=localhost;User Id=postgres; " +
            "Password=pes;Database=pmc;");
        conn.Open();

        // Define a query
        NpgsqlCommand command = new NpgsqlCommand("SELECT * from audit.exception_gl_accounts()", conn);

        // Execute the query and obtain a result set
        NpgsqlDataReader dr = command.ExecuteReader();

        // Output rows
        while (dr.Read())
            Console.Write("{0}\t{1} \n", dr[0], dr[1]);

        conn.Close();
    }
}

Here in NpgsqlCommand i sent the query without parameter to the function audit.exception_gl_accounts and it worked well.Now How do i pass a parameter to a function like this

"SELECT * FROM sms.get_accounts_info(@AccountNumber);

I am trying to retrieve all 5 columns using this function and get those objects

    public static string GetAccountInfo(string accountNumber)
    {
        NpgsqlConnection conn = new NpgsqlConnection("Server=localhost;User 
                            Id=postgres; " + "Password=pes;Database=pmc;");
        conn.Open();
        NpgsqlCommand command = new NpgsqlCommand("SELECT * FROM 
                           sms.get_accounts_info(@AccountNumber); ", conn);
        command.Parameters.AddWithValue("@AccountNumber", accountNumber);
        NpgsqlDataReader dr = command.ExecuteReader();
        while (dr.Read())
            Console.Write("{0}\t{1} \n", dr[0], dr[1]);
            return dr.ToString();
    }

Using the second sample code gives this error :

{ "accountNumber": "Npgsql.ForwardsOnlyDataReader", "balance": null, "interestRate": 0, "accountName": null, "accountType": null }

Any Help Appreciated.

DETAILS UPDATED

The Controller

[HttpPost]
[ActionName("info")]
public IHttpActionResult GetAccountInfo([FromBody]AccountInfo 
accountinfo)
 {
accountinfo.accountNumber = BusinessLayer.Api.AccountHolderApi.GetAccountInfo
          (accountinfo.accountNumber);
            return Ok(accountinfo);
 }

Account Info Class

public class AccountInfo
    {
      public string accountNumber { get; set; }
      public string balance { get; set; }
      public int interestRate { get; set; }
      public string accountName { get; set; }
      public string accountType { get; set; }
    }

URI

http://localhost:8080/v1/accounts/info

GetAccountsInfo

CREATE OR REPLACE FUNCTION sms.get_accounts_info(IN account_number_ character varying)
  RETURNS TABLE(account_number character varying, account_name text, product character varying, interest_rate numeric, balance money) AS
$BODY$
BEGIN
    RETURN QUERY(
        SELECT a.account_number,
        c.customer_name,
            p.deposit_product_name, 
            a.interest_rate::numeric, deposit.get_balance(account_number_)
        FROM deposit.account_holders a 
        JOIN core.customers_view  c ON a.customer_id = c.customer_id
        JOIN core.deposit_products p ON a.deposit_product_id = p.deposit_product_id
        WHERE a.account_number = $1
    );
END
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100
  ROWS 1000;
ALTER FUNCTION sms.get_accounts_info(character varying)
  OWNER TO postgres;
JGH
  • 15,928
  • 4
  • 31
  • 48
OLDMONK
  • 382
  • 2
  • 7
  • 25
  • 1
    that does not look like an error. could this be the (valid) result of `dr.ToString()`? Is there any runtime error (exception)? is anything written to console? `Npgsql.ForwardsOnlyDataReader` would by the `dr` recordset type - not sure how that ends up as a value. – Cee McSharpface Jun 09 '17 at 17:28
  • @dlatikay hello sir..there is nothing written to console..was looking ways to get around and found that in a tutorial..this is a webapi project and passing accountNumber as parameter to the body.Help sir.i am loosing my hair because of this.How to pass parameter to a postgre function using npgsql command?thank you – OLDMONK Jun 09 '17 at 17:38
  • I think you're doing it fine with the parameter already. To me it would look more like an issue with the presentation of the results. The output you included in your post, is that the string that `GetAccountInfo` returns, or something else? – Cee McSharpface Jun 09 '17 at 17:40
  • @dlatikay i have updated the details sir.thank you for your time. – OLDMONK Jun 09 '17 at 17:50
  • What is the plsql function GetAccountInfo returning? (please edit your post with the code). Are you sure it is returning two columns? Where is the "error" occurring? Try putting your code in a try/catch and display the error message. Note that you are looping through all results, but you return (=exit) at the 1st row. As @dlatikay writes, it looks a lot like dr.tostring – JGH Jun 09 '17 at 17:51
  • @JGH it is returning 5 columns sir..actually the code i put above for GetAccountInfo, i am testing looking through tutorials since hours to get execute.reader work..getting insane.I am not an expert.still learning.please pardon if too lame. i tried dr.ToString() because the message said CANNOT IMPLICITLY CONVERT TYPE NpgSql.NpgSqlDataReader to 'string' when i tried to return just return dr; – OLDMONK Jun 09 '17 at 18:03
  • the reader is fine, forget about it (unless it throws an exception that you can catch in a [try..catch](https://learn.microsoft.com/en-us/dotnet/standard/exceptions/how-to-use-the-try-catch-block-to-catch-exceptions) ). Your function returns 5 columns? Then you should read the 5 (dr[0] to dr[4]). Your function is returning 1 string, what should it be? (you may want to return nothing and just display the result for now). At last, make sure to read about handling [NULL values](https://stackoverflow.com/questions/1772025/sql-data-reader-handling-null-column-values) – JGH Jun 09 '17 at 18:24

1 Answers1

5

Without entity framework, you need to write the code that reads the values from the datareader into an instance of your AccountInfo class:

public static AccountInfo GetAccountInfo(string accountNumber)
{
    AccountInfo result = null;
    using(var conn = new NpgsqlConnection("..."))
    {
        conn.Open();
        using(var command = new NpgsqlCommand("SELECT * FROM sms.get_accounts_info(@AccountNumber); ", conn))
        {
            command.Parameters.AddWithValue("@AccountNumber", accountNumber);
            using(var dr = command.ExecuteReader())
            {
                if(dr.HasRows && dr.Read())
                {
                    result = new AccountInfo { 
                        accountNumber = dr["accountNumber"].ToString(),
                        balance = dr["balance"].ToString(),
                        interestRate = Convert.ToInt32(dr["interestRate"]),
                        accountName = dr["accountName"].ToString()
                    };
                }
            }
        }
    }
    return result;
}

Note that the return type of the function has been changed to AccountInfo, previously string. Also, it is limited to reading just one record, If one call to sms.get_accounts_info could return more than one record, it is a different story. I just assumed that account_number is a primary key in the account_holders table.

Some details need your attention, for example balance is money in database, but string in the class. Also I did not know if and how product (database) and accountType (class) would correspond, so I omitted it.

Database connections, commands, and datareaders are IDisposable and should be wrapped in using blocks.

Cee McSharpface
  • 8,493
  • 3
  • 36
  • 77
  • Thank you so much for your time sir...I know i lack understanding basic language constructs related to objects and populating objects and i am still learing.One last question perhaps- My controllers action [HttpPost] [ActionName("info")] public IHttpActionResult GetAccountInfo([FromBody]AccountInfo accountinfo) { accountinfo.AccountNumber = BusinessLayer.Api.AccountHolderApi.GetAccountInfo(accountinfo.AccountNumber); return Ok(accountinfo); } it says cannot convert type AccountInfo to "string". – OLDMONK Jun 09 '17 at 18:53
  • 1
    I think it should be `accountinfo = BusinessLayer.Api.AccountHolderApi.GetAccountInfo(....)` instead. So it will return the complete object with five fields, and not just the account number (which makes sense as input, but probably not as the only output). – Cee McSharpface Jun 09 '17 at 19:01
  • Made it worked..had a small issue sir..you were godsend.Thanks a lot.:) – OLDMONK Jun 10 '17 at 09:41
  • @dlatikay how do we map with EF? – balron Apr 24 '19 at 07:35
  • this is a separate question, does not belong here IMO – Cee McSharpface Apr 24 '19 at 08:21