C# - Using database table to populate variables - c#

EDIT: Thank you everyone, I figured out how to get it to work now! Details below...
I'm kind of a newbie to C#, and I'm trying to teach myself the language by programming a really simple RPG game.
Right now, I'm at the point where I want to start adding different enemies to fight (up until now I just used a single one hardcoded in for testing).
I've started setting up a database with enemy info (one column for name, one for HP, one for common stats and attacks, etc.). I have it so that when you start combat with an enemy, the player is able to select a creature from a dropdown, and whichever creature he has will set a variable called "EnemyID".
What I want to do is use that EnemyID variable to correspond to a row in my database, then pull the value of each column into variables that I can then reference during combat.
Is this something that's possible to do? If so, could someone explain the method to me in relatively simple terms? Even just a small example of how to import row data from any kind of database will do, I'm good at understanding code once I see it in use a couple of times.
(Oh yeah, if it matters, I'm using Visual Studio Express 2013, and my database is a SQL Server Express 2014 database.)
Thanks in advance!
EDIT:
After finding a simple tutorial for ADO.NET, and following a suggestion from one of the posters, I've come up with the following code.
public void DataPoll()
{
SqlConnection MonDat = new SqlConnection("Data Source=(local);
Initial Catalog=TestDatabase;Integrated Security=SSPI");
SqlDataReader rdr = null;
try
{
MonDat.Open();
SqlCommand cmd = new SqlCommand(
"select * from Monsters where Id = EnemyID", MonDat);
rdr = cmd.ExecuteReader();
while (rdr.Read())
{
EnemyIDBuffer = (int)rdr["Id"];
EnemyName = (string)rdr["Name"];
EnemyHPBase = (int)rdr["HP"];
EnemyAtkBase = (int)rdr["Atk"];
EnemyDefBase = (int)rdr["Def"];
EnemyMagBase = (int)rdr["Mag"];
PrimAtk = (string)rdr["PrimAtk"];
SecoAtk = (string)rdr["SecoAtk"];
TertAtk = (string)rdr["TertAtk"];
RareAtk = (string)rdr["RareAtk"];
}
}
finally
{
if (rdr != null)
{
rdr.Close();
}
if (MonDat != null)
{
MonDat.Close();
}
}
}
However, when I try to run it, my program stalls and crashes. I'm guessing I have something configured wrong (I just took script from the tutorial and tweaked it slightly). Can anyone give me a hand figuring out where I went wrong?
EnemyID is a variable I used to assign what enemy is fought, based on a menu selection. I'm trying to use that variable to generate the row ID to pull the rest of the row data from, but I think that might be causing an issue.
EDIT2: It took me longer than it really should have, but I figured it out. I had to change my code a little tiny bit.
SqlCommand cmd = new SqlCommand(
"select * from Monsters where Id = " + EnemyID, MonDat);
I have a habit of forgetting that you're able to join statements like this. I made a new project that only polled data and threw it into my variables, and from there put it into text boxes, and with this method I was able to poll two different sets of enemy stats by assigning different EnemyID values to two different buttons. Proof of concept, right there.
Thanks to both people who replied, both suggestions were equally useful to getting this working. :)

There's numerous tutorials out there on how to use a database, the first two use straight ADO.NET which is pure data access, making you responsible for its interaction in your code:
ADO.NET Overview
ADO.NET Tutorial for Beginners
The next two, one is for Entity, and the other for nHibernate, they connect to SQL databases and convert the objects there to usable code in your program through a process called object relational mapping.
Entity Framework Tutorials
nHibernate Tutorials
These are all relevant links to stuff in the most current years, with VS 2013; hopefully that provides you a good starting point.

You can do something like this:
Your SQL should pass in the procedure name and EnemyId.
The stored procedure would do a select * from Enemies where EnemyId = #EnemyId
DataSet dataSet = HSPDataAccessProxy.Instance.ExecuteDataSet(sql);
The dataSet has the table that is returned by the store procedure and you can retrieve the columns you need from that table.

Related

c# Windows form not updating values to SQL database

I have a problem in my program that's supposed to store projects given by the user in a database. I'm stuck on the edit project button. After entering new values in the program and hitting the button to save the values everything runs successfully with no errors. The messagebox that says "project edited" appears, I hit ok but the database stays the same. There is no error in the code, the SQL code that gets sent to update the database values is also correct but it doesn't work. Can anyone help with this because I am lost.
Here is the method that creates and executes the SQL code to update the database.
enter image description here
Wow man that code is wrong in so many ways according to code standards and principles most popular :) but that is not what the question is about directly, though getting you past lost we have to start at the basic tbh:
Suggestions
when you catch that exception if it comes, show that in a messagebox also you can even add an error icon as part of the .Show command, it's build in.
Move the connection.Close to the finally block instead of having it replicated
Consider making an SQL procedure instead and just parse the parameter into that, this code is prone to sql injection that you pose
Consider not making the procedure and familiarize Yourself with entity framework, it's going to make your life so much easier
do not concatenate like that, use interpolation or string.Combine or you'll be copying stuff all around on the stack, for each + a new copy one and two into third, it is super inefficient
When You write the code works and the sql is correct, consider that the outcome is not the desired and therefore it doesn't technically ;) the best and the worst about computers, is that they do what you ask.
Don't write Your DAL code in the form at all
Consider checking your parameters for default values
You do not have data to say 'project was updated' only 'values were saved', you do not check the old values in the code
Still besides that I do not see why what you wrote wouldn't work, provided the resulting sql is valid in what db you use, but i suppose if you do some of these things the error will present itself
I don't think it's a connection problem because I have a function that updates only the finishdate of the project and that works completely fine.
Here is the function:
public static MySqlCommand FinishProject(int projID, string finishdate) {
try {
if (connection != null) {
connection.Open();
cmd = connection.CreateCommand();
cmd.CommandType = CommandType.Text;
cmd.Parameters.AddWithValue("#value", projID);
cmd.Parameters.AddWithValue("#finishdate", finishdate);
cmd.CommandText = "UPDATE `b1c`.`projects` SET `finishdate` = (#finishdate) WHERE (`projectid` = (#value));";
int i = cmd.ExecuteNonQuery();
connection.Close();
if (i != 0) {
MessageBox.Show("Project finalized.");
i = 0;
}
}
} catch (Exception ex) {
MessageBox.Show("Catch");
connection.Close();
}
return cmd;
}
You can see it's basically the same the only difference are the values.
So it shouldn't be a connection thing because this one works fine I think.
I also don't think it's a problem in the SQL database because all the problems
I've had up until now that had anything to do with the database have shown as errors in visual studio.
If anyone can help I will provide screenshots of anything you need and thank you all once again for trying to help.
Here is the screenshot of the function I've pasted previously so it's easier to look at.
finishprojectfunction

Update Table based on updated information

C#
I am currently working on a project that relies on downloading a table roughly 100 entries.
I first download the table and store it in a local variable, then link the variable to a DataGridView where the user can edit values.
Once done the user pushes save and it must update the table in the SQL DB with the changed information.
I am asking for a best practice here, is it advisable to delete the rows you have changed and bulk upload the changes or update based or even multiple parameters?
I know when working with SQL exclusively, you can use commands like UPDATE FROM and use tables as the source but I do not know how this would work using C#.
Thanks for help in advance.
public DataTable GetSingleTable(string sTableName, string sGetConnString)
{
DataTable dtTabletoReturn = new DataTable();
string sCommand = "SELECT * FROM " + sTableName+ " WHERE
BranchID = '"+ sBranchID +"'";
SqlConnection sqlConnection = new SqlConnection(sGetConnString);
sqlConnection.Open();
SqlDataAdapter sqlOilAdapter = new SqlDataAdapter(sCommand, sqlConnection);
sqlOilAdapter.Fill(dtTabletoReturn);
sqlConnection.Close();
return dtTabletoReturn;
}
Entity Framework MVC will be the best practice for you. You can start with the basics from here:
https://www.entityframeworktutorial.net/what-is-entityframework.aspx
As others have mentioned, if this is not impossible in Your project, try EF core or Dapper - both should simplify your struggles (not without adding some other later in some peculiar scenarios).
If going with EF core, take a look at connected / disconnected scenarios.
In any case, when getting data by using EF in, lets say for simplicity, connected scenario, the EF core context tracks entities (Your data).
It will detect changes made to those entities, so in the end, just calling a SaveChanges() method on EF core DbContext will save and transfer just the modified data.
Mind that this very basic explanation, You will have to read about it by Yourself if You choose to go that way.
So after fiddling around and I rate the best procedure would be to use the DataAdapter Update command, I was looking for best practices here. Unfortunately the Entity Framework, as far as I can tell, works best when building an application from scratch. https://www.youtube.com/watch?v=Y-aGjF6_Ptc&t=166s <- this was the best so far.

Generic and a Database

I'm sure this has been asked; but I can't find the exact context. Would this be a novice or ill-advised approach? Or is this an acceptable approach.
I have a project in which I'm working on. I have these four tables:
Customer
Address
Site
Login
So the database is basic, nothing too crazy.
But I've created a Restful Service to link my Client to my Server. Which to perform my Crud Operation will require a simple Insert Command.
Like:
string sqlInsertStr = "INSERT INTO Customer (First, Last, Phone, Email)
VALUES (#first, #last, #phone, #email)";
So that'd handle one of my four tables; my thought was one of these two approaches:
Use a Generic to store each individual Query; then loop to iterate each query.
Use a Generic that contains a series of Stored Procedures to iterate through.
My goal is to avoid several strings to iterate through and write to the database.
My reasoning was, when a client modifies an individual field or an entire profile; it would handle all the tables congruently. In SQL I know it has limitations when trying to insert data across multiple tables.
Is that a bad approach; this particular is fairly small and simple. So I'm not looking to go to crazy; but it is for school. So I'm trying to understand if I'm even coming up with a valid solution, and if I am is it considered a right solution?
Any input on the matter and explanation would be terrific.
Update:
I apologize for the confusion; yes I have one Object Model- which contains all the information for each column of each table. My question is this:
SQL does not like Inserting across multiple tables. So this works just fine for one table:
using (connection)
{
string sqlInsertStr = "INSERT INTO Customer (First, Last, Phone, Email)
VALUES (#first, #last, #phone, #email)";
connection = new SqlConnection(connectionStr);
command = new SqlCommand();
command.Connection = connection;
command.Connection.Open();
command.CommandText = sqlInsertStr;
SqlParameter firstParam = new SqlParameter(#"first", cust.First);
SqlParameter lastParam = new SqlParameter(#"last", cust.Last);
SqlParameter phoneParam = new SqlParameter(#"phone", cust.Phone);
SqlParameter emailParam = new SqlParameter(#"email", cust.Email);
command.Parameters.AddRange(new SqlParameter[]
{ firstParam, lastParam, phoneParam, emailParam});
command.ExecuteNonQuery();
command.Connection.Close();
}
That will easily write to the one table; but what about all the other data that needs to be inserted to those other tables? Would I need to create a sqlInsertStr for each table? Or couldn't I simply do something like this:
using(connection)
{
list<string> sqlInsertStr = new list<string>();
sqlInsertStr.Add("INSERT INTO Customer (First, Last, Phone, Email)
VALUES (#first, #last, #phone, #email)"l
sqlInsertStr.Add("INSERT INTO Address (Street, City, State, Zip, Country)
VALUES (#Street, #City, #State, #Zip, #Country)";
connection = new SqlConnection(connectionStr);
command = new SqlCommand();
command.Connection = connection;
command.CommandText = sqlInsertStr;
// Repeat with Parameters and etc.
}
So wouldn't essentially using a Generic or StringBuilder allow me to just include one string to handle all those Inserts with the proper data? Or am I completely over-thinking / confusing myself. I'm still learning, so an explanation would be great.
Based on the numerous questions here on making it work, I would recommend not trying to be clever by genericizing too much. My recommended approach for any problem is:
Make it work - by any means necessary (even {gasp} copy-and-paste)
Make it better - refactoring, genericizing, etc.
Make it faster - by profiling and looking at the SLOWEST pieces first
Too many people try to do 2) and 3) before it even works and end up spending more time hacking or micro-optimizing just to get it to work. If you get it to work first, you can always throw away your optimizations and start over, and you still have a working product!
All that to say, don't worry about generics yet - create four repositories (one for each table), then analyze and see if Generics can make is cleaner.
Comment on your update
am I completely over-thinking / confusing myself
It sounds like it. Don't try to make one class handle CRUD operations for all types just to avoid using the same connection/command code in multiple places. Each repository (and I'm using that term loosely, meaning some class that handles the CRUD operations) should have its own connection/command code. If later you decide that you can create a base class that deals with the grunge work, that's fine - or you can use a ORM layer like Entity Framework or NHibernate to handle that for you. Or you can have a mixture. Whatever works best AFTER you get it to work. :)

how to get exec string for sql management studio from .net code

I am debugging code someone else wrote that calls a lot of stored procedures (sql server 2008 r2) from C# code. The C# code looks like this
SqlCommand sqlCommand = new SqlCommand(strSP, ConnectionOpen());
sqlCommand.CommandType = CommandType.StoredProcedure;
sqlCommand.CommandTimeout = intTimeOut;
//System.Data.SqlClient.SqlParameter[] prmSQL
if (prmSQL != null)
{
while (i < prmSQL.Length)
{
sqlCommand.Parameters.Add(prmSQL[i]);
i = i + 1;
}
}
SqlDataReader sqlReader = sqlCommand.ExecuteReader();
For debugging my stored procedures I really need the string that sql management studio needs which is like
exec sp_name param one, param two (with quotes if needed for strings and dates..)
The sql command object does not provide this string via some property. The only way I know is to run the sql profiler on sql server and grab the string. Unfortunately the DBA's do not like this since they say running the profiler impacts performance. Is there any addin or code snippet you guys use to get the sp exec string from c# code ? Whats the best way to get this string ? Thanks
You could use a tool like mvc-mini-profiler available on NuGet (note: the name is misleading; it isn't limited to MVC). Minor clarification - since it wraps the connection, you would need to use the abstract DbConnection rather than SqlConnection, and then you just tweak the one line of code (probably in a utility class somewhere) that creates your connection, i.e. instead of:
var conn = new SqlConnection(someString);
return conn;
you might use:
var conn = new SqlConnection(someString);
return new StackExchange.Profiling.Data.ProfiledDbConnection(
conn, MiniProfiler.Current);
There's a couple of other steps to enable it (all shown on the site page), but it literally takes 2 minutes to add to an MVC application. The output is that it monitors, in real time, for enabled users (developers etc), all the activity. We use it 24x7 on stackoverflow/stackexchange (meaning: we made very sure it didn't impact performance). A live demo is available on https://data.stackexchange.com/ - just log in, and the profiling data is visible top-left. It automatically presents the data in a form runnable from SSMS, because that is how we often use it - so: it presents parameters as though they were variable declarations / initializations.
It also plays nicely with ORMs such as LINQ-to-SQL and dapper-dot-net (and many others).
Rep is too low (still a noob to StackOverflow)to comment so I'm posting this as an answer. My apologies. However, you might consider looking at SMO. SMO is a .NET object model that can be used to interact with SQL Server. Using SMO you can get a reference to a specific Stored Procedure
and then enumerate it's parameters.
That might help you get started.
In order to construct the EXEC command, you will need to know the parameter names used by the procedure. I believe you can find them by using the GetDbSchemaTable method, whcih will retrieve stored procedure SQL (I have done this using MS-Access/OLEDB and am assuming it works the same for MS-SQL/SqlClient):
using (conn == new OleDb.OleDbConnection(DBConnection)) {
conn.Open();
DataTable DBObject = conn.GetOleDbSchemaTable(OleDb.OleDbSchemaGuid.Procedures, null);
}
The column named "PROCEDURE_DEFINITION" contains the procedure's SQL and hopefully the parameter list.
You may also want to have a look at Obtaining Schema Information from a Database.
HTH

How do you perform basic CRUD on a database using windows forms?

I have used the .Net framework extensively for backend processes and web pages but I have never needed to use Windows Forms. The only experience I have with Windows form type technology is with Delphi 6&7.
I have searched for tutorial as they seem to be too basic for what I am looking for. Or it seems inappropriate for what I am trying to do.
I would like to have a grid display (for this I have been using DataGridView) on a form (which I have managed to do) so that the user can view, filter and search for data. Other things like pagination would also be involved but I think that I can work out how to do that for myself.
All of the examples I come across use the DataGridView for editing, adding and deleting as well. I am not that comfortable with the idea that user user the grid for everything. It seems confusing and likely to be quite error prone.
I would like to have buttons to Add, Edit and Delete the various types of data. So for example if I had a Form to manage customers, I would like to be able to select the row to edit and click the button. This should open a new windows form with all the data preloaded in textboxes, radio buttons, checkboxes and dropdowns etc.
If they click add they would go to the same screen as the edit except all the information there would be blank. If they select a row and clicked delete it would then delete that customer and remove it from the DataGridView.
With some of the controls and databinding options I have tried it seems to fetch ALL the rows from the table. How would I be able to just get the row that I am interested in from the database. I am not sure what best practises are here.
I am suffering from information overload right now and would just appreciate someone point me in the right direction.
EDIT:
I should mention that from my Delphi days I am expecting to be able to set up something like a query or SqlCommand (Drag and drop on the gui) and set its SQL property, perhaps parameterize the SQL a little bit. Drag and drop a datasource on the form. Point the datasource to the SqlQuery/SqlCommand and click activate on the command. Now I can drag and drop compononents onto the form and set their datasource properties and to the field that they refer to.
Take a look at this sample. It shows basic ADO.NET binding to WinForm controls (not just the DataGridView).
You could do it in many ways. And there are a bunch of frameworks to help. Entity Framework, NHiberbnate.
but at a low level you can use the database related objects. SqlConnection, SqlCommand, DataReader. Below is SQL-Server related example to load a rows.
private IList<IPosition> PositionsLoad(SqlConnection connection, PositionsRequest request)
{
SqlCommand cmd = new SqlCommand();
cmd.Connection = connection;
cmd.CommandText = "select * from mytable WHERE x ";
cmd.CommandType = CommandType.Text;
//Get the reader
SqlDataReader reader = cmd.ExecuteReader;
IList<IPosition> ret = new List<IPosition>();
if (reader.HasRows()) {
//Create our converter to convert DataReader into a business object/s
DataReaderToPosition readerConvert = new DataReaderToPosition();
//loop rows
while (reader.Read) {
IPosition pos = readerConvert.DataReaderToBusinessObject(reader);
ret.Add(pos);
}
}
reader.Close();
return ret;
}
using the datareader:
Public Overrides Function DataReaderToBusinessObject(ByVal reader As System.Data.IDataReader) As IPosition
Dim res As IPosition = New Position
res.ItemDate = reader.GetDateTime(reader.GetOrdinal("Date"))
res.Strategy = reader.GetString(reader.GetOrdinal("Strategy"))
res.SubStrategy = reader.GetString(reader.GetOrdinal("SubStrategy"))
res.BrokerPrime = reader.GetString(reader.GetOrdinal("BrokerPrime"))
res.BrokerExecuting = reader.GetString(reader.GetOrdinal("BrokerExecuting"))
res.AccountName = reader.GetString(reader.GetOrdinal("AccountName"))
res.ExpectedLoss = reader.GetDouble(reader.GetOrdinal("Expected_Loss"))
res.RiskNotional = reader.GetDouble(reader.GetOrdinal("Risk_Notional"))
res.ModelDelta = reader.GetDouble(reader.GetOrdinal("Model_Delta"))
res.ExpectedTrancheLoss = reader.GetDouble(reader.GetOrdinal("Expected_Tranche_Loss"))
res.BaseCorrelation = reader.GetDouble(reader.GetOrdinal("Base_Correlation"))
res.LossOnSingleNameDefault = reader.GetDouble(reader.GetOrdinal("Loss_on_Single_Name_Default"))
res.RiskCapitalAllocation = reader.GetDouble(reader.GetOrdinal("Risk_Capital_Allocation"))
res.MarginFundingAllocation = reader.GetDouble(reader.GetOrdinal("Margin_Funding_Allocation"))
res.DataSource = reader.GetString(reader.GetOrdinal("DataSource"))
Return res
End Function

Categories

Resources