Generating a paramatarized query with variable number of parameters - c#

I have some designer generated code that I am using to query a dataset. The designer generated it because I have a Form with a ReportViewer which created it's own BindingSouce andTableAdapter. I used the "Add Query..." function on the TableAdapter smarttag.
The query is a simple SELECT command. It works find but I'd like to sometimes query for multiple records at once (I am generating a report based on a list of barcodes and there will almost always be many). The designer gave me this code :
public virtual int FillBySampleID(dbReceivedSamplersDataSetAccess.tblReceivedSamplersDataTable dataTable, string Param1) {
//FYI the select command it used is "SELECT * FROM tblReceivedSamplers WHERE SampleID IN (?)"
this.Adapter.SelectCommand = this.CommandCollection[2];
if ((Param1 == null)) {
throw new global::System.ArgumentNullException("Param1");
}
else {
this.Adapter.SelectCommand.Parameters[0].Value = ((string)(Param1));
}
if ((this.ClearBeforeFill == true)) {
dataTable.Clear();
}
int returnValue = this.Adapter.Fill(dataTable);
return returnValue;
}
And that works and is good for a single record so I overloaded this method and created this code to allow me to pass any number of parameters at once using the WHERE...IN SQL statement.
public virtual int FillBySampleID(dbReceivedSamplersDataSetAccess.tblReceivedSamplersDataTable dataTable, string[] Params)
{
//this.Adapter.SelectCommand = this.CommandCollection[2];
if ((Params == null))
{
throw new global::System.ArgumentNullException("Param1");
}
else
{
int numParams = Params.Length;
List<string> lstParamQuesMarks = Enumerable.Repeat("'?'", numParams).ToList();
string strParamQuesMarks = String.Join(",", lstParamQuesMarks);
this.Adapter.SelectCommand.CommandText = "SELECT * FROM tblReceivedSamplers WHERE SampleID IN (" + strParamQuesMarks + ")";
this.Adapter.SelectCommand.Parameters.Clear();
for (int i = 0; i < numParams; i++)
{
this.Adapter.SelectCommand.Parameters.AddWithValue("Param"+i, Params[i]);
}
}
if ((this.ClearBeforeFill == true))
{
dataTable.Clear();
}
int returnValue = this.Adapter.Fill(dataTable);
return returnValue;
}
I thought I was being clever but it doesn't seem to be working. It doesn't give an error or anything. It generated a SelectCommand text of SELECT * FROM tblReceivedSamplers WHERE SampleID IN ('?','?','?','?') if I pass it 4 parameters and all the parameters values look good. When I look at the dataTable while debugging and browse to the count property it is set to 0 (unlike the designer generated code which would be set to 1).
My database is OleDb.
Is what I am trying to do possible?

Parameters should not be enclosed in quotes. use ?, not '?'.

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);

Dapper query with dynamic list of filters

I have a c# mvc app using Dapper. There is a list table page which has several optional filters (as well as paging). A user can select (or not) any of several (about 8 right now but could grow) filters, each with a drop down for a from value and to value. So, for example, a user could select category "price" and filter from value "$100" to value "$200". However, I don't know how many categories the user is filtering on before hand and not all of the filter categories are the same type (some int, some decimal/double, some DateTime, though they all come in as string on FilterRange).
I'm trying to build a (relatively) simple yet sustainable Dapper query for this. So far I have this:
public List<PropertySale> GetSales(List<FilterRange> filterRanges, int skip = 0, int take = 0)
{
var skipTake = " order by 1 ASC OFFSET #skip ROWS";
if (take > 0)
skipTake += " FETCH NEXT #take";
var ranges = " WHERE 1 = 1 ";
for(var i = 0; i < filterRanges.Count; i++)
{
ranges += " AND #filterRanges[i].columnName BETWEEN #filterRanges[i].fromValue AND #filterRanges[i].toValue ";
}
using (var conn = OpenConnection())
{
string query = #"Select * from Sales "
+ ranges
+ skipTake;
return conn.Query<Sale>(query, new { filterRanges, skip, take }).AsList();
}
}
I Keep getting an error saying "... filterRanges cannot be used as a parameter value"
Is it possible to even do this in Dapper? All of the IEnumerable examples I see are where in _ which doesn't fit this situation. Any help is appreciated.
You can use DynamicParameters class for generic fields.
Dictionary<string, object> Filters = new Dictionary<string, object>();
Filters.Add("UserName", "admin");
Filters.Add("Email", "admin#admin.com");
var builder = new SqlBuilder();
var select = builder.AddTemplate("select * from SomeTable /**where**/");
var parameter = new DynamicParameters();
foreach (var filter in Filters)
{
parameter.Add(filter.Key, filter.Value);
builder.Where($"{filter.Key} = #{filter.Key}");
}
var searchResult = appCon.Query<ApplicationUser>(select.RawSql, parameter);
You can use a list of dynamic column values but you cannot do this also for the column name other than using string format which can cause a SQL injection.
You have to validate the column names from the list in order to be sure that they really exist before using them in a SQL query.
This is how you can use the list of filterRanges dynamically :
const string sqlTemplate = "SELECT /**select**/ FROM Sale /**where**/ /**orderby**/";
var sqlBuilder = new SqlBuilder();
var template = sqlBuilder.AddTemplate(sqlTemplate);
sqlBuilder.Select("*");
for (var i = 0; i < filterRanges.Count; i++)
{
sqlBuilder.Where($"{filterRanges[i].ColumnName} = #columnValue", new { columnValue = filterRanges[i].FromValue });
}
using (var conn = OpenConnection())
{
return conn.Query<Sale>(template.RawSql, template.Parameters).AsList();
}
You can easily create that dynamic condition using DapperQueryBuilder:
using (var conn = OpenConnection())
{
var query = conn.QueryBuilder($#"
SELECT *
FROM Sales
/**where**/
order by 1 ASC
OFFSET {skip} ROWS FETCH NEXT {take}
");
foreach (var filter in filterRanges)
query.Where($#"{filter.ColumnName:raw} BETWEEN
{filter.FromValue.Value} AND {filter.ToValue.Value}");
return conn.Query<Sale>(query, new { filterRanges, skip, take }).AsList();
}
Or without the magic word /**where**/:
using (var conn = OpenConnection())
{
var query = conn.QueryBuilder($#"
SELECT *
FROM Sales
WHERE 1=1
");
foreach (var filter in filterRanges)
query.Append($#"{filter.ColumnName:raw} BETWEEN
{filter.FromValue.Value} AND {filter.ToValue.Value}");
query.Append($"order by 1 ASC OFFSET {skip} ROWS FETCH NEXT {take}");
return conn.Query<Sale>(query, new { filterRanges, skip, take }).AsList();
}
The output is fully parametrized SQL, even though it looks like we're doing plain string concatenation.
Disclaimer: I'm one of the authors of this library
I was able to find a solution for this. The key was to convert the List to a Dictionary. I created a private method:
private Dictionary<string, object> CreateParametersDictionary(List<FilterRange> filters, int skip = 0, int take = 0)
{
var dict = new Dictionary<string, object>()
{
{ "#skip", skip },
{ "#take", take },
};
for (var i = 0; i < filters.Count; i++)
{
dict.Add($"column_{i}", filters[i].Filter.Description);
// some logic here which determines how you parse
// I used a switch, not shown here for brevity
dict.Add($"#fromVal_{i}", int.Parse(filters[i].FromValue.Value));
dict.Add($"#toVal_{i}", int.Parse(filters[i].ToValue.Value));
}
return dict;
}
Then to build my query,
var ranges = " WHERE 1 = 1 ";
for(var i = 0; i < filterRanges.Count; i++)
ranges += $" AND {filter[$"column_{i}"]} BETWEEN #fromVal_{i} AND #toVal_{i} ";
Special note: Be very careful here as the column name is not a parameter and you could open your self up to injection attacks (as #Popa noted in his answer). In my case those values come from an enum class and not from user in put so I am safe.
The rest is pretty straight forwared:
using (var conn = OpenConnection())
{
string query = #"Select * from Sales "
+ ranges
+ skipTake;
return conn.Query<Sale>(query, filter).AsList();
}

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

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.

Entity Framework Get Table By Name

I am looking for ways to do LINQ on a table selected in runtime via string variable.
This is what I have so far using reflection:
private Entities ctx = new Entities();
public List<AtsPlatform> GetAtsPlatformByName(string atsPlatformName)
{
List<AtsPlatform> atsPlatform = null;
System.Reflection.PropertyInfo propertyInfo = ctx.GetType().GetProperty(atsPlatformName.ToLower());
var platform = propertyInfo.GetValue(ctx, null);
// it fails here highlighting "platform" with error that reads "Error 1 Could not find an implementation of the query pattern for source type 'System.Data.Objects.ObjectQuery'. 'Select' not found. Consider explicitly specifying the type of the range variable 'ats'."
atsPlatform = ((from ats in platform select new AtsPlatform { RequestNumber = ats.RequestNumber, NumberOfFail = ats.NumberOfFail, NumberOfFailWithCR = ats.NumberOfFailWithCR, NumberOfTestCase = ats.NumberOfTestCase }).ToList());
return atsPlatform;
}
In my model class, I have:
public class AtsPlatform
{
public string Name { get; set; }
public string RequestNumber { get; set; }
public Int32? NumberOfFail { get; set; }
public Int32? NumberOfTestCase { get; set; }
public Int32? NumberOfFailWithCR { get; set; }
}
In Database, I have the following tables: "ats1", "ats2", "ats3" .. "atsN" where each of them has the same entity fields as the properties defined in "AtsPlatform"
What I would like to do is simply:
List<AtsPlatform> a1 = GetAtsPlatformByName("ats1");
List<AtsPlatform> a2 = GetAtsPlatformByName("ats2");
List<AtsPlatform> aN = GetAtsPlatformByName("atsN");
I could use "switch" but this makes the code less expandable and requires update whenever new "ats(N+1)" gets created.
My 2 days of research lead me nowhere but back to ground zero. I'm quite stuck.
PLEASE HELP! Thanks!
Instead of reflection, how about using the SqlQuery function?
So
List<AtsPlatform> GetAtsPlatformByName(int index)
{
using (var ctx = new Entities())
{
return ctx.Database.SqlQuery<AtsPlatform>("SELECT * FROM dbo.ats" + index)
.ToList();
}
}
Also, there is no change tracking on the entities using the SqlQuery method on the Database object (which is ok in your case I suppose since the AtsPlatform class only contains primitive properties).
For changes tracking you will need to use the DbSet SqlQuery method, and may need to mix some reflection in.
Sorry for my late response as I wondered off trying out different solutions:
Solution #1: Master Table
As suggested by #Alexw, creating a Master Table works the best ONLY if you are allowed to change the design of the db. I'm currently working with the db owner to make this change. Due to dependencies, this change has to wait till next phase.
Meanwhile, I've created mock db to exercise this approach.
Solution #2: Raw Query
As Suggested by #Umair, raw query will do the job. I've created a class that handles raw sql query.
public class AtsRawQuery
{
private string ConnetionString = "";
public AtsRawQuery(string connectionString)
{
this.ConnetionString = connectionString;
}
public List<List<string>> Query(string queryString)
{
List<List<string>> results = null;
MySqlConnection conn = null;
MySqlDataReader rdr = null;
try
{
conn = new MySqlConnection(this.ConnetionString);
conn.Open();
MySqlCommand cmd = new MySqlCommand(queryString, conn);
rdr = cmd.ExecuteReader();
if (rdr.HasRows)
{
results = new List<List<string>>();
while (rdr.Read())
{
List<string> curr_result = new List<string>();
for (int columnIndex = 0; columnIndex <= rdr.FieldCount - 1; columnIndex++)
{
curr_result.Add(rdr.GetString(columnIndex));
}
results.Add(curr_result);
}
}
}
catch (MySqlException ex)
{
Console.WriteLine(ex.Message);
return null;
}
finally
{
if (rdr != null)
{
rdr.Close();
}
if (conn != null)
{
conn.Close();
}
}
return results;
}
}
This class returns a 2 dimension list for later consumption.
In my model class, I added a parser method:
public class AtsPlatform
{
public string Name { get; set; }
public string RequestNumber { get; set; }
public Int32? NumberOfFail { get; set; }
public Int32? NumberOfTestCase { get; set; }
public Int32? NumberOfFailWithCR { get; set; }
public void Parse(string name, string requestNumber, string numberOfFail, string numberOfTestCase, string numberOfFailWithCR)
{
Int32 temp;
this.Name = name;
this.RequestNumber = requestNumber;
this.NumberOfFail = (Int32.TryParse(numberOfFail, out temp)) ? Int32.Parse(numberOfFail) : 0;
this.NumberOfTestCase = (Int32.TryParse(numberOfTestCase, out temp)) ? Int32.Parse(numberOfTestCase) : 0;
this.NumberOfFailWithCR = (Int32.TryParse(numberOfFailWithCR, out temp)) ? Int32.Parse(numberOfFailWithCR) : 0;
}
}
Solution #2(b): Raw Query using ExecuteStoreCommand
public List<AtsPlatform> GetAtsPlatformByName(string atsPlatformName)
{
List<AtsPlatform> atsPlatforms = null;
string stm = String.Format("SELECT RequestNumber, NumberOfFail, NumberOfTestCase, NumberOfFailWithCR FROM {0}", atsPlatformName);
atsPlatforms = new List<AtsPlatform>();
foreach (AtsPlatform ats in ctx.ExecuteStoreQuery<AtsPlatform>(stm))
{
atsPlatforms.Add(ats);
}
return atsPlatforms;
}
Solution #3: Stored Procedure
I've created a stored procedure and here is the code:
DELIMITER $$
CREATE PROCEDURE `UnionAtsTables`()
BEGIN
DECLARE atsName VARCHAR(10);
DECLARE atsIndex INT;
SET atsIndex = 1;
SET #qry = '';
WHILE atsIndex > 0 DO
SET atsName =concat('ATS',atsIndex);
IF sf_is_table(atsName) = 1 THEN
Set #temp_qry = CONCAT('SELECT *, ''', atsName ,''' As TestPlatform FROM ', atsName, ' WHERE RequestNumber <> ''''' );
If #qry = '' THEN
SET #qry = #temp_qry;
ELSE
SET #qry = CONCAT(#qry, ' UNION ', #temp_qry);
END IF;
ELSE
SET atsIndex = -1;
END IF;
SET atsIndex = atsIndex + 1;
END WHILE;
DROP TABLE IF EXISTS ats_all;
SET #CreateTempTableQuery = CONCAT('CREATE TEMPORARY TABLE ats_all AS ', #qry ,'');
PREPARE stmt1 FROM #CreateTempTableQuery;
EXECUTE stmt1;
DEALLOCATE PREPARE stmt1;
ALTER TABLE ats_all DROP COLUMN ExecOrder;
ALTER TABLE ats_all ADD ExecOrder INT PRIMARY KEY AUTO_INCREMENT;
ALTER TABLE ats_all auto_increment = 0;
END
Here is the function I found online that checks if table exists in db.
DELIMITER $$
CREATE FUNCTION `sf_is_table`(`in_table` varchar(255)) RETURNS tinyint(4)
BEGIN
/**
* Check if table exists in database in use
*
* #name sf_is_table
* #author Shay Anderson 08.13 <http://www.shayanderson.com>
*
* #param in_table (table name to check)
* #return TINYINT (1 = table exists, 0 = table does not exist)
*/
# table exists flag
DECLARE is_table BOOLEAN DEFAULT FALSE;
# table count
DECLARE table_count INT DEFAULT 0;
# database name
SET #db = NULL;
# set database name
SELECT
DATABASE()
INTO
#db;
# check for valid database and table names
IF LENGTH(#db) > 0 AND LENGTH(in_table) > 0 THEN
# execute query to check if table exists in DB schema
SELECT COUNT(1) INTO table_count
FROM information_schema.`TABLES`
WHERE TABLE_SCHEMA = #db
AND TABLE_NAME = in_table;
# set if table exists
IF table_count > 0 THEN
SET is_table = TRUE;
END IF;
END IF;
RETURN is_table;
END
Conclusion:
Thank you everyone for your suggestions.
I decided to use Solution #2 since it does not cause as much impact to the db performance as Solution #3 and it does not require db redesign as Solution #1.
I don't think what you are doing will work like that. You should create an entity based on a single 'master' table, eg. Ats
Once you have done this you will have a property on your Entities class called Ats. You can now use this property to select the entities using a raw SQL query like this.
var atsName = "ats1";
using (var context = new Entities())
{
var blogs = context.Ats.SqlQuery(string.Format("SELECT * FROM {0}", atsName)).ToList();
}
Alternatively you could try this (I am assuming the property type is DBSet as you haven't specified it in the question)
var platform = propertyInfo.GetValue(ctx, null) as DBSet<Ats>;
atsPlatform = platform.Select(ats => new A new AtsPlatform { RequestNumber = ats.RequestNumber, NumberOfFail = ats.NumberOfFail, NumberOfFailWithCR = ats.NumberOfFailWithCR, NumberOfTestCase = ats.NumberOfTestCase }).ToList();
return atsPlatform;

Categories