I am using C# to create a SQL Server view, then open an access database and link the table into access. The create view statement, open database statement and link statement work great BUT the catch here is it will always link the table as read-only. What piece o'code do I need to add or update current so that the view is not always linked as read-only?
string MasterDatabase = "R:\\Testing\\MasterDatabase.mdb";
DAO.Database dd;
DAO.DBEngine db = new DAO.DBEngine();
DAO.TableDef tdf9;
bool found = false;
DAO.TableDef tdf1;
string Table = "ServiceEntranceLog";
string TableAccess = "Service_Entrance_Log";
using (var connection = new SqlConnection(ConnectionStringHere))
using (var command = connection.CreateCommand())
{
using (var command4 = connection.CreateCommand())
{
command4.CommandText = "CREATE VIEW HelperView" AS SELECT * FROM monster.ServiceEntranceLog";
command4.ExecuteNonQuery();
}
}
if (_combobox1.SelectedItems.Contains("MasterDatabase"))
{
dd = db.OpenDatabase(CRDB);
try
{
string[] tableNames = new string[1] { TableAccess };
for (int q = tableNames.GetLowerBound(0); q <= tableNames.GetUpperBound(0); q++)
{
foreach (DAO.TableDef tabledef in dd.TableDefs)
{
string name = tableNames[q];
if (tabledef.Name == name) { found = true; }
try { if (found) { dd.TableDefs.Delete(name); } }
catch { }
}
}
}
catch { }
tdf1 = dd.CreateTableDef(TableAccess);
tdf1.Connect = connectionString;
tdf1.SourceTableName = Table;
dd.TableDefs.Append(tdf1);
}
Alritey, so it seems the issue is I needed to define a primary key when linking in the table into access so that the table would be updateable. Using this syntax does the trick
dd.Execute "CREATE UNIQUE INDEX SomeIndex ON SomeTable (PrimaryKeyColumn) WITH PRIMARY"
Related
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);
I need to modify the UpgradeCode property of the Upgrade MSI table via C#.
This code works ok with other tables' properties, but throws an error when I'm trying to modify these.
using (var database = new Database(TEMPDATABASE, DatabaseOpenMode.Direct))
{
string upgradeCode = Guid.NewGuid().ToString("B").ToUpper();
database.Execute("Update `Upgrade` Set `Upgrade`.`UpgradeCode` = '{0}'", upgradeCode);
}
The error is:
Microsoft.Deployment.WindowsInstaller.InstallerException: 'Function failed during execution.'
I got curious and pillaged github.com - and it giveth the following: Full project - just download it as a whole.
The actual code was (some unicode line feed issues in the file on github.com, I have fixed them up here):
public static void UpdateUpgradeTable(this Database db, Guid upgradeCode)
{
using (View view = db.OpenView("SELECT * FROM `Upgrade`", new object[0]))
{
view.Execute();
using (Record record = view.Fetch())
{
record[1] = upgradeCode.ToString("B").ToUpperInvariant();
view.Replace(record);
}
db.Commit();
}
}
I took the above and made the following mock-up (very ugly, but it worked):
using (Database db = new Database(#"C:\Test.msi", DatabaseOpenMode.Direct))
{
using (View view = db.OpenView("SELECT * FROM `Upgrade`", new object[0]))
{
view.Execute();
using (Record record = view.Fetch())
{
record[1] = "{777888DD-1111-1111-1111-222222222222}";
record[2] = "";
record[3] = "4.0.1";
record[4] = "";
record[5] = "1";
record[6] = "";
record[7] = "WIX_UPGRADE_DETECTED";
view.Replace(record);
}
db.Commit();
using (Record record = view.Fetch())
{
record[1] = "{777888DD-1111-1111-1111-222222222222}";
record[2] = "";
record[3] = "";
record[4] = "4.0.1";
record[5] = "1";
record[6] = "";
record[7] = "WIX_DOWNGRADE_DETECTED";
view.Replace(record);
}
db.Commit();
}
}
The SDK doc says:
UPDATE queries only work on nonprimary key columns.
UpgradeCode is the primary key for the Upgrade table.
I am apparently Inserting data using LINQ by creating classes of tables in the databases but it just has error that says object is null.
This is my sample code using C# LINQ:
using (dc = new linqDBDataContext(conn))
{
Subject_Curriculum sc;
Subject_Schedule ss;
Subject_Department sd;
Subject_Standing sst;
Pre_Requisite pr;
Pre_Requisite_Year_Standing prys;
Curriculum cu = new Curriculum();
cu.Curriculum_Title = curriculumName;
cu.Course_Number = courseNumber;
foreach (var s in ssd)
{
sc = new Subject_Curriculum();
sc.Course_Code = s.courseCode;
sc.Course_Title = s.courseTitle;
cu.Subject_Curriculums.Add(sc);
dc.Subject_Curriculums.InsertOnSubmit(sc);
for (int i = 0; i < s.numberOfSchedules; i++)
{
ss = new Subject_Schedule();
if (i == 0)
{
ss.Units = s.unitsLec;
ss.Schedule_Type = "Lecture";
ss.Number_Of_Hours = s.numberOfHoursLec;
}
else
{
ss.Units = s.unitsLab;
ss.Schedule_Type = "Laboratory";
ss.Number_Of_Hours = s.numberOfHoursLab;
}
sc.Subject_Schedules.Add(ss);
dc.Subject_Schedules.InsertOnSubmit(ss);
}
foreach (var sdl in s.department)
{
sd = new Subject_Department();
sd.Department_Number = sdl;
sc.Subject_Departments.Add(sd);
dc.Subject_Departments.InsertOnSubmit(sd);
}
sst = new Subject_Standing();
sst.Year = s.year;
sst.Semester = s.semester;
cu.Subject_Standings.Add(sst);
dc.Subject_Standings.InsertOnSubmit(sst);
if (s.yearStandingStatus)
{
prys = new Pre_Requisite_Year_Standing();
prys.Year_Standing = Convert.ToInt32(s.yearStanding.ToString().Substring(0, 1));
sc.Pre_Requisite_Year_Standings.Add(prys);
dc.Pre_Requisite_Year_Standings.InsertOnSubmit(prys);
}
else
{
if (s.prereq.Count == 0)
{
pr = new Pre_Requisite();
pr.Pre_Requisite_Code = null;
sc.Pre_Requisites.Add(pr);
dc.Pre_Requisites.InsertOnSubmit(pr);
}
else
{
foreach (var p in s.prereq)
{
pr = new Pre_Requisite();
pr.Pre_Requisite_Code = Convert.ToInt32(p);
sc.Pre_Requisites.Add(pr);
dc.Pre_Requisites.InsertOnSubmit(pr);
}
}
}
}
dc.Curriculums.InsertOnSubmit(cu);
dc.SubmitChanges();
return true;
}
As you can see in the code, the Curriculum table has the highest hierarchy in the database and the other tables inherits its primary key into Subject_Curriculum, Pre_Requisite, Subject_Standing and Pre_Requisite_Year_Standing. While Subject_Schedules and Subject_Department inherits Subject_Curriculum's primary key. What can I do to make this insertion possible to all table at once?
I already solved my question. It is just by adding all tables from their foreign keys and insert and submit changes at the end of the loop. This makes this thread close.
I am able to do SqlBulkCopy when there is no dependency on the table with the code below:
//TableData and FieldData have column info in memory.
using (SqlConnection connection = new SqlConnection(#"Data Source=WLO1;Initial Catalog=GenTest2;Integrated Security=True"))
{
connection.Open();
foreach (string tableName in tablesInOrderToProcess)
{
if (tableDataList.ContainsKey(tableName))
{
TableData td = tableDataList[tableName];
SqlBulkCopy copy = new SqlBulkCopy(connection);
copy.BatchSize = 10000;
copy.DestinationTableName = tableName;
System.Data.DataTable dt = new DataTable();
foreach (FieldData fd in td.FieldList.Values)
{
string fieldName = fd.Name;
string fieldDataType = fd.DataType;
string fieldSize = fd.Size.ToString(); ;
string fieldConstant = fd.constantValue;
string fieldAverage = fd.averageSize;
string fieldPickList = fd.pickList;
copy.ColumnMappings.Add(fieldName, fieldName);
switch (fieldDataType)
{
case "char":
{
dt.Columns.Add(fieldName, System.Type.GetType("System.String"));
}
break;
case "nvarchar":
{
dt.Columns.Add(fieldName, System.Type.GetType("System.String"));
}
break;
case "number":
{
if (fd.Size == 10)
dt.Columns.Add(fieldName, System.Type.GetType("System.Int64"));
else
dt.Columns.Add(fieldName, System.Type.GetType("System.Int32"));
}
break;
default:
{
dt.Columns.Add(fieldName);
}
break;
}
}
for (int i = 0; i < int.Parse(td.NumRows); i++)
{
System.Data.DataRow r = dt.NewRow();
foreach (FieldData fd in td.FieldList.Values)
{
string fieldName = fd.Name;
string fieldDataType = fd.DataType;
string fieldSize = fd.Size.ToString(); ;
string fieldConstant = fd.constantValue;
string fieldAverage = fd.averageSize;
string fieldPickList = fd.pickList;
switch (fieldDataType)
{
case "char":
{
if (fd.averageSize.Length > 0)
{
r[fieldName] = GetRandomString(Int32.Parse(fd.averageSize), true);
}
else
{
r[fieldName] = fieldConstant;
}
}
break;
case "nvarchar":
{
if (fd.averageSize.Length > 0)
{
r[fieldName] = GetRandomString(Int32.Parse(fd.averageSize), true);
}
else
{
r[fieldName] = fieldConstant;
}
}
break;
case "number":
{
if (fd.Size == 10)
{
r[fieldName] = i;
}
else
{
r[fieldName] = i;
}
}
break;
default:
{
r[fieldName] = fieldConstant;
}
break;
}
}
dt.Rows.Add(r);
}
try
{
copy.WriteToServer(dt);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
However, when there is a dependency, I am confused.
1)Should I create a DataTable for each table?
2)Since the table relationship info is provided by user and loaded into memory, how can I tie tables without reading information from the schema? or Do I have to read the info and get the primary key from Schema?
Thanks in advance. I hope I made it clear.
In situations like this the only way it can be done is you do your bulk copy operations to temporary tables with no key linking the two objects (and enough metadata to build the links later).
Say you have 3 tables: #item_unit_staging, #shop_order_staging, and #shop_order_operation_staging. You would create them with the same schema as your item_unit, shop_order, and shop_order_operation with auto-generated primary keys. You also would want to add whatever data you used to link the data in memory on your local machine as a "metadata column", lets say we used a extra column called metadata_id on all three tables and a column called parent_id on #shop_order_staging and #shop_order_operation_staging.
We then bulk insert in to our 3 tables, having our primary key columns filled and our foreign key columns left NULL. Once we have the data on the server we use the metadata columns to populate the foreign key columns.
update #shop_order_staging
set parent_item_unit_id = #item_unit_staging.item_unit_id
from #shop_order_staging
inner join #item_unit_staging on #shop_order_staging.parent_id = #item_unit_staging.metadata_id
update #shop_order_operation_staging
set parent_shop_order_id = #shop_order_staging.shop_order_id
from #shop_order_operation_staging
inner join #shop_order_staging on #shop_order_operation_staging.parent_id = #shop_order_staging.metadata_id
You then can copy the data from the temporary tables in to the real tables
insert into item_unit
select item_unit_id
, /*all other columns except [metadata_id]*/
from #item_unit_staging
insert into shop_order
select shop_order_id
, parent_item_unit_id
, /*all other columns except metadata_id and parent_id*/
from #shop_order_staging
insert into shop_order_operation
select shop_order_operation_id
, parent_shop_order_id
, /*all other columns except metadata_id and parent_id*/
from #shop_order_operation_staging
How to check where table is created in db database or not.
var folder = Environment.GetFolderPath (Environment.SpecialFolder.Personal);
SQLiteConnection db = new SQLiteConnection (System.IO.Path.Combine (folder,"note.db"));
try{
var existTable = db.Query<TransationTable>("SELECT count(*) FROM sqlite_master WHERE type = 'Table' AND name = 'TransationTable' ");
Console.WriteLine ("Count {0}",existTable.Count);
if(existTable.Count == 0){
tableview.Hidden = true;
lbl_NotFound.Hidden = false;
}
else{
tableview.Hidden = false;
lbl_NotFound.Hidden = true;
}
}
catch{
Console.WriteLine ("Calling Excpetion!");
}
}
Its always gives me of count 1.
#thanks in advance.
var info = conn.GetTableInfo(tableName);
if (!info.Any())
{
conn.CreateTable<T>();
}
why do you need count(), of course even if it exist, the value must be 1,
my suggestion is
SELECT name FROM sqlite_master WHERE type='table' AND name='your table name';
table with low t by the way ;)
To expand upon Jasons point. A better more generic way would be:
string tableName = typeof(Customer).Name;
var customAttributes = typeof(Customer).GetCustomAttributes(typeof(SQLite.Net.Attributes.TableAttribute),false);
if (customAttributes.Count() > 0)
{
tableName = (customAttributes.First() as SQLite.Net.Attributes.TableAttribute).Name;
}
var info = database.Connection.GetTableInfo(tableName);
if (!info.Any())
{
//do stuff
}
public MainPage()
{
InitializeComponent();
conn = DependencyService.Get<ISQLite>().GetConnection();
try
{
//Student is table name ,replace student with your table name
var existTable = conn.Query<Student>("SELECT name FROM sqlite_master WHERE type='table' AND name='Student'; ");
if ((existTable.Count > 0))
{
//Write code if table exists
}
}
catch (Exception Ex)
{
}
}