C# Script - modify to only write to certain database fields - c#

I have the following C# script that takes a configured list of elements and writes the values to our database.
using System;
using System.IO;
using System.Data.SqlClient;
public class Script
{
public static bool ExportData(DataExportArguments args)
{
try
{
var sqlStringTest = new SqlConnectionStringBuilder();
// SERVER/INSTANCE
sqlStringTest.DataSource = "SQL Server";
// DATABASE NAME
sqlStringTest.InitialCatalog = "Database";
sqlStringTest.IntegratedSecurity = false;
sqlStringTest.UserID = "user";
sqlStringTest.Password = "password";
using (var sqlConnection = new SqlConnection(sqlStringTest.ConnectionString))
{
sqlConnection.Open();
using (IExportReader dataReader = args.Data.GetTable()) //Get the main data table
{
while (dataReader.Read()) // Loop through Export data rows
{
using (var sqlCommand = new SqlCommand())
{
sqlCommand.Connection = sqlConnection;
sqlCommand.CommandText = #"INSERT INTO [dbo].[Subjects] (
SourceId,
DirectSourceUrl,
Identifier,
Name,
Category)
VALUES (
#SourceId,
#DirectSourceUrl,
#Identifier,
#Name,
#Category)";
sqlCommand.Parameters.AddWithValue("#SourceId", dataReader.GetStringValue("SourceId"));
sqlCommand.Parameters.AddWithValue("#DirectSourceUrl", dataReader.GetStringValue("DirectSourceUrl"));
sqlCommand.Parameters.AddWithValue("#Identifier", dataReader.GetStringValue("Identifier"));
sqlCommand.Parameters.AddWithValue("#Name", dataReader.GetStringValue("Name"));
sqlCommand.Parameters.AddWithValue("#Category", dataReader.GetStringValue("Category"));
sqlCommand.ExecuteNonQuery();
}
}
}
}
}
catch (Exception exp)
{
args.WriteDebug(exp.ToString(), DebugMessageType.Error);
return false;
}
return true;
}
}
The issue is that for each configuration to which I attach this script, the fields can be any combination of the above. For example, in one configuration, I may only be using the Identifier and Name fields, and in another, all 5 fields, and in yet another maybe just 3 of them. Therefore I'm having to manually remove the unused fields from the script for each separate configuration I do to avoid getting an error when running the script.
How would I modify this code so that I don't have to manually remove the fields that aren't being used? It would seem that it would be some form of IF and ELSE IF statements, but I can't think of how it would be done outside of listing every single possible combination of fields?

Assuming your IExportReader has some way to determine if a field exists, you could just do something similar to this (Assuming a method HasField, and all your fields are nullable):
sqlCommand.Parameters.AddWithValue("#SourceId",
dataReader.HasField("SourceId")
? dataReader.GetStringValue("SourceId")
: DBNull.Value);

Related

How to create Login/Register page in ASP MVC without EF

I am trying to create a register and login Page in ASP MVC without EF.
I wrote this solution but I recieve always error that "connection" and "command" does not exist in the current context :
public ActionResult Login(Client cmodel)
public string GetPassword(string email)
{
using (connection = new SqlConnection(ConfigurationManager.AppSettings["deliverycon"]))
{
using (command = new SqlCommand(#"select top 1 password from clients where email=#email", connection))
{
connection.Open();
command.Parameters.AddWithValue("#email", email);
using (reader = command.ExecuteReader())
{
reader.Read();
return reader["password"].ToString;
}
}
}
}
In C#, you must declare your variables before you assign to them, even within a using statement. You have basically done the equivalent of this:
myValue = 10;
The compiler will complain that myValue doesn't exist in the current context. The solution is to declare the variable and then assign it, which you can do with a single line of code:
int myValue = 10;
You never actually declared your variables for connection or command or reader. You need to declare them as a SqlConnection and SqlCommand and SqlDataReader, or you can use the implicit var.
This example shows both types:
public string GetPassword(string email)
{
using (SqlConnection connection = new SqlConnection(ConfigurationManager.AppSettings["deliverycon"]))
{
using (var command = new SqlCommand(#"select top 1 password from clients where email=#email", connection))
{
connection.Open();
command.Parameters.AddWithValue("#email", email);
using (var reader = command.ExecuteReader())
{
reader.Read();
return reader["password"].ToString;
}
}
}
}
var is often used when the type is clear from the context, while explicitly saying the type is often used when it's unclear.
Also, you appear to be storing your password in plaintext in your database. This is a terrible idea. Passwords should be one-way hashed and salted. It is not a good idea to roll your own security system if you don't know what you're doing.

Update if disabled = 0 in C#

I am making a project in C#- in which one can "vote".
When you run the program you first log in. After you've logged in you have to select a value out of a dropdownlist. After you've selected the teacher you press on a button which votes.
The problem is I don't really know how to let this validate properly. And to check if the person has already voted.
It has to check the column in the database named "disabled" if the value = 1 or 0. If the value = 1 they can't vote and if it's 0 they can.
When the person votes it increases the column aantalStemmen by 1. and the disabled column to 1 aswell. Which gets shown in a datagridview.
And the values in the dropdownlist has to match the 1 in the database.
I have this code:
private void db_connection()
{
try
{
conn = "Data Source=localhost;Initial Catalog=docent;Integrated Security=True";
connect = new SqlConnection(conn);
connect.Open();
}
catch (SqlException e)
{
throw;
}
}
private bool validate_disabled(string favoriet)
{
db_connection();
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "Select disabled from leerling";
cmd.Connection = connect;
SqlDataReader disabled = cmd.ExecuteReader();
if (disabled.Read())
{
connect.Close();
return true;
}
else
{
connect.Close();
return false;
}
}
private void btnStem_Click(object sender, EventArgs e)
{
string favoriet = cmbFavoriete.Text;
db_connection();
SqlCommand cmd = new SqlCommand();
bool r = validate_disabled(favoriet);
if(r){
cmd.CommandText = "UPDATE docent SET aantalStemmen = aantalStemmen + 1 where docentid=#id";
cmd.Parameters.AddWithValue("#id", cmbFavoriete.Text);
}
else
{
MessageBox.Show("You have already voted.");
}
}
my tables in my database looks like this:
Thanks in advance, I've been struggling really hard with this as I'm still a rookie in C#.
I will try an answer to cover more aspects of your code (many already mentioned in comments):
1) Declare your connection string outside of your methods. Also choose meaningful variable names - you will than yourself in a few months when you revisit the code.
private const String ConnectionStr = "Data Source=localhost;Initial Catalog=docent;Integrated Security=True";
2. Appropriate names for methods - also, try to use Camel or Pascal case for method names.
3. Pay attention to possible exceptions. SQLException is not the only possible exception when constructing or opening an SqlConnection, so it is better to catch anything that might occur
private SqlConnection createConnection
{
try
{
connect = new SqlConnection(ConnectionStr);
connect.Open();
}
// this is laziness, but it is better than before
catch (Exception e)
{
// best to log the real error somewhere
throw;
}
}
4. Dispose connection and other disposables like SqlCommand. Also var might save some typing (just hover your mouse over the keyword and you will see the actual type).
SqlConnection allows to directly create a command to be executed using that particular connection by using CreateCommand.
Since your are expecting a single value (scalar) (or a single row with a single column), you can use ExecuteScalar method. So, no more reader.
private bool isDisabled(string favoriet)
{
using (var connection = createConnection())
{
using (var cmd = new connection.CreateCommand())
{
cmd.CommandText = "Select disabled from leerling where leerlingnummer = #number";
cmd.Parameters.AddWithValue("#number", favoriet);
// for simplicity I have assumed that it will always find a value. This should be checked
var disabled = Convert.ToBoolean(cmd.ExecuteScalar());
return disabled;
}
}
}
5. Try not to mix UI logic with database logic (they are usually put in different assemblies)
private void castVote(String favoriete)
{
using (var connection = createConnection())
{
using (var cmd = new connection.CreateCommand())
{
cmd.CommandText = "UPDATE docent SET aantalStemmen = aantalStemmen + 1 where docentid = #id";
cmd.Parameters.AddWithValue("#id", cmbFavoriete.Text);
// command must be actually executed, otherwise nothing happens
cmd.ExecuteNonQuery();
}
}
}
private void btnStem_Click(object sender, EventArgs e)
{
string favoriet = cmbFavoriete.Text;
bool r = isDisabled(favoriet);
if (r)
castVote(favoriet);
// maybe, it would make sense to also notify the user that the vote has been cast
else
MessageBox.Show("You have already voted.");
}
6. Use EntityFramework - in order to avoid the troubles related to handling commands and readers, you can use an ORM to do the dirty work for you.
I would suggest you use bit Data Type (0 - false, 1 - true) instead of int Data Type in your table. It does exactly what you need and you don't have to use int for this.
This means you could change your validate_disabled method to use something like this:
cmd.CommandText = "SELECT disabled FROM leerling WHERE disabled = 1 AND leerlingnummer = #favoriet";
cmd.Parameters.AddWithValue("#favoriet", favoriet);
I've assumed string favoriet is equal to leerlingnummer in your table. After you've executed that query, you would simply check if the query contains more than 0 records - if more than 0 records that means the person does not have permission to vote.

Using C# & MySQL

I have been able to interface MySQL with my C# WinForms application with the help of wonderful guides all over the Internet, but I am confused as to which "method" is the standard way of doing so, if ever it does exist. To elaborate, I'll first describe the application that I am developing.
C# Application Overview
My application accepts input from the user, uses DataGridView as the main control for CRUD, and generates Excel reports. Basically, it does the below:
Accept input and insert said data into the database
Retrieve records and display them via DataGridView
Update records using the CellValueChanged event
Delete records
Generate reports using Crystal Reports
Using Objects
What I currently do is to store and retrieve data as objects and use those for all of the above operations. For example:
public class Cat
{
private int id;
public int Id
{
get { return id; }
set { id = value; }
}
private string breed;
public string Breed
{
get { return breed; }
set { breed = value; }
}
public Cat(int id, string breed)
{
this.id = id;
this.breed = breed;
}
}
For me to retrieve data, I do:
public void FillCats()
{
cats = new List<Cat>();
conn.Open();
string query = "SELECT * from cat;";
MySqlCommand cmd = new MySqlCommand(query, conn);
MySqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
Cat cat = new Cat(
Convert.ToInt32(reader[0]),
reader[1].ToString(),
);
cats.Add(cat);
}
reader.Close();
conn.Close();
}
Likewise, my create, update, and delete operations are simply variants of the below create function:
public void Insert(DatabaseDriver db)
{
string insert = #"
INSERT INTO cat(id, breed)
VALUES(?id, ?breed);";
db.open();
db.setQuery(insert);
db.setParameter("id", this.id);
db.setParameter("breed", this.breed);
db.executeNonQuery();
db.close();
}
Am I doing this right?
It does work, of course, but I was wondering whether there is a less time-consuming method, perhaps similar to how creating a DBML file would automatically connect your application to your SQL Server database, and even automagically associate every attribute with its corresponding primary key. Before anyone asks, unfortunately, it is not an option for me to use SQL Server.
Check out this answer for a much less-involved way of doing it: Fill Datagridview with MySQL data
As a side note, you can simply call reader.GetInt32(0) and reader.GetString(1) instead of Convert.ToInt32(reader[0]) and reader[1].ToString(). That way you won't get a NullReferenceException when breed is null.

EF6 Code First drop tables (not entire database) when model changes

I'm doing Code First development with Entity Framework 6, using Database Migrations, and I'm using a new database that is populated with sample seed data. I'd like to be able to initialize my database with that seed data any time I change the model.
The catch is this: I don't have database create permissions; because of this, I can't just utilize DropCreateDatabaseIfModelChanges.
Is there a way that I can programmatically drop all of my tables, or am I stuck manually deleting them from the database each time?
Ultimately, I didn't need to delete the tables, just the data they contained.
I ended up solving this by simply truncating a list of tables at the beginning of my Seed method, based on this answer.
protected override void Seed(MyContext context)
{
var listOfTables = new List<string> { "Table1", "Table2", "Table3" };
foreach (var tableName in listOfTables)
{
context.Database.ExecuteSqlCommand("TRUNCATE TABLE [" + tableName + "]");
}
context.SaveChanges();
// seed data below
}
If you're not using automatic migrations, but code based migrations, you can back all the way down to the first version using the follow command:
Update-Database –TargetMigration: 0
This will follow the Down path on all of your migrations until you have a clean database. Then you can execute:
Update-Database
This will bring everything back up to date. This solution assumes you've properly maintained your Down path and seeded your data with Migrations. I do this for my integration tests to ensure I start with the data in an expected state.
My suggestion is to use the local DB or another DB you have full permission on (Azure is nice, and free if you have a MSDN account). Then migrate that final DB schema over once it's set in stone and ready for production.
That being said, this might be helpful, but I've never tried it before.
If you dont have permission access to the database, it may be better to address that issue.
Anyway:
public bool TruncateTable(string connectionString, string schema, string tableName) {
var statement = "TRUNCATE TABLE [{0}].[{1}] ;";
statement = string.Format(statement, schema, tableName);
return ExecuteSqlStatement(connectionString, statement);
}
public bool DeleteAllEntriesTable(string connectionString, string schema, string tableName) {
var statement = "DELETE FROM [{0}].[{1}] ;";
statement = string.Format(statement, schema, tableName);
return ExecuteSqlStatement(connectionString, statement);
}
public bool ExecuteSqlStatement(string connectionString, string statement, bool suppressErrors = false) {
int rowsAffected;
using (var sqlConnection = new SqlConnection(connectionString)) {
using (var sqlCommand = new SqlCommand(statement, sqlConnection)) {
try {
sqlConnection.Open();
rowsAffected = sqlCommand.ExecuteNonQuery(); // potential use
}
catch (Exception ex) {
if (!suppressErrors) {
// YOUR ERROR HANDLER HERE
}
return false;
}
}
}
return true;

Insert byte[] to blob column in Informix DB using c#

Just like these links
Link 1
Link 2
Link 3
Link 4
Am also unable to insert only byte[] related operations on my informix database. I tried many ways and gone through IBM site. but no where its explained "how to use byte[] to insert into blob column using c#".
"LINK 4" is very helpful. but am facing problem with this code.
Error: The %0 enumeration value, %1, is invalid.
At line: blob.Open(IfxSmartLOBOpenMode.ReadWrite);
if i use cmd.Parameters.Add(new IfxParameter()).Value = byteuploaded`;
Here is my code snippet.
protected void uploadfile_Click(object sender, EventArgs e)
{
string extension;
// checks if file exists
if (!_imageUpload.HasFile)
{
_resultLbl.Text = "Please, Select a File!";
return;
}
// checks file extension
extension = System.IO.Path.GetExtension(_imageUpload.FileName).ToLower();
if (!extension.Equals(".jpg") && !extension.Equals(".jpeg") && !extension.Equals(".png"))
{
_resultLbl.Text = "Only image files (.JPGs and .PNGs) are allowed.";
return;
}
try
{
// ========= This is not working ==============
string sqlQuery = "insert into db95:TestBlobUpload (id ,fileblob) values('2', 'two');";
// ========= This is working properly ==============
//string sqlQuery = "insert into db95:TestBlobUpload (id ,filetext) values('4',?);";
string connString = "Database=db95;Host=172.16.XX.XX;Server=vsXXXX;Service=88;Protocol=onsoctcp;UID=ed;Password=ca94;";
using (this.connection = new IfxConnection(connString))
{
this.connection.Open();
using (this.cmd = new IfxCommand(sqlQuery, this.connection))
{
// Start a local transaction
this.trans = this.connection.BeginTransaction(IsolationLevel.Unspecified);
// Assign transaction object for a pending local transaction
this.cmd.Transaction = trans;
try
{
IfxBlob byteuploaded = new IfxBlob(this.connection);
byteuploaded.Read(_imageUpload.FileBytes);
// ========= BOTH OF THESE are not working ==============
//cmd.Parameters.Add(new IfxParameter()).Value = data;// System.Text.Encoding.UTF8.GetString(data);
cmd.Parameters.Add(new IfxParameter()).Value = byteuploaded;// _imageUpload.FileBytes;
int res = cmd.ExecuteNonQuery();
// commiting the transaction
this.cmd.Transaction.Commit();
}
catch
{
//this.cmd.Transaction.Rollback();
}
}
this.connection.Close();
}
}
catch (Exception)
{
}
}
am using this dll as a reference and using IBM.Data.Informix;
particularly am unable to add byte[] to blob columns. All other insert/update/delete operations i can do.
Any help?
I even upgraded to ibm_data_server_driver_package_win64_v10.1.exe & clientsdk.4.10.FC1DE.WIN.exe
But am facing problems with dll compatibility. unable to load'XX.XX.dll" exception is comin.
I even tried to execute the insert query using
INSERT INTO db95#vsXXXX:testblobupload (fileblob)
VALUES (db95#vsXXXX:FILETOBLOB('C:\tmp\Untitled.png', 'client'));
and facing error as
ERROR: Smart-large-object error.
Error Code: -9810.
Smart Large Objects: No sbspace number specified.
This is not your c# app. It's the Informix environment needs to be setup for large smart objects. I think it basically specifies space to use on server. I don't know anything else. If you return the isam error code it would be this
-12053 Smart Large Objects: No sbspace number specified.
No default sbspace was f ound, and the caller has not specified an sbspace
to use.
Either specify the smart-large-object space name in the
smart-large-object function call or set the SBSPACENAME onconfig
file parameter to the name of a valid smart-large-object space.
We can include blob fields in specific sbspace using the PUT IN clause.
If we use connection.GetIfxBlob() without specifying the table name and column name, the blob field will be included in the default sbspace set in the onconfig SBSPACENAME, and if not set will give the error SQLERR-9810 ISAM ERR-12053.
I guess, that the best way of save Informix Blob is using this function :
string insertSql = string.Format("insert into \"{0}\" (sbfotoint,NmArqBlob) values (?,?);", this.table);
using (var command = new IfxCommand(insertSql, this.connection))
{
this.connection.Open();
SetRole();
command.CommandType = System.Data.CommandType.Text;
command.Parameters.Add(new IfxParameter()).Value = CreateIfxBlob(entidade.SBlob);
command.Parameters.Add(new IfxParameter()).Value = entidade.NomeArquivo;
command.ExecuteNonQuery();
this.connection.Close();
}
private IfxBlob CreateIfxBlob(byte[] data)
{
IfxBlob blob = connection.GetIfxBlob(this.table, "sbfotoint");
blob.Open(IfxSmartLOBOpenMode.ReadWrite);
blob.Write(data);
blob.Close();
return blob;
}

Categories