Linked database sql server 2008 to visual studio 2010 - c#

I'm doing a software which can manage electronics component quantity. My software is linked to a first DB (T_importReel) that will have all the new reels component when the factory receive them. The machine that uses component is counting how many it has used in each reels and marks the count in its own DB (SiplaceSetupCenter). I'm using an Unique ID for each reel so T_importReel and SiplaceSetupCenter are using that Unique ID to differentiate each bobine. Basically, I have in the two DB two columns that are the same.
Now I would like to link the two DB with a simple SQL request. This is supposed to be simple : for every Unique ID where the quantity is different between the two DB, T_importReel changes its quantity for the Unique ID concerned.
But when I've tried to do so, my soft didn't access DB_center...
How can I enable my soft to access SetUpCenter? What do I do to update T_importreels?
Thank you in advance.
private void BT_croiserlesdonnées_Click(object sender, EventArgs e)
{
SqlConnection con = new SqlConnection("Server=SIPLACESERVER;Database=SiplaceSetupCenter;User=**;Password=****;");
con.Open();
// using (SqlCommand command = new SqlCommand(
// "SELECT packagingunitid,Quantity FROM PackagingUnit",
// con))
//using (SqlDataReader reader = command.ExecuteReader())
// {
// while (reader.Read())
// {
// for (int i = 0; i < reader.FieldCount; i++)
// {
// Console.WriteLine(reader.GetValue(i));
// }
// Console.WriteLine();
// }
// }
string queryString =
"Select Quantity FROM PackagingUnit, T_ImportReels WHERE PackagingUnit.id = T_ImportReels.id";
SqlCommand command = new SqlCommand(
queryString, con);
SqlDataReader reader = command.ExecuteReader();
try
{
while (reader.Read())
{
Console.WriteLine(String.Format("{0}, {1}",
reader[0], reader[1]));
}
}
finally
{
// Always call Close when done reading.
reader.Close();
}
//_siplaceSetupCenter = new SiplaceSetupCenterEntities();
//string queryString = #"SELECT * FROM PackagingUnit";
//using (SiplaceSetupCenterEntities context = new SiplaceSetupCenterEntities())
//{
// //ObjectQuery<PackagingUnit> contactQuery =_siplaceSetupCenter.CreateQuery<PackagingUnit>(queryString);
// // new ObjectParameter("fn", "Frances"));
// Console.WriteLine("on y go"); Console.WriteLine("on y go"); Console.WriteLine("on y go"); Console.WriteLine("on y go");
// var tableresult = _siplaceSetupCenter.PackagingUnit.FirstOrDefault(id => id.PackagingUnitId == "100717132736");
//var contactQuery = from contact in context.PackagingUnit
// where contact.PackagingUnitId == "200717100643"
// select contact;
//// Iterate through the collection of Contact items.
//foreach (PackagingUnit result in contactQuery)
// Console.WriteLine("First Name: {0}, Last Name: {1}",result.PackagingUnitId, result.Quantity);
//Console.WriteLine(table_result.Count); Console.WriteLine(table_result.Count); Console.WriteLine(table_result.Count); Console.WriteLine(table_result.Count);
//foreach (var value in table_result)
//{
// Console.WriteLine(value); Console.WriteLine(value); Console.WriteLine(value); Console.WriteLine(value);
// Console.WriteLine("a"); Console.WriteLine("a"); Console.WriteLine("a"); Console.WriteLine("a");
//}
//string dataid = tableresult.PackagingUnitId;
//Console.WriteLine(dataid);
}
}
I

I don't know how it could help you?
You can make two connections that each one connects to each database then select your Data from two Db. In this situation you have 2 DataTable/ SqlDataReader objects. Now You can compare these Datatables/SqlDataReader row by row with each others or combine them into a single datatable/SqlDataReader or update each one base another.

Sql Server's name should be sth like this SERVERNAME\INSTANCENAME. But your server name is this: SIPLACESERVER/SIPLACE_2012EX.
If you use "\" character in your server name, in connection string change it into "\\".
i mean change SERVERNAME\INSTANCENAME into SERVERNAME\\INSTANCENAME.

Related

C# MySQLDataReader returns column names instead of field values

I am using MySQLClient with a local database. I wrote a method which returns a list of data about the user, where I specify the columns I want the data from and it generates the query dynamically.
However, the reader is only returning the column names rather than the actual data and I don't know why, since the same method works previously in the program when the user is logging in.
I am using parameterised queries to protect from SQL injection.
Here is my code. I have removed parts which are unrelated to the problem, but i can give full code if needed.
namespace Library_application
{
class MainProgram
{
public static Int32 user_id;
static void Main()
{
MySqlConnection conn = LoginProgram.Start();
//this is the login process and works perfectly fine so i won't show its code
if (conn != null)
{
//this is where things start to break
NewUser(conn);
}
Console.ReadLine();
}
static void NewUser(MySqlConnection conn)
{
//three types of users, currently only using student
string query = "SELECT user_role FROM Users WHERE user_id=#user_id";
Dictionary<string, string> vars = new Dictionary<string, string>
{
["#user_id"] = user_id.ToString()
};
MySqlDataReader reader = SQLControler.SqlQuery(conn, query, vars, 0);
if (reader.Read())
{
string user_role = reader["user_role"].ToString();
reader.Close();
//this works fine and it correctly identifies the role and creates a student
Student user = new Student(conn, user_id);
//later i will add the logic to detect and create the other users but i just need this to work first
}
else
{
throw new Exception($"no user_role for user_id - {user_id}");
}
}
}
class SQLControler
{
public static MySqlDataReader SqlQuery(MySqlConnection conn, string query, Dictionary<string, string> vars, int type)
{
MySqlCommand cmd = new MySqlCommand(query, conn);
int count = vars.Count();
MySqlParameter[] param = new MySqlParameter[count];
//adds the parameters to the command
for (int i = 0; i < count; i++)
{
string key = vars.ElementAt(i).Key;
param[i] = new MySqlParameter(key, vars[key]);
cmd.Parameters.Add(param[i]);
}
//runs this one
if (type == 0)
{
Console.WriteLine("------------------------------------");
return cmd.ExecuteReader();
//returns the reader so i can get the data later and keep this reusable
}
else if (type == 1)
{
cmd.ExecuteNonQuery();
return null;
}
else
{
throw new Exception("incorrect type value");
}
}
}
class User
{
public List<string> GetValues(MySqlConnection conn, List<string> vals, int user_id)
{
Dictionary<string, string> vars = new Dictionary<string, string> { };
//------------------------------------------------------------------------------------
//this section is generating the query and parameters
//using parameters to protect against sql injection, i know that it ins't essential in this scenario
//but it will be later, so if i fix it by simply removing the parameterisation then im just kicking the problem down the road
string args = "";
for (int i = 0; i < vals.Count(); i++)
{
args = args + "#" + vals[i];
vars.Add("#" + vals[i], vals[i]);
if ((i + 1) != vals.Count())
{
args = args + ", ";
}
}
string query = "SELECT " + args + " FROM Users WHERE user_id = #user_id";
Console.WriteLine(query);
vars.Add("#user_id", user_id.ToString());
//-------------------------------------------------------------------------------------
//sends the connection, query, parameters, and query type (0 means i use a reader (select), 1 means i use non query (delete etc..))
MySqlDataReader reader = SQLControler.SqlQuery(conn, query, vars, 0);
List<string> return_vals = new List<string>();
if (reader.Read())
{
//loops through the reader and adds the value to list
for (int i = 0; i < vals.Count(); i++)
{
//vals is a list of column names in the ame order they will be returned
//i think this is where it's breaking but im not certain
return_vals.Add(reader[vals[i]].ToString());
}
reader.Close();
return return_vals;
}
else
{
throw new Exception("no data");
}
}
}
class Student : User
{
public Student(MySqlConnection conn, int user_id)
{
Console.WriteLine("student created");
//list of the data i want to retrieve from the db
//must be the column names
List<string> vals = new List<string> { "user_forename", "user_surname", "user_role", "user_status"};
//should return a list with the values in the specified columns from the user with the matching id
List<string> return_vals = base.GetValues(conn, vals, user_id);
//for some reason i am getting back the column names rather than the values in the fields
foreach(var v in return_vals)
{
Console.WriteLine(v);
}
}
}
What i have tried:
- Using getstring
- Using index rather than column names
- Specifying a specific column name
- Using while (reader.Read)
- Requesting different number of columns
I have used this method during the login section and it works perfectly there (code below). I can't figure out why it doesnt work here (code above) aswell.
static Boolean Login(MySqlConnection conn)
{
Console.Write("Username: ");
string username = Console.ReadLine();
Console.Write("Password: ");
string password = Console.ReadLine();
string query = "SELECT user_id, username, password FROM Users WHERE username=#username";
Dictionary<string, string> vars = new Dictionary<string, string>
{
["#username"] = username
};
MySqlDataReader reader = SQLControler.SqlQuery(conn, query, vars, 0);
Boolean valid_login = ValidLogin(reader, password);
return (valid_login);
}
static Boolean ValidLogin(MySqlDataReader reader, string password)
{
Boolean return_val;
if (reader.Read())
{
//currently just returns the password as is, I will implement the hashing later
password = PasswordHash(password);
if (password == reader["password"].ToString())
{
MainProgram.user_id = Convert.ToInt32(reader["user_id"]);
return_val = true;
}
else
{
return_val = false;
}
}
else
{
return_val = false;
}
reader.Close();
return return_val;
}
The problem is here:
string args = "";
for (int i = 0; i < vals.Count(); i++)
{
args = args + "#" + vals[i];
vars.Add("#" + vals[i], vals[i]);
// ...
}
string query = "SELECT " + args + " FROM Users WHERE user_id = #user_id";
This builds a query that looks like:
SELECT #user_forename, #user_surname, #user_role, #user_status FROM Users WHERE user_id = #user_id;
Meanwhile, vars.Add("#" + vals[i], vals[i]); ends up mapping #user_forename to "user_forename" in the MySqlParameterCollection for the query. Your query ends up selecting the (constant) value of those parameters for each row in the database.
The solution is:
Don't prepend # to the column names you're selecting.
Don't add the column names as variables to the query.
You can do this by replacing that whole loop with:
string args = string.Join(", ", vals);

IBM db2Command SQL SELECT * FROM IN #namedParameter

When using the C# code below to construct a DB2 SQL query the result set only has one row. If I manually construct the "IN" predicate inside the cmdTxt string using string.Join(",", ids) then all of the expected rows are returned. How can I return all of the expected rows using the db2Parameter object instead of building the query as a long string to be sent to the server?
public object[] GetResults(int[] ids)
{
var cmdTxt = "SELECT DISTINCT ID,COL2,COL3 FROM TABLE WHERE ID IN ( #ids ) ";
var db2Command = _DB2Connection.CreateCommand();
db2Command.CommandText = cmdTxt;
var db2Parameter = db2Command.CreateParameter();
db2Parameter.ArrayLength = ids.Length;
db2Parameter.DB2Type = DB2Type.DynArray;
db2Parameter.ParameterName = "#ids";
db2Parameter.Value = ids;
db2Command.Parameters.Add(db2Parameter);
var results = ExecuteQuery(db2Command);
return results.ToArray();
}
private object[] ExecuteQuery(DB2Command db2Command)
{
_DB2Connection.Open();
var resultList = new ArrayList();
var results = db2Command.ExecuteReader();
while (results.Read())
{
var values = new object[results.FieldCount];
results.GetValues(values);
resultList.Add(values);
}
results.Close();
_DB2Connection.Close();
return resultList.ToArray();
}
You cannot send in an array as a parameter. You would have to do something to build out a list of parameters, one for each of your values.
e.g.: SELECT DISTINCT ID,COL2,COL3 FROM TABLE WHERE ID IN ( #id1, #id2, ... #idN )
And then add the values to your parameter collection:
cmd.Parameters.Add("#id1", DB2Type.Integer).Value = your_val;
Additionally, there are a few things I would do to improve your code:
Use using statements around your DB2 objects. This will automatically dispose of the objects correctly when they go out of scope. If you don't do this, eventually you will run into errors. This should be done on DB2Connection, DB2Command, DB2Transaction, and DB2Reader objects especially.
I would recommend that you wrap queries in a transaction object, even for selects. With DB2 (and my experience is with z/OS mainframe, here... it might be different for AS/400), it writes one "accounting" record (basically the work that DB2 did) for each transaction. If you don't have an explicit transaction, DB2 will create one for you, and automatically commit after every statement, which adds up to a lot of backend records that could be combined.
My personal opinion would also be to create a .NET class to hold the data that you are getting back from the database. That would make it easier to work with using IntelliSense, among other things (because you would be able to auto-complete the property name, and .NET would know the type of the object). Right now, with the array of objects, if your column order or data type changes, it may be difficult to find/debug those usages throughout your code.
I've included a version of your code that I re-wrote that has some of these changes in it:
public List<ReturnClass> GetResults(int[] ids)
{
using (var conn = new DB2Connection())
{
conn.Open();
using (var trans = conn.BeginTransaction(IsolationLevel.ReadCommitted))
using (var cmd = conn.CreateCommand())
{
cmd.Transaction = trans;
var parms = new List<string>();
var idCount = 0;
foreach (var id in ids)
{
var parm = "#id" + idCount++;
parms.Add(parm);
cmd.Parameters.Add(parm, DB2Type.Integer).Value = id;
}
cmd.CommandText = "SELECT DISTINCT ID,COL2,COL3 FROM TABLE WHERE ID IN ( " + string.Join(",", parms) + " ) ";
var resultList = new List<ReturnClass>();
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
var values = new ReturnClass();
values.Id = (int)reader["ID"];
values.Col1 = reader["COL1"].ToString();
values.Col2 = reader["COL2"].ToString();
resultList.Add(values);
}
}
return resultList;
}
}
}
public class ReturnClass
{
public int Id;
public string Col1;
public string Col2;
}
Try changing from:
db2Parameter.DB2Type = DB2Type.DynArray;
to:
db2Parameter.DB2Type = DB2Type.Integer;
This is based on the example given here

Entity Framework baseline integrity

I am working with Entity Framework database first, and I have a table that stores historical values using baseline ids. We store parent/child links on this table using the baseline id. The following columns makeup this design:-
Id int (primary key - unique)
BaselineId int (not unique, but does uniquely identity revisions of the same item)
ParentBaselineId int nullable (refers to the baseline of the linked entity, no FK)
Latest bit (indicated that this is the most recent baseline in a series)
Example data for clarity
Id BaselineId ParentBaselineId Latest
1 1 NULL 0
2 1 NULL 1
3 2 1 0
4 2 1 1
This shows two items, each with two revisions. Baseline 1 is the parent of baseline 2.
My issue is that for the reasons listed below I lookup the next available baseline in C# and manually specify the BaselineId/ParentBaselineId to be saved. When two users trigger this method at the same time, they save the same Baseline ids, as the save does not complete before the second users looks up the next available baseline id.
The method can add many items at once that must be linked together by baseline ids
This must be a single SQL transaction so it can rollback completely on error
SQL trigger cannot be used to set the baselines because they are needed ahead of time to indicate the relationships
What measures can I take to ensure that the same baseline won't be used by two users running the method at the same time?
My C# looks something like this
using (var tx = new TransactionScope())
{
using (var context = new DbContext(connectionString))
{
int baseline = context.MyTable.Max(e => e.BaselineId);
context.MyTable.Add(new MyTable() {BaselineId = baseline + 1, Latest = true});
context.MyTable.Add(new MyTable() { BaselineId = baseline + 2, ParentBaselineId = baseline + 1, Latest = true });
context.SaveChanges();
}
tx.Complete();
}
Using #Steve Greene 's suggestion, I was able to use an SQL sequence. After creating a new sequence in my database and setting the start value to match my existing data, I updated my code to the following.
public long NextBaseline(DbContext context)
{
DataTable dt = new DataTable();
var conn = context.Database.Connection;
var connectionState = conn.State;
try
{
if (connectionState != ConnectionState.Open)
conn.Open();
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = "SELECT NEXT VALUE FOR MySequence;";
using (var reader = cmd.ExecuteReader())
{
dt.Load(reader);
}
}
}
catch (Exception ex)
{
throw new HCSSException(ex.Message, ex);
}
finally
{
if (connectionState != ConnectionState.Open)
conn.Close();
}
return Convert.ToInt64(dt.AsEnumerable().First().ItemArray[0]);
}
public void Save()
{
using (var tx = new TransactionScope())
{
using (var context = new DbContext(connectionString))
{
var parent = new MyTable() { BaselineId = NextBaseline(context), Latest = true };
var child = new MyTable() { BaselineId = NextBaseline(context), ParentBaselineId = parent.BaselineId, Latest = true }
context.MyTable.Add(parent);
context.MyTable.Add(child);
context.SaveChanges();
}
tx.Complete();
}
}

c# Better way to retrieve large bulks of data from SQL Server 2014?

Context: I retrieve bulks of data to index them in an ElasticSearch cluster (it has no-sql format).
I do this by determining how many delegates do I need to create with a for loop depending of how many rows does the current analyzed table has compared to my bulk_size, and execute them to do the code below.
I use offset-fetch. tb represents the current table, and my current bulk_size is 5000.
My code looks like this:
using (SqlConnection db = new SqlConnection(tb.Conn))
{
int offset = i * BULK_SIZE;
int nextRows = BULK_SIZE;
string queryParams = string.Format(" ORDER BY {0} OFFSET {1} ROWS FETCH NEXT {2} ROWS ONLY", tb.FirstPK(), offset, nextRows);
string dataQuery = string.Format("SELECT * FROM dbo.{0} {1}", tb.TableName, queryParams);
try
{
db.Open();
object[,] bulk = new object[BULK_SIZE, 2];//[data[], id]
int n = 0;
object[] values;
using (SqlDataReader reader = new SqlCommand(dataQuery, db).ExecuteReader())
{
while (reader.Read())
{
string docId = string.Empty;
foreach (string pk in tb.PKeys)
{
docId += reader[pk].ToString() + '_';
}
docId = docId.Substring(0, docId.Length - 1);//delete last '_'
values = new object[reader.FieldCount];
reader.GetValues(values);
bulk[n, 0] = values;
bulk[n, 1] = docId;
n++;
}
reader.Close();
}
db.Close();
if (IsMemoryAvailable())//Waits for other delegates to finish
{
DataUpload(bulk, tb, mapper);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message); Console.WriteLine(e.StackTrace); Console.ReadLine();
//throw e;
}
}
Is there a better way to do this?
I get then bulk, insert it into an array and handle that bulk in another task.
The problem is that the memory of SQL Server keeps going up (cache goes too big), and in late-bulk-fetching I get timeout exceptions and such; it gets to slow.
The solution is to create a temp table with only the primary keys, and query back and forth from the table and the temp table with joins.
This is way more memory efficient than offset-fetch, and also faster.

No value given for required parameters, with value

I'm working in a c#/ASP.NET on a web app.
As part of the app, I want to connect to an ms-access database, and get values from it into a dataset.
For some reason, I get the error in the title when filling the dataset with a DataAdaptor- despite this, when I use breakpoints, I can see the command is as follows:
SELECT ..... WHERE ItemID = #value0 (if you need the parameters, ask for them and i`ll copy the whole thing).
I can also see that #value0 has the value 2 with breakpoints, and I am assured it's the only value in the query.
My question is, how could this happen? If the only value in the query is filled, what am I missing?
EDIT:
Full query:
SELECT ItemName as Name,ItemPicture as Picture,ItemHeroModif as Assistance,ItemTroopModif as Charisma, HerbCost as Herbs, GemCost as Gems FROM Item WHERE ItemID = #value0"
Full building code (Generating the query for each user requires a different amount of items, this one has only a single item, so i've used it to test):
static public DataSet getUserShopItemDS(string username,List<shopItem> items)
{
string madeForCommand = "SELECT ItemName as Name,ItemPicture as Picture,ItemHeroModif as Assistance,ItemTroopModif as Charisma, HerbCost as Herbs, GemCost as Gems FROM Item WHERE ItemID = ";
int count = 0;
foreach (shopItem item in items)
{
madeForCommand += "#value"+count+" OR ";
count++;
}
madeForCommand = madeForCommand.Substring(0, madeForCommand.Length - 3);
OleDbCommand command = GenerateConnection(madeForCommand);
for (int ii = 0; ii < items.Count; ii++)
{
command.Parameters.AddWithValue("#value" + ii, items[ii].ID);
}
var FreestyleAdaptor = new OleDbDataAdapter();
FreestyleAdaptor.SelectCommand = command;
DataSet Items = new DataSet();
FreestyleAdaptor.Fill(Items);//The error is thrown here.
return Items;
}
EDIT 2: - The shopitem class:
public class shopItem
{
//Irrelevant parameters,
public int ID // Whenever a user logs in, I create a list of his items, and attach each one it's ID from the database. I send the list as a parameter to the function.
{
get;
private set;
}
public shopItem(int ID)
{
this.ID = ID;
}
public shopItem() { }
// more constructors.
}
Here's the code you should use:
var madeForCommand = "SELECT ItemName as Name,ItemPicture as Picture,ItemHeroModif as Assistance,ItemTroopModif as Charisma, HerbCost as Herbs, GemCost as Gems FROM Item WHERE ";
OleDbCommand command = new OleDbCommand();
for (int ii = 0; ii < items.Count; ii++)
{
madeForCommand += "ItemId = ? OR ";
command.Parameters.AddWithValue("value" + ii, items[ii].ID);
}
madeForCommand = madeForCommand.Substring(0, madeForCommand.Length - 3);
In MS Access parameters are passed using ? and have to be provided in the same order as they are used in the query.

Categories