I am using the following code as a controller for a single table to access the table from my database, and assign values of each column to a variable. However, I have many tables(Booktrans in this case) and I want to use a single controller that can access all the tables using table ID as a variable. After that, assigning values of different columns from different tables will also be different from the code below. Could anyone help me with a "dynamic" way of coding to replace this controller for a specific table with a dynamic controller?
I am using MVC4.
All the tables have different structures and different column names.
public ActionResult Booktrans()
{
String connectionString = "Dsn=SECURE;Uid=internwebuser";
OdbcConnection conn = new OdbcConnection(connectionString);
String sql = "SELECT * FROM booktrans";
OdbcCommand cmd = new OdbcCommand(sql, conn);
var model = new List<Booktrans>();
using (conn)
{
conn.Open();
OdbcDataReader rdr = cmd.ExecuteReader();
//model = new List<Booktrans>();
while (rdr.Read())
{
var book = new Booktrans();
book.ship_last_name = rdr["ship_last_name"].ToString();
book.ship_first_name = rdr["ship_first_name"].ToString();
book.ship_zip = rdr["ship_zip"].ToString();
book.ship_state = rdr["ship_state"].ToString();
book.ship_address = rdr["ship_address"].ToString();
book.ship_city = rdr["ship_city"].ToString();
book.day_phone = rdr["day_phone"].ToString();
book.email_address = rdr["email_address"].ToString();
model.Add(book);
}
}
return View(model);
}
How about you use an actual ORM, since that's what you're trying to reverse-engineer here. With something like Entity Framework, you create an entity class that maps to a particular table and that has properties that map to columns in that table. Then, with Entity Framework, in particular, you add a DbSet that represent the collection of rows in this table to your context:
public DbSet<Foo> Foos { get; set; }
With that, you can then simply use the API Entity Framework provides to interact with that data:
// Get specific Foo
var foo = db.Foos.Find(id);
// Get matching Foos
var foos = db.Foos.Where(m => m.Bar == "bar");
// Add a new Foo
db.Foos.Add(new Foo { Bar = "bar" });
So on and so forth. Other ORMS like NHibernate have slight different setups and API, but they all basically behave the same and don't require that you worry about generating SQL.
Related
I try to read sql table and load all into a variable
Code:
String query2 = "";
query2 = String.Format("SELECT * FROM Seguridad.UsuarioPerfil WHERE UsuarioID = {0}", UsuarioID);
SQLService sqlservice2 = new SQLService();
DataTable reader2 = sqlservice.Leer(query2);
I want to store all data into a variable var tmpPerfiles as object.
I can do something like:
var tmpPerfiles ="";
foreach (DataRow row in reader.Rows)
{
tmpPerfiles = row["UsuarioId"].ToString();
tmpPerfiles = row["PerfilId"].ToString();
}
But I canĀ“t call tmpPerfiles two times. How can I achieve that? Regards
Okay, first up: STOP! Do not EVER write SQL queries like this. SQL Injection Attack is still the #1 cause of security breaches and vulnerabilities (per OWASP), and it's exclusively caused by people writing SQL statements like this.
Never ever write SQL statements like:
statement = "SELECT something from sometable where " + someVar ...
... because all it takes is for that 'somevar' to have an apostrophe and some malicious hacking code, and you're granting an external entity access to your database. Don't even do it if you're not expecting the field to be user-provided or such - it's a bad habit, and it leads to horrendous security faults.
Instead, you should always use one of the following:
Stored Procedures with parameterized inputs. Aka, dbo.usp_FindUser,
which accepts #userName, and the proc has WHERE name = #userName
Parameterized Sql Command. Aka, creating a SqlCommand with "Select *
from something from someTable where userName = #userName", and then
adding a parameter to the SqlCommand of userName, and a value of what
user you're looking for.
Okay, all that said?
Keep in mind, a variable can contain a grouping of things. Generally, if you're looking to contain a table within a single variable? It'll typically look something like:
string x, int y, string z - fields within the Database
Class dataRecord - a class, which contains string x, int y, string z.
List<dataRecord> - a list of instances of a dataRecord class
... make sense? You've got one variable per column, which you group into a class. One instance of the class represents one data row. And then a List<> of that class represents multiple rows of that table (or just the whole table itself.)
Usually, code that follows SRP (but that doesn't use EntityFramework) will look something like:
List<myFancyClass> tableEntries = new List<myFancyClass>();
foreach (DataRow dr in myDataTable.Rows)
{
myFancyClass line = new myFancyClass(dr); // constructor that takes in a DataRow
tableEntries.Add(line);
}
... at that point, the table is stored in the tableEntries variable.
e.g.
use a dto for storing it into a list
private class TmpDto {
string UsuarioId { get; set;}
string PerfilId { get; set;}
}
var Ilist<TmpDto> list = new List<TmpDto>();
foreach (DataRow row in reader.Rows)
{
var dto = new TmpDto();
dto.UsuarioId = row["UsuarioId"].ToString();
dto.UsuarioId = row["PerfilId"].ToString();
list.Add(dto);
}
so you have several objects stored within a list
If you are using EntityFramework, this is really easy. After setting up a class for the object (containing all fields), just use linq to get the object.
var myData = UsuarioPerfil.Where(e => e.UsuarioID == UsuarioID).FirstorDefault();
If all you want to do is get values from a database and put them into something, then I think the introduction of a datatable is overkill. While the implementation is easy enough, it adds overhead. I'd opt to use a DbDataReader instead.
This is an example of extracting a single field, and then just adding it to a list.
List<string> results = new List<string>();
String query2 = "SELECT PerfilId FROM Seguridad.UsuarioPerfil WHERE UsuarioID = #USARIO";
SqlCommand cmd = new SqlCommand(query2, connection);
cmd.Parameters.Add(new SqlParameter("#USARIO", SqlDbType.VarChar));
cmd.Parameters[0].Value = UsuarioID;
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
results.Add(reader.GetString(0));
}
reader.Close();
You referenced two fields, but since UsuarioID was defined in the where clause, it didn't seem necessary to pull it back.
Also, as #DotNetDev mentioned, don't use literals... the use of parameters is not only SQL-Injection safe, but it's more scalable and actually friendlier to the database (compile-once, execute many).
Finally, if you want to pull back multiple fields, create a data (domain) object, and make your results a list of that object. If you need an example, feel free to ask.
How can I use dynamic queries in C# ? From what I've searched its similiar to when we use SqlCommand with parameters to prevent sql injection(example below).
using (SQLiteConnection DB_CONNECTION = new SQLiteConnection(connectionString))
{
DB_CONNECTION.Open();
string sqlquery = "UPDATE table SET Name =#Name, IsComplete=#IsComplete WHERE Key =#Key;";
int rows = 0;
using (SQLiteCommand command = new SQLiteCommand(sqlquery, DB_CONNECTION))
{
SQLiteParameter[] tableA = { new SQLiteParameter("#Key", todo.Key), new SQLiteParameter("#Name", table.Name), new SQLiteParameter("#IsComplete", table.IsComplete) };
command.Parameters.AddRange(tableA);
rows = command.ExecuteNonQuery();
}
DB_CONNECTION.Close();
return (rows);
}
I'm new to c# and i wondering how can I make this work, thanks in advance.
Basically just build up the string sqlQuery based on a set of conditions and ensure that the appropriate parameters have been set. For example, here is some psuedo-C# (not tested for bugs):
//Set to true, so our queries will always include the check for SomeOtherField.
//In reality, use some check in the C# code that you would want to compose your query.
//Here we set some value we want to compare to.
string someValueToCheck = "Some value to compare";
using (SQLiteConnection DB_CONNECTION = new SQLiteConnection(connectionString))
{
DB_CONNECTION.Open();
string sqlquery = "UPDATE MyTable SET Name =#Name, IsComplete=#IsComplete WHERE Key =#Key";
//Replace this with some real condition that you want to use.
if (!string.IsNullOrWhiteSpace(someValueToCheck))
{
sqlquery += " AND SomeOtherField = #OtherFieldValue"
}
int rows = 0;
using (SQLiteCommand command = new SQLiteCommand(sqlquery, DB_CONNECTION))
{
//Use a list here since we can't add to an array - arrays are immutable.
List<SQLiteParameter> tableAList = {
new SQLiteParameter("#Key", todo.Key),
new SQLiteParameter("#Name", table.Name),
new SQLiteParameter("#IsComplete", table.IsComplete) };
if (!string.IsNullOrWhiteSpace(someValueToCheck)) {
//Replace 'someValueToCheck' with a value for the C# that you want to use as a parameter.
tableAList.Add(new SQLiteParameter("#OtherFieldValue", someValueToCheck));
}
//We convert the list back to an array as it is the expected parameter type.
command.Parameters.AddRange(tableAList.ToArray());
rows = command.ExecuteNonQuery();
}
DB_CONNECTION.Close();
return (rows);
}
In this day and age it would probably be worth looking into LINQ to Entities, as this will help you to compose queries dynamically in your code - for example https://stackoverflow.com/a/5541505/201648.
To setup for an existing database - also known as "Database First" - see the following tutorial:
https://msdn.microsoft.com/en-au/data/jj206878.aspx
You can skip step 1 since you already have a database, or do the whole tutorial first as practice.
Here is some psuedo-C# LINQ code to perform roughly the same update as the previous example:
//The context you have setup for the ERP database.
using (var db = new ERPContext())
{
//db is an Entity Framework database context - see
//https://msdn.microsoft.com/en-au/data/jj206878.aspx
var query = db.MyTable
.Where(c => c.Key == todo.Key);
if (!string.IsNullOrWhiteSpace(someValueToCheck))
{
//This where is used in conjunction to the previous WHERE,
//so it's more or less a WHERE condition1 AND condition2 clause.
query = query.Where(c => c.SomeOtherField == someValueToCheck);
}
//Get the single thing we want to update.
var thingToUpdate = query.First();
//Update the values.
thingToUpdate.Name = table.Name;
thingToUpdate.IsComplete = table.IsComplete;
//We can save the context to apply these results.
db.SaveChanges();
}
There is some setup involved with Entity Framework, but in my experience the syntax is easier to follow and your productivity will increase. Hopefully this gets you on the right track.
LINQ to Entites can also map SQL stored procedures if someone one your team objects to using it for performance reasons:
https://msdn.microsoft.com/en-us/data/gg699321.aspx
OR if you absolutely ust compose custom queries in the C# code this is also permitted in Entity Framework:
https://msdn.microsoft.com/en-us/library/bb738521(v=vs.100).aspx
I have a requirement where I need to read queries from Access DB in c# and check if the access db query has any keyword like "KEY" if it has keywords I need to enclose that in square brackets"[]".just like how it is done in SQL.
Could someone suggest me how to do that?
You can retrieve the query text like this:
string connString = #"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\...\myDB.mdb";
using (var conn = new OleDbConnection(connString )) {
conn.Open();
string[] restrictions = new string[] { null, null, "myQuery" };
DataTable schema = conn.GetSchema("Views", restrictions);
if (schema.Rows.Count > 0) {
DataRow row = schema.Rows[0];
string queryText = (string)row["VIEW_DEFINITION"];
Console.WriteLine(queryText);
}
}
If you drop the restrictions argument with the query name, conn.GetSchema("Views") returns one row for each query. If you query conn.GetSchema("Procedures") other types of queries like insert, update and DDL statements that are not considered as queries are returned in row["PROCEDURE_DEFINITION"].
View (query) names are returned in row["TABLE_NAME"] and procedure names in row["PROCEDURE_NAME"].
And you can update the query like this:
using (var conn = new OleDbConnection(connString)) {
conn.Open();
var cmd = new OleDbCommand("DROP PROCEDURE myQuery", conn);
cmd.ExecuteNonQuery();
cmd = new OleDbCommand("CREATE PROCEDURE myQuery AS SELECT * FROM myTable", conn);
cmd.ExecuteNonQuery();
}
Strangely enough the OleDb CREATE DDL (Data Definition Language) designates the queries as 'procedures' but the schema table returns a 'VIEW_DEFINITION' and the query name is returned in the column 'TABLE_NAME'. SELECT queries must be retrieved as "Views", other types of queries as "Procedures"; however, both types are created as PROCEDUREs.
While I was testing the answer that #Olivier Jacot-Descombes provided, I was not able to retreive all the queries text representation. Therefore I applied some other method where you open the existing Ms Access database instance and read the queries that are stored in it.
Here is the class I used:
public class MsAccess
{
private Microsoft.Office.Interop.Access._Application _oAccess;
public MsAccess(string path)
{
_oAccess = (Microsoft.Office.Interop.Access._Application)System.Runtime.InteropServices.Marshal.BindToMoniker(path);
}
public string ReturnSqlQueryText(string queryName)
{
string queryDef = null;
var qdefs = _oAccess.CurrentDb().QueryDefs;
foreach (QueryDef qdef in qdefs)
{
if(qdef.Name.Equals(queryName))
queryDef = qdef.SQL;
}
return queryDef;
}
}
Using this code might require you adding using Microsoft.Office.Interop.Access.Dao and Microsoft.Office.Interop.Access both (15.0.0.0) where you can find them under Extension on the reference menu
I have a stored procedure that returns two columns without specifying their name, one is the ID (int) and the other one is a string. It is not possible for me to alter the stored procedure so that it can return the results with column names
If I let Visual Studio to create the .edmx file with the stored procedures in the dbContext, it creates a new model class with two properties called Column1 and Column2. The problem is that when I run it, I receive the following error:
The data reader is incompatible with the specified 'Schema.addCliente_Result'. A member of the type, 'Column1', does not have a corresponding column in the data reader with the same name.
Is there any other way to map the result to maybe a a dictionary<int,string> or anything else that does not need the column name?
You can try using KeyValuePair. It saves the value specified for your key. You can create list of KeyValuePair if you have more pairs and you want to iterate through it easier. I'm not sure if this helps in your case, sorry if it's not helping.
KeyValuePair MSDN
Perhaps you may want to consider projection since you don't want it mapped to a Model. Projection is when the result of a query is output to a different type than the one queried. Either way, you can use the results of the stored procedure to either create an anonymous type or a class of your own. Since there's no code for me to reference, I have an example as follows:
Class used to store results
public Class Result
{
public int myID {get; set;}
public string myString {get; set;}
}
Code to call stored procedure
string connectionString = ConfigurationManager.AppSettings["MyDatabase"];
var conn = new SqlConnection(connectionString);
conn.Open();
string query = #"my_stored_procedure";
using (var sqlAdpt = new SqlDataAdapter(query, conn))
{
sqlAdpt.SelectCommand.CommandType = CommandType.StoredProcedure;
// Ex: Parameters if your sp takes one
var dataDate = new SqlParameter { ParameterName = "#DataDate", Value = DateTime.Now };
sqlAdpt.SelectCommand.Parameters.Add(dataDate);
var results = new DataSet();
sqlAdpt.Fill(results);
List<Result> resultList = results.Tables[0].AsEnumerable().
Select(dataRow => new Result
{
myID = dataRow.Field<int>("ID"),
myString = dataRow.Field<string>("column_I_cant_change")
}).ToList();
}
In a EF6 context, I want to filter multiples entities by dynamic fields/attributes. I'm new to EF and my perspective is very corrupted by T-SQL and stored procedures and dynamic SQL queries.
For instance in a ERP environment, the user can filter by a code, and the system should return the:
Customer with CustomerID = code
Supplier with SupplierID = code
User with UserID = code
Orders with CustomerID/SupplierID = code
etc.
But can not only be a code, can multiples concepts to filter for: a name, a city, a date, ... and may all not apply to all the entities.
So since each entity has different attributes names to refer to that "code" concept, I've thought that the best solution is to use EntityCommand instead of LinQ.
And the code should look something like:
// Create a query that takes two parameters.
string eSqlCustomerQuery =
#"SELECT VALUE Contact FROM AdventureWorksEntities.Customer AS Customer";
string eSqlCustomerQuery =
#"SELECT VALUE Contact FROM AdventureWorksEntities.Customer AS Customer";
// Create a list of parameters
var param = new SortedList<string, EntityParameter>();
// for each clauses add a pamater and build the query command dynamically.
if(!code)
{
eSqlCustomerQuery += "WHERE Customer.CustomerID = #CODE";
eSqlSupplierQuery += "WHERE Supplier.SupplierID = #CODE";
//... more entities to
param["CODE"].ParameterName = "CODE";
param["CODE"].Value = code;
}
// more parameters here...
using (EntityConnection conn =
new EntityConnection("name=AdventureWorksEntities"))
{
conn.Open();
using (EntityCommand cmd = new EntityCommand(eSqlCustomerQuery, conn))
{
cmd.Parameters.Add(param["CODE"]);
cmd.Parameters.Add(param["DATE"]);
// more parameters here...
}
// the same for each query...
// ...
// run all the queries ...
// ...
// Etc.
conn.Close();
}
My questions are 3:
At the time I'm doing cmd = new EntityCommand(eSqlCustomerQuery, conn) can I use something like the System.Data.SqlClient.SqlCommandBuilder.DeriveParameters(cmd);?
Since this dynamic query it's so dynamic that it can be cached or have a reusable execution plan, how can it be improved?
Is it possible to do it with LinQ in a cleaner way?
Use LINQ like that:
//define base LINQ
Contracts = from R in AdventureWorks.Customer select R; //there is IQueryable, not actually materialized
//tune filters, no records will fetched
Result = Contracts;
if (code!=null) Result = Result.Where(_=>_.Code==code);
if (date!=null) Result = Result.Where(_=>_.Date==date);
//materialize records
Records = Result..Select(_=>_.Contract).ToArray();