I try to identify in C# ,a DB Access with thousands of queries, only the queries with fictitious parameters,
(eg. Select id, Nation, Name from someTable where Nation = [Give a Nation]).
I tried with GetOleDbSchemaTable but with no luck.
I also tried to use MSysQueries and MSysObjects from Access and then retrieve the info in C # but with no luck.
How can you help me?
You have to ref the interop - this one:
using System.Data.OleDb
Imports Microsoft.Office.Interop.Access.Dao
public void ShowParms()
{
var strPath = #"C:\Users\AlbertKallal\source\repos\DesktopFun\test44.accdb";
Database db;
DBEngine dbACE = new DBEngine();
db = dbACE.OpenDatabase(strPath);
QueryDef query;
Parameter qParm;
foreach (var query in db.QueryDefs)
{
if (Strings.Left(query.Name, 1) != "~")
{
Debug.Print("Query name = " + query.Name);
// parmaters
foreach (var qParm in query.Parameters)
Debug.Print("query parmater name = " + qParm.Name + " type = " + qParm.Type);
}
}
}
the above will list out each query - and if it has parameters - then it will list out those.
Related
I would like to do the same thing as oracle sqldeveloper do with parametered queries (but from c#, not java).
Let's say there's an arbitrary, user supplied query, eg
select * from dual where 1 = :parameter
My task is to parse safely similar strings, identify the parameters, ask them from user and execute the query.
Which is the right / safe approach? I guess, there's some oracle client api to do this. Or is the right way using some pl/sql stuff (eg. from DBMS_SQL)? I couldn't find such a thing yet...
Update / clarification: see the example code below:
// user enters the query string with parameters somehow:
string sql = AskUserForSelectString();
// now the value of sql is:
// "select column0 from tablename where column1 = :param1 and column2 = :param2 ;"
// this is my original question: HOW TO DO THIS?
List<string> param_names = OracleParseQueryAndGiveMyParameters(sql);
// param_names is now a list of ":param1",":param2"
// ask user again for parameter values:
var param_values = new List<string>();
foreach (var param_name in param_names)
{
string param_value = AskUserForParameterValue(param_name);
param_values.Add(param_value);
}
// give the parameter values for the query in safe way:
using (var cmd = new SqlCommand(sql, myDbConnection))
{
for (int i=0; i< param_names.Count; i++)
cmd.Parameters.AddWithValue(param_names[i], param_values[i]);
var result = cmd.ExecuteReader();
// process result...
}
The key point is that I don't know the parameters in advance. This is exactly what SqlDeveloper can do.
(That isn't an issue if EF expects # before the parameter name instead of colon, that can be worked out easily.)
You can do it like this:
var sql = "INSERT INTO myTable (myField1, myField2) " +
"VALUES (#someValue, #someOtherValue);";
using (var cmd = new SqlCommand(sql, myDbConnection))
{
cmd.Parameters.AddWithValue("#someValue", someVariable);
cmd.Parameters.AddWithValue("#someOtherValue", someTextBox.Text);
cmd.ExecuteNonQuery();
}
What you absolutly must NOT do is:
var sql = "INSERT INTO myTable (myField1, myField2) " +
"VALUES ('" + someVariable + "', '" + someTextBox.Text + "');";
var cmd = new SqlCommand(sql, myDbConnection);
cmd.ExecuteNonQuery();
The problem with the second example is that it opens your code to an SQL Injection attack.
One (hacky but accurate?!) way with the original ":parameter" bind variable syntax is to call out to C and use Oracle OCI functions to do the parsing for you.
Prepare the statement with OCIStmtPrepare2() and then call
OCIStmtGetBindInfo() to get the variable names.
I'm using a Database I was able to get all the data from the database and save it in a List<>. I made changes in the List<> using a DataGrid and now I want to replace each Database value for the List<> values WHERE the List.ID == Database ID. I use Dapper (In case it matters);
public void SetData(List<DatabaseInfoModel> database)
{
using (IDbConnection connection = new System.Data.SqlClient.SqlConnection("Server=.\\SQLEXPRESS; Database=XXXX; User Id=XXXX; Password=password;"))
{
foreach(DatabaseInfoModel item in database)
{
connection.Execute($"UPDATE DataTable " +
$"SET Name = {item.Name}, " +
$" Description = {item.Description}, " +
$" Record = {item.Record} " +
$" WHERE ID = {item.ID}");
}
}
}
you can pass a model, ex
.Execute("update mydogs set age=#Age where id=#Id",dog);
"dog" is your model
UPDATE DataTable
SET
Name = REPLACE(Name, item.Name, 'Replacement Value')
.
.
WHERE ID = {item.ID}
Is this what you are looking for ?
In case you are looking for the syntax, here is more information:
https://learn.microsoft.com/en-us/sql/t-sql/functions/replace-transact-sql?view=sql-server-ver15
I am in a application where Parameterized Sql queries are not written. Below is code block
public List<MyClass> GetData(int Id, IEnumerable<string> state)
{
using (var dataContext = new DataContext(_connectionString))
{
var query = new StringBuilder("SELECT * FROM table");
query.Append(" Id = ");
query.Append(Id);
query.Append(" AND state IN ('");
query.Append(string.Join("','", state));
query.Append("')");
return dataContext.ExecuteQuery<MyClass>(query.ToString()).ToList();
}
I am refactoring code using parameterized query like this :
public List<MyClass> GetData(int Id, IEnumerable<string> state)
{
using (var dataContext = new DataContext(_connectionString))
{
var statestring = new StringBuilder("'");
statestring.Append(string.Join("','", state));
statestring.Append("'");
string myStates= statestring.ToString();
string query = "SELECT * FROM table WHERE Id ={0} AND state IN ({1})";
return dataContext.ExecuteQuery<MyClass>(query, new object[] {Id, myStates}).ToList();
}
}
I get no data on running this query. On debugging i found my query is getting formed like this
SELECT * FROM table WHERE Id ={0} AND state IN ({1}) where in ({1})
For state I see data as "'error',' warning'".
In sql server I run query like this
SELECT * FROM table WHERE Id =34 AND state IN ('error','warning').
Do i need to remove " " around mystate? I tried removing " using trim method and assigning it back to string but it didn't work. I can still see double quotes.
myStates = myStates.trim('"');
How can parameterize my query better without using any string builder for the same
Alternative suggestion: dapper...
int x = ...;
int[] y = ...
var items = connection.Query<MyClass>(
"SELECT * FROM table WHERE X = #x AND Y in #y", new {x,y}).AsList();
Dapper will deal with this for you, using an appropriate query for 0, 1, or many items, including (optional configuration options) padding the parameters to avoid query plan saturation (so when you have large lists, you use the same query and query-plan for 47 items, 48 items and 49 items, but possibly a different query for 50), and using string_split if it is supported on your server.
To parameterize the in clause every case has to be an individual parameters. So the in clause has to reflect that.
See this similar question: How to pass sqlparameter to IN()?
public List<MyClass> GetData(int Id, IEnumerable<string> state)
{
using (var dataContext = new DataContext(_connectionString))
{
var stateParameterNumbers = Enumerable.Range(1, state.Count())
.Select(i => string.Format("{{{0}}}", i));
var stateParameterString = string.Join(",", stateParameterNumbers);
string query = "SELECT * FROM table WHERE Id ={0} AND state IN (" + stateParameterString + ")";
return dataContext.ExecuteQuery<MyClass>(query, new object[] { Id }.Concat(state).ToArray()).ToList();
}
}
I think that you should change the way you pass the parameters:
return dataContext.ExecuteQuery<MyClass>(query, Id, stateString).ToList();
For reference please have a look at the signature of this method, which can be found here.
I'm looking for a simple way to store any data objects in SQL server without defining a table first.
Think about this pseudo code creating IEnumerable of anomyous type (LINQ):
var result = from item in items select new { item.First, item.Last, Age = 42 };
I'm looking for a simple solution, a function call like this:
// StoreResultInNewTable(database/context, tablename, result);
I'm aware of EF6 and code first, but I don't want to define an explicit type (class). And I don't need the other parts of entity framework like caching data or detailed tracking database layout. If table already exists and object cannot be stored in there, raise error. Otherwise (create table) and insert data.
Data inserts should not be too slow (SqlBulkCopy / BulkInsert).
edit: I really look for a solution where the result set is stored as plain database table in SQL server which means having a property of .NET type string stored as (n)varchar, decimal as money and so on. Column names in database should be 1:1 to property names. I'm flexible regarding details, but should be similar to EF6 mapping in effect (without explicitly defined types).
No key-value store, no storage of serialized objects, no NoSQL, no flat files.
edit 2: To make this more clear I give details about types in my example:
class Person
{
public string First {get; set;}
public string Last {get; set;}
}
IEnumerable<Person> items = ...
This means result is some IEnumerable<TypeWithoutName>. The compiler is the only one known the name TypeWithoutName but I can use it in a type-safe way, e.g. via LINQ. And I'm quite sure this type could be inspected by reflection.
As mentioned in comments: I'm looking for an ORM that takes the anonymous type in result and builds some create table statement with two nvarchar columns and one integer column (and corresponding inserts).
As I couldn't find an existing solution to my question, I hacked some code:
internal static void StoreEntitiesToDatabase<T>(this IEnumerable<T> elements, SqlConnection connection,
string tablename)
{
var sbc = new SqlBulkCopy(connection);
{
var table = new DataTable();
Type listType = typeof (T);
foreach (PropertyInfo propertyInfo in listType.GetProperties())
{
table.Columns.Add(propertyInfo.Name, propertyInfo.PropertyType);
sbc.ColumnMappings.Add(propertyInfo.Name, propertyInfo.Name);
}
foreach (T value in elements)
{
DataRow dr = table.NewRow();
foreach (PropertyInfo propertyInfo in listType.GetProperties())
{
dr[propertyInfo.Name] = propertyInfo.GetValue(value, null);
}
table.Rows.Add(dr);
}
string sqlsc = "CREATE TABLE " + tablename + "(";
for (int i = 0; i < table.Columns.Count; i++)
{
sqlsc += "[" + table.Columns[i].ColumnName + "] ";
int maxlen = table.Columns[i].MaxLength;
if (maxlen == -1) maxlen = 255;
if (table.Columns[i].DataType.ToString().Contains("System.Int32"))
sqlsc += " int ";
else if (table.Columns[i].DataType.ToString().Contains("System.Int64"))
sqlsc += " bigint ";
else if (table.Columns[i].DataType.ToString().Contains("System.DateTime"))
sqlsc += " datetime ";
else if (table.Columns[i].DataType.ToString().Contains("System.String"))
sqlsc += " nvarchar(" + maxlen + ") ";
else if (table.Columns[i].DataType.ToString().Contains("System.Double"))
sqlsc += " float ";
else if (table.Columns[i].DataType.ToString().Contains("System.Decimal"))
sqlsc += " money ";
else
throw new Exception("no mapping for " + table.Columns[i].DataType);
if (table.Columns[i].AutoIncrement)
sqlsc += " IDENTITY(" + table.Columns[i].AutoIncrementSeed.ToString() + "," +
table.Columns[i].AutoIncrementStep.ToString() + ") ";
if (!table.Columns[i].AllowDBNull)
sqlsc += " NOT NULL ";
sqlsc += ",";
}
sqlsc = sqlsc.Substring(0, sqlsc.Length - 1) + ")";
SqlCommand cmd = new SqlCommand(sqlsc, connection);
cmd.ExecuteNonQuery();
sbc.DestinationTableName = tablename;
sbc.WriteToServer(table);
}
}
Can be called like this:
using (var conn = new SqlConnection(connectionString))
{
conn.Open();
elems.StoreEntitiesToDatabase(conn, "myTable");
conn.Close();
}
The code above works just fine for my problem and supports elements of anonymous type.
Sidenode: I first tried to "trick" EF6 using this:
internal class DbQuickInsert<T> : DbContext where T : class
{
public DbSet<T> MyRecords { get; set; }
public DbQuickInsert(string databasename) : base(databasename)
{
}
}
internal static class HelperQuick
{
public static void InsertIntoDatabase<T>(this IEnumerable<T> records, string databasename) where T : class
{
var qi = new DbQuickInsert<T>(databasename);
qi.Configuration.AutoDetectChangesEnabled = false;
qi.BulkInsert(records);
qi.SaveChanges();
}
}
The latter code compiles but raises a runtime error because Entity Frameworkcannot handle anonymous types.
it sounds like you're hoping to use an RDBMs for exactly what it wasn't designed for. Look into a NoSQL solution like MongoDB for storing data like this.
Another potential option if you have to use SQL Server... I guess... would be to create an xml/json representation of your objects and store them in a table. That will make querying quite a challenge however.
A table such as the above described could be considered a key/value pair store, similar to the following:
CREATE TABLE keyValuePairs (
key varchar(200) not null primary key ,
value xml
)
or
CREATE TABLE keyValuePairs (
key varchar(200) not null primary key ,
value varchar(max)
)
In the first you could store your objects as xml, the second as json (or technically xml as well). You would need to query your table based on the appropriate key, or do some really fancy query work - assuming your value's "schema" can differ dependent on object type being stored.
I obtain a table reference as such:
public static void MyDataGrabbingClass<T>(MyModelCls Model) where T : class
{
DataContext dc = new DataContext(Config.ConnectionString);
//var ITable = (Devart.Data.Linq.ITable)dc.GetType().GetProperty(tableName).GetValue(dc, null);
//var table = dc.GetTable(ITable.GetType());
//var dataModel = dc.Mapping;
//Type tableType = ITable.GetType();
//var t = dc.Mapping.MappingSource.GetModel(typeof(DataContext)).GetMetaType(tableType);
var table = dc.GetTable<T>();
}
I want to then select the specific columns of data using:
var Query = from c in table where Model.DateToColName < Model.DateTo select (Model.ColSelections);
obviously I need to somehow map the table column, and not use Model.DateToColName in the where clause, how do I do this?
In other words, with my dynamically chosen table, how do i get the column data from a string column name
Unfortunately I don't think you'll be able to construct a dynamic query in LINQ (at least not easily). Take a look at Dapper.NET - it's a "simple object mapper for .NET" that might work for you, created by the makers of StackOverflow. For example, using Dapper.NET, your method might look like:
public static void MyDataGrabbingClass<T>(MyModelCls Model) where T : class
{
using (SqlConnection conn = new SqlConnecrion(Config.ConnectionString)
{
conn.Open();
string tableName = ...;
string dateToColumnName = ...;
// depending on how dateToColumnName is constructed, ensure it is not a SQL-injection risk
if (tableName.Any(c => !char.IsLetterOrDigit(c))
throw new ArgumentException("Invalid table name.");
if (dateToColumnName.Any(c => !char.IsLetterOrDigit(c))
throw new ArgumentException("Invalid column name.");
// Query is a
var results = conn.Query<T>("SELECT * FROM [" + tableName + "] WHERE [" + dateToColumnName + "] < #DateTo", new { DateTo = someDate });
...
}
}
Security concern: Dynamic SQL can be susceptible to SQL injection - so be sure that the dateToColumnName variable is not from user-input, or is sanitized or validated appropriately.