Reputation: 15
I am setting up a winform
that takes the first name, last name, and student ID of a student into an sql
database named college
, and performs a stored procedure which searches for that student, then displays the results in a DataGridView
when the search button is pressed. Whenever I press the search button I get the following error
"A first chance exception of type 'System.TypeInitializationException' occurred in Search2.exe".
My program is skipping over the Try
block shown, and going to the Catch
statement. Can anyone tell me why this is?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Search2
{
public partial class frmSearch : Form
{
public frmSearch()
{
InitializeComponent();
}
private void btnSearch_Click(object sender, EventArgs e)
{
string studid, fname, lname;
try
{
// get the values
fname = txtFname.Text.Trim();
lname = TxtLname.Text.Trim();
studid = txtStudentID.Text.Trim();
//instantiate datatier
Class1 astudent = new Class1();
DataSet ds = new DataSet();
ds = astudent.GetStudents(studid, fname, lname);
// populate the datagrid with dataset
dgvStudents.DataSource = ds.Tables[0];
}
catch (Exception ex)
{
}
}
private void frmSearch_Load(object sender, EventArgs e)
{
// TODO: This line of code loads data into the 'collegeDataSet.STUDENT' table. You can move, or remove it, as needed.
//this.sTUDENTTableAdapter.Fill(this.collegeDataSet.STUDENT);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualBasic;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Data.SqlClient;
using System.Globalization;
namespace Search2
{
class Class1: frmSearch
{
static String connString = ConfigurationManager.ConnectionStrings["Data Source=EVEDELL17;Initial Catalog=College;Integrated Security=True"].ConnectionString;
static SqlConnection myConn = new SqlConnection(connString);
static System.Data.SqlClient.SqlCommand cmdString = new System.Data.SqlClient.SqlCommand();
public DataSet GetStudents(string studid, string fname, string lname)
{
try
{
// Open Connection
myConn.Open();
//clear command argument
cmdString.Parameters.Clear();
//command
cmdString.Connection = myConn;
cmdString.CommandType = CommandType.StoredProcedure;
cmdString.CommandTimeout = 1500;
cmdString.CommandText = "SearchStudent";
// define input parameter
cmdString.Parameters.Add("@fname", SqlDbType.VarChar, 1).Value = fname;
cmdString.Parameters.Add("@lname", SqlDbType.VarChar, 25).Value = lname;
// adapter and daraset
SqlDataAdapter aAdapter = new SqlDataAdapter();
aAdapter.SelectCommand = cmdString;
DataSet aDataSet = new DataSet();
// fill adapter
aAdapter.Fill(aDataSet);
//return Dataset
return aDataSet;
}
catch (Exception ex)
{
throw new ArgumentException(ex.Message);
}
finally
{
myConn.Close();
}
}
}
}
Upvotes: 0
Views: 112
Reputation: 9824
Xander got the answer I think: Something goes "bump" when initializing those static fields. And static/type constructors are notoriously poor at communicating exceptions:
https://learn.microsoft.com/en-us/dotnet/api/system.typeinitializationexception
The underlying problem however, is one of pattern. And there are several issues. However you can often fudge these parts for simple learning examples.
Disposeable
SqlConnectons is Disposeable, as it contains some unmanaged resources. Always dispose of disposeables. My personal rule is:
Avoid Globals/Static
Static variables are global variables. And quicklly after inventing those, we realized that using either was a terrible idea 90% of the time. Particular for exchanging/sharing data.
While I go even a bit further, never have a field that is static and can be written. constant
and readonly
(runtime constants) should be the only statics you ever use. If you can not make it that, do not make it static. Stuff like connection strings are way up on that rule. If you can not tag it like that, do not make it a static to begin with.
At tops I make a class, struct or tupple. And assign a instance of it to a static readonly/constant variable. Stuff like connection strings are either a instance variable, or handed in on each call of a function like GetStudents
. Indeed, Class1 looks a lot like a DB access abstraction. And those in particular fall under the "do not make static" rule.
Upvotes: 0
Reputation: 1709
Judging by the exception type--TypeInitializationException
--I suspect the problem is with the static field initializers:
static String connString = ConfigurationManager.ConnectionStrings["Data Source=EVEDELL17;Initial Catalog=College;Integrated Security=True"].ConnectionString;
static SqlConnection myConn = new SqlConnection(connString);
static System.Data.SqlClient.SqlCommand cmdString = new System.Data.SqlClient.SqlCommand();
Those initializers will run the first time their containing class (Class1
) is "touched" by the runtime. Because they aren't in a method, it's hard for the compiler to give a helpful stack trace when they fail. Try replacing the inline initializers with a static constructor:
static String connString;
static SqlConnection myConn;
static System.Data.SqlClient.SqlCommand cmdString;
static Class1() {
connString = ConfigurationManager.ConnectionStrings["Data Source=EVEDELL17;Initial Catalog=College;Integrated Security=True"].ConnectionString;
myConn = new SqlConnection(connString);
cmdString = new System.Data.SqlClient.SqlCommand();
}
I think you'll get a better error message that way. You can also set a breakpoint in the constructor to see exactly what happens during initialization.
Read more here: https://learn.microsoft.com/en-us/dotnet/api/system.typeinitializationexception?view=netframework-4.8#Static
Upvotes: 3