Nullreference, learning OOP [duplicate] - c#

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 7 years ago.
I'm making an assignment for school and it involves creating an application with certain restrictions on how to create it. Such as having a set amount of methods. I figured i'd say that before posting code that looks unoptimized, and can infact be done with less methods.
Anyway, currently i have a Northwind class, that handles everything that comes from the Database and in this case, creates a list filled with the Shipper object, then i use another Northwind Method to convert that to a queue. (this is what i mean with unoptimized, i could just use a queue from the get-go but i'm not allowed to.) However, when i use it it comes up with a very common error. But i can't figure out why...
class Northwind
{
public Queue<Shipper> Queue { get; set; }
public List<Shipper> GetList()
{
var con = new SqlConnection("Data Source=DESKTOP-G5VBFCN;Initial Catalog=Northwind;Integrated Security=True");
var cmd = new SqlCommand("SELECT CompanyName, Phone FROM Shippers",con);
con.Open();
var reader = cmd.ExecuteReader();
var ShipList = new List<Shipper>();
while (reader.Read())
{
var s = new Shipper
{
CompanyName = reader["CompanyName"].ToString(),
Phone = reader["Phone"].ToString()
};
ShipList.Add(s);
}
con.Close();
return ShipList;
}
public Queue<Shipper> GetQueue(List<Shipper> List)
{
Queue<Shipper> ShipperQueue = new Queue<Shipper>(List);
return ShipperQueue;
}
}
}
And i use the class in my Form1.cs
public partial class Form1 : Form
{
Northwind db;
Shipper Shipper;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
db.Queue = db.GetQueue(db.GetList());
}
Do please notice that the Northwind db; is actually underlined in green. I just started learning OOP. Thanks for taking the time to look at my code.

Northwind db = new Northwind();
Or make the class static and you don't need to initialize on your own.
static class Northwind
{
public static Queue<Shipper> Queue { get; set; }
public static List<Shipper> GetList()
{
var con = new SqlConnection("Data Source=DESKTOP-G5VBFCN;Initial Catalog=Northwind;Integrated Security=True");
var cmd = new SqlCommand("SELECT CompanyName, Phone FROM Shippers",con);
con.Open();
var reader = cmd.ExecuteReader();
var ShipList = new List<Shipper>();
while (reader.Read())
{
var s = new Shipper
{
CompanyName = reader["CompanyName"].ToString(),
Phone = reader["Phone"].ToString()
};
ShipList.Add(s);
}
con.Close();
return ShipList;
}
public static Queue<Shipper> GetQueue(List<Shipper> List)
{
Queue<Shipper> ShipperQueue = new Queue<Shipper>(List);
return ShipperQueue;
}
}
and invoke like
private void Form1_Load(object sender, EventArgs e)
{
Northwind.Queue = Northwind.GetQueue(Northwind.GetList());
}

Related

How to call object from one class to another in C#

I have a problem that I just can't solve on my own. I am new to programming and I would appreciate if you could help me with this issue:
I have a class I would like to inherit from:
namespace rsDeployer.Common.SQLServerCommunication
{
public class RSDatabaseConnectionCreator: LoggerBase
{
public RSProfile profile;
public RSDatabaseConnectionCreator(RSProfile profile)
{
this.profile = profile;
}
public SqlConnection CreateConnection(RSDatabaseNames DatabaseName, bool UseWindowsAuthentication, bool testConnection = false)
{
var connectionString = BuildRSDatabaseConnectionString(DatabaseName, UseWindowsAuthentication);
if (testConnection)
{
return IsConnectionAvailable(connectionString) ? new SqlConnection(connectionString) : null;
}
return new SqlConnection(connectionString);
}
}
}
and I would like to call CreateConnection() in another class to inject to methods to allow me to open connection and then execute scripts.
Edit 1 - class I would like to have it injected to.
public void QueryExecution(string SQLQuery)
{
//here's where I would like to inject it
SqlCommand command = new SqlCommand(SQLQuery, conn);
var file = new StreamWriter(#"D:\Project\rsDeployer\ImportedQuery.txt");
file.WriteLine(command);
file.Close();
}
If this question is way to silly to deserve answer would you just point in the direction where I should read about it?
I hope this question is well asked and clear.
Thanks in advance.
Like this,
public void QueryExecution(string SQLQuery)
{
RSProfile profile = new RSProfile();
RSDatabaseConnectionCreator instance = new RSDatabaseConnectionCreator(profile);
SqlConnection conn = instance.CreateConnection(...);
SqlCommand command = new SqlCommand(SQLQuery, conn);
var file = new StreamWriter(#"D:\Project\rsDeployer\ImportedQuery.txt");
file.WriteLine(command);
file.Close();
conn.Close();
}
You also told that you want to inherit from this class, here is another approach,
public class RSDatabaseConnectionCreator : LoggerBase
{
public virtual object CreateConnection() // by virtual you can override it.
{
return new object();
}
}
public class AnotherClass : RSDatabaseConnectionCreator {
public AnotherClass() {
CreateConnection(); // by inheriting RSDatabaseConnectionCreator , you can reach public functions.
}
public override object CreateConnection() // or you can override it
{
// here might be some user Login check
return base.CreateConnection(); // then you open connection
}
}
Hope helps,
Hope this is what you are requesting for
public class ClassX
{
private RSProfile _rsprofile;
RSDatabaseConnectionCreator _dbConnectionCreator;
private SqlConnection _sqlConnection;
public ClassX()
{
_rsProfile = xxx; // Get the RSProfile object
_dbConnectionCreator = new RSDatabaseConnectionCreator (_rsProfile);
RSDatabaseNames databaseName = yyy; // get the RSDatabaseNames
var useWindowsAuthentication = true;
var testConnection = false;
_sqlConnection = _dbConnectionCreator.CreateConnection(databaseName,useWindowsAuthentication ,testConnection );
}
}
This is how you do it. Apologies for a major blunder in the earlier answer. This one has the connection surrounded by a using.
namespace rsDeployer.Common.SQLServerCommunication
{
public class ConsumerClass
{
public void QueryExecution(string SQLQuery)
{
var profile = new RsProfile();
var rsConnectionCreator = new RSDatabaseConnectionCreator(profile);
using(var sqlConnection = rsConnectionCreator.CreateConnection(...Parameters here...)){
SqlCommand command = new SqlCommand(SQLQuery, sqlConnection );
}
var file = new StreamWriter(#"D:\Project\rsDeployer\ImportedQuery.txt");
file.WriteLine(command);
file.Close();
}
}
}
You can inject the connection creator into the consumer class through the constructor.
public class Consumer
{
private RSDatabaseConnectionCreator _connectionCreator;
// Constructor injection
public Consumer (RSDatabaseConnectionCreator connectionCreator)
{
_connectionCreator = connectionCreator;
}
public void QueryExecution(string SQLQuery)
{
using (var conn = _connectionCreator.CreateConnection(dbName, true, true)) {
if (conn != null) {
...
}
}
}
}
Note: The using statement automatically closes the connection.
Usage
var connectionCreator = new RSDatabaseConnectionCreator(profile);
var consumer = new Consumer(connectionCreator);
consumer.QueryExecution(sqlQuery);
If you want to inject the connection creator at each call of QueryExecution, you can inject it directly into the method as an additional parameter, instead.
public void QueryExecution(string SQLQuery, RSDatabaseConnectionCreator connectionCreator)
{
using (var conn = connectionCreator.CreateConnection(dbName, true, true)) {
if (conn != null) {
...
}
}
}
Usage
var connectionCreator = new RSDatabaseConnectionCreator(profile);
var consumer = new Consumer();
consumer.QueryExecution(sqlQuery, connectionCreator);

Importing array values in constructor

In my main form I have the following code
[ImportingConstructor]
public MainForm([ImportMany] IEnumerable<AudioPlugin> content)
{
InitializeComponent();
listBox.DisplayMember = "Name";
foreach (var listing in content)
{
listBox.Items.Add(listing);
}
}
In my AudioPlugin class I have the following code
[Export(typeof(INAudioPlugin))]
public class RecordingPanelPlugin : AudioPlugin
{
private string _customer { get; set; }
public void ConnectionString()
{
using (var conn = new SqlCeConnection("Data Source=MyDatabase.sdf;Password=pass;Persist Security Info=True"))
{
conn.Open();
var comm = new SqlCeCommand("SELECT * FROM main", conn);
SqlCeDataReader reader = comm.ExecuteReader();
while (reader.Read())
{
_customer = (string)(reader["CustomerName"]);
Console.WriteLine(_customer);
}
}
}
public string Name
{
get
{
ConnectionString();
return _customer;
}
}
public Control CreatePanel()
{
return new RecordingPanel();
}
}
With the code as it is, I'm only getting the last value returned from the SQL query. What am I missing?
You're assigning the value of the last read element to the variable _customer, you should use a datastructure (like a List) to keep all the elements you're getting and then pass that to the constructor.
Your code should be fixed this way:
private List<string> _customers = new List<string>();
public void ConnectionString()
{
using (var conn = new SqlCeConnection("Data Source=MyDatabase.sdf;Password=pass;Persist Security Info=True"))
{
conn.Open();
var comm = new SqlCeCommand("SELECT * FROM main", conn);
SqlCeDataReader reader = comm.ExecuteReader();
string customer;
while (reader.Read())
{
customer = (string)(reader["CustomerName"]);
Console.WriteLine(customer);
_customers.Add(customer);
}
}
}
I guess I was over-thinking this problem. I removed the Import/Export from both classes and instead decided to call the query directly in my main form so I could populate the listbox as needed. Then I'm assigning a variable to the listbox.SelectedItem and am passing that over to the AudioPlugin class. Everything is working as expected now, thanks for the suggestions to try and resolve the issue though.

Defensive Database Programming - should I be using a Singleton pattern

Is the following defensive programming?
What I mean is that if it loses the connection, or some problem occurs during run-time and then the user runsit again will the .NET framework have tidied up any open connections and objects that were created when it first ran?
I've heard mention of a "Singleton pattern" - is this something I should use in the static method CreateConnection?
class Program {
static void Main(string[] args) {
DataTable CasTable = fillSampleDataTable("SELECT top 100 * FROM x");
//do other stuff
}
static SqlConnection CreateConnection() {
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["XXX"].ConnectionString);
return conn;
}
static SqlDataAdapter CreateAdapter(string myCommand) {
SqlDataAdapter myAdapt = new SqlDataAdapter(myCommand, CreateConnection());
return myAdapt;
}
static DataTable fillSampleDataTable(string myCommand) {
using (var adapt = CreateAdapter(myCommand)) {
DataSet mySet = new DataSet();
adapt.Fill(mySet, "SampleData");
return mySet.Tables["SampleData"];
}
}
}
I would recommend you using the ADO.NET connection pool, a.k.a disposing the connections as soon as you have finished using them => wrap all IDisposable resources in using statements:
class Program
{
static void Main(string[] args)
{
DataTable CasTable = fillSampleDataTable("SELECT top 100 * FROM x");
//do other stuff
}
static DataTable fillSampleDataTable(string myCommand)
{
var connectionString = ConfigurationManager.ConnectionStrings["XXX"].ConnectionString;
using (var conn = new SqlConnection(connectionString))
using (var cmd = conn.CreateCommand())
using (var adapt = new SqlDataAdapter(cmd, conn))
{
conn.Open();
cmd.CommandText = myCommand;
DataSet mySet = new DataSet();
adapt.Fill(mySet, "SampleData");
return mySet.Tables["SampleData"];
}
}
}
But normally DataSets and DataTables are artifacts of the past. Today you are better off using strongly typed models.
So define a model:
public class MyModel
{
public int Id { get; set; }
public string Name { get; set; }
}
and then write a method that will return a list of those models:
class Program
{
static void Main(string[] args)
{
var models = SelectTop100Models("SELECT top 100 * FROM x");
//do other stuff
}
static IEnumerable<MyModel> SelectTop100Models()
{
var connectionString = ConfigurationManager.ConnectionStrings["XXX"].ConnectionString;
using (var conn = new SqlConnection(connectionString))
using (var cmd = conn.CreateCommand())
{
conn.Open();
cmd.CommandText = "SELECT top 100 * FROM x";
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
yield return new MyModel
{
Id = reader.GetInt32(reader.GetOrdinal("ID")),
Name = reader.GetString(reader.GetOrdinal("Name")),
};
}
}
}
}
}
Alternatively you might consider using an ORM framework such as the ADO.NET Entity Framework as it will simplify you querying the relational database and working directly with your strongly typed models using LINQ queries.

saving object to database c#

I'm having class Report and class Program, what I want to accomplish (no luck so far) is to send data from class Program and method SaveRep() to class Report method Save() and save it in this method.
I apologize if the question is badly formulated, I am really stuck at this, please help. Thanks
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Configuration;
using System.Data.SqlClient;
namespace Application
{
class Program
{
static void Main(string[] args)
{
//call method SaveRep
}
public void SaveRep(...)
{
int RepID = 1;
string Data1 = "SomeData1"
string Data2 = "SomeData2"
//This method should send the data above to method Save() in Report class
//data to this method will be provided from another method.
}
}
public class Report
{
private static int _repID;
public int RepID
{
get { return _repID; }
set { _repID = value; }
}
private static string _data1;
public string Data1
{
get { return _data1; }
set { _data1 = value; }
}
private static string __data2;
public string Data1
{
get { return _data2; }
set { _data2 = value; }
}
public void Save()
{
string strConnectionString = (#"server=(local)\sqlexpress;Integrated Security=True;database=DataBase");
SqlConnection connection = new SqlConnection(strConnectionString);
connection.Open();
// This method should save all data (RepID,Data1,Data2)
// to the DB, provided to her by SaveRep method from Program class.
// Properties are columns from a database
}
}
}
public void Save()
{
string yourConnString="Replace with Your Database ConnectionString";
using(SqlConnection connection = new SqlConnection(yourConnString))
{
string sqlStatement = "INSERT Table1(Data1) VALUES(#Data1)";
using(SqlCommand cmd = new SqlCommand(sqlStatement, connection))
{
cmd.CommandType = CommandType.Text;
cmd.Parameters.AddWithValue("#Data1",Data1);
//add more parameters as needed and update insert query
try
{
connection.Open();
cmd.ExecuteNonQuery();
}
catch(Exception ex)
{
//log error
}
}
}
}
Assuming your Data1 is the name of your Column name. Update the sqlStatement to have your relevant columns.
Now in SaveRep, you can call it like
public void SaveRep()
{
Report objReport=new Report();
objReport.Data1="Something somethinng";
objReport.Save();
}
string connectionString = ....; //adjust your connection string
string queryString = "INSERT INTO ....;"; //Adjust your query
using (SqlConnection connection = new SqlConnection(
connectionString))
{
using( SqlCommand command = new SqlCommand(
queryString, connection))
{
connection.Open();
command .ExecuteNonQuery();
}
}
One tiny thing that the previous speakers have not noticed, is that your Report class is severely broken. Why did you use static keywords on the variables? Do not do that! Well, unless you know what it does, and here I have the feeling you don't. Update your Report class to look like this:
public class Report
{
public int RepID {get;set;}
public string Data1 {get;set;}
public string Data2 {get;set;}
public void Save()
{
// what others said
}
}
'static' in your original code makes all te variables global/shared. That means that if you'd create 500 Report objects with new, they all would have the same one ID, Data1 and Data2. Any change to any single of those 500 objects would cause all the objects to change. But it's be misleading, the objects would not change. They would simply have the same data. Looking at the "ID" field, I think you'd rather want a separate objects with separate data for separate records..

N-tier architecture transacions handling

I'd like to implement N-tier architecture in my WinForms applications to separate (just logically - in one project) business logic from data access, however I have some doubts about using transacion in BLL. All tutorials I've found in the Internet either are very simple implementations of that architecture (without transactions), or are too complex for my needs. Trying to find my own way, I've come to the point, where I don't know the best way to handle transactions of in BLL layer.
I'll try to use some simple example to illustrate the problem (all classes are in separate files):
//DTO - Data Transfer Objects
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
}
public class SomeOtherItem
{
public int Id { get; set; }
public string Name { get; set; }
}
//DAL - Data Access layer
public class ItemDAL
{
public ItemDAL()
{
}
public void Add(Item item)
{
using (NpgsqlConnection conn = new NpgsqlConnection(connString))
{
conn.Open();
using (NpgsqlCommand cmd = new NpgsqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = #"INSERT INTO tbl_items (name)
VALUES (#name)";
cmd.Parameters.AddWithValue("#name", item.Name);
cmd.ExecuteNonQuery();
}
}
}
}
public class SomeOtherItemDAL
{
public SomeOtherItemDAL()
{
}
public void Add(SomeOtherItem someOtherItem)
{
using (NpgsqlConnection conn = new NpgsqlConnection(connString))
{
conn.Open();
using (NpgsqlCommand cmd = new NpgsqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = #"INSERT INTO tbl_some_other_items (name)
VALUES (#name)";
cmd.Parameters.AddWithValue("#name", someOtherItem.Name);
cmd.ExecuteNonQuery();
}
}
}
}
//BLL - Business Logic Layer
public class SomeBLL
{
public SomeBLL()
{
}
public void Add(Item item, SomeOtherItem someOtherItem)
{
ItemDAL itemDAL = new ItemDAL();
SomeOtherItemDAL someOtherItemDAL = new SomeOtherItemDAL();
// *** this must be done in one transaction ***
itemDAL.Add(item);
someOtherItemDAL.Add(someOtherItem);
}
}
Now, the problem is that if I want to use Transacion, I cannot use:
using (NpgsqlConnection conn = new NpgsqlConnection(connString))
in DAL. To use NpgsqlTransacion object I must somehow keep connection opened and visible in both DAL classes.
I've tried use TransacionScope object for that, but from from some reasons it's not working with PostgreSQL and the driver I'm using (INSERTS are done just after executed and there is no transaction rollback when exception within TransacionScope occures).
What I've come into is to make additional Singleton class to keep connection alive and manage transactions:
public class DB
{
private static DB instance;
private const string connString = #"Server=localhost;Port=5432;Database=db_test;User Id=usr_test;Password=pass";
private NpgsqlConnection conn;
private DB()
{
conn = new NpgsqlConnection(connString);
}
public static DB Instance
{
get
{
if (instance == null)
{
instance = new DB();
}
return instance;
}
}
#region --- connection ---
public NpgsqlConnection GetOpenConnection()
{
OpenConnection();
return conn;
}
private void OpenConnection()
{
if (conn.State == ConnectionState.Closed || conn.State == ConnectionState.Broken)
conn.Open();
}
public void CloseConnection()
{
if (conn != null && !inTransaction)
{
conn.Close();
}
}
#endregion
#region --- transaction ---
private NpgsqlTransaction trans;
private bool inTransaction;
public bool InTransaction { get { return inTransaction; } }
public void TransactionStart()
{
OpenConnection();
trans = conn.BeginTransaction();
inTransaction = true;
}
public void TransactionCommit()
{
if (inTransaction)
{
try
{
trans.Commit();
trans.Dispose();
}
finally
{
inTransaction = false;
CloseConnection();
}
}
}
public void TransactionRollback()
{
if (inTransaction)
{
try
{
trans.Rollback();
trans.Dispose();
}
finally
{
inTransaction = false;
CloseConnection();
}
}
}
#endregion
}
and rebuild both DAL Add methods to access connection like that:
//DAL - Data Access layer
public class ItemDAL
{
public ItemDAL()
{
}
public void Add(Item item)
{
using (NpgsqlCommand cmd = new NpgsqlCommand())
{
cmd.Connection = DB.Instance.GetOpenConnection();
cmd.CommandText = #"INSERT INTO tbl_items (name)
VALUES (#name)";
cmd.Parameters.AddWithValue("#name", item.Name);
cmd.ExecuteNonQuery();
}
if (!DB.Instance.InTransaction)
DB.Instance.CloseConnection();
}
}
public class SomeOtherItemDAL
{
public SomeOtherItemDAL()
{
}
public void Add(SomeOtherItem someOtherItem)
{
using (NpgsqlCommand cmd = new NpgsqlCommand())
{
cmd.Connection = DB.Instance.GetOpenConnection();
cmd.CommandText = #"INSERT INTO tbl_some_other_items (name)
VALUES (#name)";
cmd.Parameters.AddWithValue("#name", someOtherItem.Name);
cmd.ExecuteNonQuery();
}
if (!DB.Instance.InTransaction)
DB.Instance.CloseConnection();
}
}
Note that I'd like to follow the rule to "close database connection as soon as possible", so when Add method is called without transaction scope, I'd like it to close the connection.
So, the final questions are:
1. What do you think about it, is there a better way to handle that issue, any suggestions?
2. Should I dispose a connection in DB.CloseConnecion()? I surely do when using using (NpgsqlConnection conn = ...) { ... } pattern, but as Singleton is alive as long as application, does it make sense? Connection is returned to ConnectionPool after Close(), isn't it? Or maybe I should also dispose a Singleton object (together with connection), after each using?
3. It's not directly connected question, but if I use DTO objects (just properties, no methods) and have also some BusinessObjects (BO) with the same properties, but also with additional methods (validations, calculations, operations etc.), can it be inherited from DTO? Or maybe I can use full BusinessObject to transfer it between layers, and get rid off DTO?
EDIT: TransacionScope
As requested, I add some code from my tries with TransactionScope. Simply WinForm application, no Exceptions handling. As a result, there is an Exception window when I throw it, but in database I see records with values test1 ans test2. Both when debbuging in VS and executing application from .exe
using Npgsql;
using System.Transactions;
//...
private void button1_Click(object sender, EventArgs e)
{
using (System.Transactions.TransactionScope scope = new TransactionScope())
{
AddValue("test1");
AddValue("test2");
throw new Exception("bam!");
AddValue("test3");
scope.Complete();
}
}
private void AddValue(string value)
{
string connString = "Server=localhost;Port=5432;Database=db_test;User Id=usr_test;Password=pass";
using (NpgsqlConnection conn = new NpgsqlConnection(connString))
{
conn.Open();
using (NpgsqlCommand cmd = new NpgsqlCommand())
{
cmd.Connection = conn;
cmd.CommandText = #"INSERT INTO tbl_test (name)
VALUES (#name)";
cmd.Parameters.AddWithValue("#name", value);
cmd.ExecuteNonQuery();
}
}
}
I've never used NpgSql, but reading the documentation of NpgSql it appears they have some support of TransactionScope() if you add "enlist=true" in your connection string.
I'm looking at the "System.Transactions Support" section of the below NpgSql documentation:
http://npgsql.projects.postgresql.org/docs/manual/UserManual.html
Assuming TransactionScope() did work, then you can do simething like this...
using (var scope = new System.Transactions.TransactionScope())
{
ItemDAL itemDAL = new ItemDAL();
SomeOtherItemDAL someOtherItemDAL = new SomeOtherItemDAL();
// *** this must be done in one transaction ***
itemDAL.Add(item);
someOtherItemDAL.Add(someOtherItem);
scope.Complete()
}
What you did is brave, but not scalable. I'm not familiar with PGSQL, but this problem is the exact reason why the TransactionScope API was designed.
Can you show your code using the TransactionScope api? Make sure that you are not calling scope.Complete(); if an error occurs in one of the methods. Be careful to not "eat" the exceptions inside the methods because in that case the flow will continue just like if nothing happened.
More reading about TransactionScope here:
http://msdn.microsoft.com/en-us/library/ms172152.aspx
Update 1
Thanks for sharing your code that uses the TransactionScope class. The code looks perfectly correct to me. According to this (http://npgsql.projects.postgresql.org/docs/manual/UserManual.html) document (the same quoted by ChrisNeil52), Enlist=true should be included in the connection string for transactions to work.
You might be dealing with a buggy API. Good luck with this.
I know this sounds peculiar, but something I would try would be to use a different NpgsqlCommand constructor. new NpgsqlCommand("sql query", connection), instead of creating the command and assigning it the connection. They should be equivalent. but who know...

Categories