VS2013 c# windows form.
I am learning Dapper and this is my 1st shot at it:
I have a simple class:
public class R
{
public int RID { get; set; }
public int RType { get; set; }
public string CC { get; set; }
public string RI { get; set; }
.
.
.
}
private void _setR(string rID)
{
int r_ID = Convert.ToInt16(requestID);
MY_R = new R();
SqlConnection c = new SqlConnection(Connection string);
c.Open();
var v= c.Query<R>("select RID, RType, CC, RI, .,.,., " +
"from Db_View_R where RID=#r_ID",
new { #r_ID = r_ID }) ;
c.Close();
MY_R = (R)v ; <--------- Invalid cast error here
}
The query is simple: a few columns from a view. Returns only 1 row. Not sure what am I missing here.
Thanks in advance
Extension method Query<T> returns IEnumerable<T>. So you definitely can't assign value of type IEnumerable<T> to variable of type T. You should take only one item from sequence:
MY_R = v.FirstOrDefault(); // or First, Single, SingleOrDefault
Actually with improved naming your code should look like:
var sql = "SELECT RID, RType, CC, RI FROM Db_View_R where RID = #id";
using(var conn = new SqlConnection(ConnectionString))
{
MY_R = conn.Query<R>(sql, new { id = Convert.ToInt16(requestID) })
.FirstOrDefault();
}
I still don't like names like R (it probably should be Request) but its already much more readable and safe (you should wrap connection usage in using statement).
use SingleOrDefault()
var v= c.Query<R>("select RID, RType, CC, RI, .,.,., " +
"from Db_View_R where RID=#r_ID",
new { #r_ID = r_ID }).SingleOrDefault();
Related
I have a query
string sQuery = string.Format("SELECT {0} FROM vwweb_Orders WHERE CustFID = ?", columns);
that gets executed here
var result = await conn.QueryAsync<Order>(sQuery, new { ID = Custid });
But say that I'm searching based on parameters chosen by the user. I can build up the where clause, but how do I build up the anonymous array?
new { ID = Custid }
I want something like
var params = new {};
if (!String.IsNullOrWhiteSpace(username)) {
params += {username}
}
If you really want to have params like an anonymous type, you can use an ExpandoObject:-
dynamic params = new ExpandoObject();
if (!string.IsNullOrWhiteSpace(username)) {
params.Username = username;
}
Or if you want an array (and you don't know the length ahead of time), use a List<string>:-
var paramlist = new List<string>();
if (!string.IsNullOrWhiteSpace(username)) {
paramlist.Add("username");
}
var params = paramlist.ToArray();
However, if you are constructing the WHERE clause, you will always have a fixed number of parameters in your SQL statement anyway (or you'll have to construct it dynamically too).
One other method you can use when dynamically building a query for filtering is this:-
SELECT *
FROM vwweb_Orders
WHERE 1=1
AND (#custid IS NULL OR CustFID = #custid)
AND (#xyz IS NULL OR XYZ = #xyz)
-- etc
Then supply all the parameters to your QueryAsync call, and if any are null, they'll be skipped in the WHERE clause.
Maybe you can write your query to check for null/empty/non-zero values first otherwise evaluate the real values as follows:
public async Task<List<Order>> Execute(OrderQuery query)
{
var sql = $#"SELECT
...
FROM vwweb_Orders
WHERE #{nameof(query.CustomerId)} <= 0 OR customer_id = #{nameof(query.CustomerId)}
AND ISNULL(#{nameof(query.CustomerName)}, '') = '' OR customer_name = #{nameof(query.CustomerName)}";
return await conn.QueryAsync<Order>(sql, new { query.CustomerId, query.CustomerName});
}
public class OrderQuery
{
public int CustomerId { get; set; }
public string CustomerName { get; set; }
}
public class Order
{
}
I create a question pool from where I choose random question to apply some algorithm, anyway when I debug the program I get Index was outside the bounds of the array error.
to have a clear idea about what I am talking about, here is my class for question:
Firstly I define class Gene represent question
public class Gene
{
public string question { get; set; }
public string CLO { get; set; }
public string type { get; set; }
public string Answer { get; set; }
public string mark { get; set; }
public string chapter { get; set; }
public Gene(string s, string t, string i, string a, string m, string c)
{
this.question = s;
this.type = t;
this.CLO = i;
this.Answer = a;
this.mark = m;
this.chapter = c;
}
}
List<Gene> QuestionList = new List<Gene>();
then I bring question from database
protected void DropDownList5_SelectedIndexChanged(object sender, EventArgs e)
{
string sub = DropDownList5.Text;
SqlConnection con = new SqlConnection(#"Data Source=.\SQLEXPRESS;AttachDbFilename=C:\Users\Sondos\Downloads\For the project\suz\ExamGenerationSystem.mdf;Integrated Security=True;Connect Timeout=30;User Instance=True");
string s = "select * FROM QuestionBank WHERE (Cource_Code = '" + DropDownList5.SelectedItem.Text + "') ORDER BY RAND()";
SqlCommand cmd = new SqlCommand(s, con);
SqlDataReader dr;
con.Open();
dr = cmd.ExecuteReader();
dr.Read();
while (dr.Read())
{
string ques = dr["Question"].ToString();
string questype = dr["Question_Type"].ToString();
string quesCLO = dr["CLO"].ToString();
string quesAnswer = dr["Answer"].ToString();
string quesMark = dr["Mark"].ToString();
string quesChapter = dr["Chapter"].ToString();
QuestionList.Add(new Gene(ques, questype, quesCLO, quesAnswer, quesMark, quesChapter));
}
}
then I make the question Pool
Gene[] QuestionPool { get { return QuestionList.ToArray(); } }
and when I try to choose question randomly using this :
private System.Random randome;
private Gene GetRandomQuestion()
{
int i = randome.Next(QuestionPool.Length);
return QuestionPool[i];
}
the error Index was outside the bounds of the array was in line
return QuestionPool[i];
int i = randome.Next(QuestionPool.Length);
Here if QuestionPool is empty, then this becomes int i = randome.Next(0) which returns zero.
Therefore,
return QuestionPool[i]; // Where i becomes 0
Will throw Index was outside the bounds as QuestionPool is empty and doesn't have anything at index 0.
So make sure that QuestionPool is not empty.
Some of the situations which will lead to QuestionPool being empty.
No record in database which has Cource_Code = DropDownList5.SelectedItem.Text
In this case while (dr.Read()) will be false on the first iteration and no Gene will be added to QuestionList.
GetRandomQuestion is called before any Gene is added to QuestionList
Avoiding SQL Injection
This line is vulnerable to SQL Injection
(Cource_Code = '" + DropDownList5.SelectedItem.Text + "')
This means that if a user changes the SelectedItem's Text to something like
a'); Drop table QuestionBank; -- //Anything after -- becomes a comment
Then the query will become
select * FROM QuestionBank WHERE (Cource_Code = 'a'); Drop table QuestionBank;
And your table will be dropped.
To avoid this please refer to this answer.
Lists as Arrays
When you have lists, then you can also access its items by index. And to get its length you can use Count. Thus, GetRandomQuestion can become
private Gene GetRandomQuestion()
{
int i = randome.Next(QuestionList.Count);
return QuestionList[i];
}
And you won't require the QuestionPool, provided that you're using it only for this purpose.
You just missed instantiate randome before you reference it.
private System.Random randome;
private Gene GetRandomQuestion()
{
randome = new System.Random // here
int i = randome.Next(QuestionPool.Length);
return QuestionPool[i];
}
Below I have a method I am trying to create. The overall objective of the method is to create a list, open a database in SQL, and convert that information to a string. The problem though I am having is returning all the data. I understand for a method to work you need a return statement of some sort, however when I try to return the list, it keeps telling me
Cannot implicitly convert type 'System.Collections.Generic.List(Namespace).MyHome' to '(namespace).MyHome
So my question to you all is, can I return the list, or will I have to create another variable to return, or am I trying to return the wrong thing entirely?
Below is the method I am working on. For explanation, this method is using a class I created that holds the credentials for the data I am trying to get.
private static MyHome GetUserDataFromMyHome(string username)
{
List<MyHome> myHomeInformation = new List<MyHome>();
using (SqlConnection connection = new SqlConnection(Properties.Settings.Default.MyHomeConnectionString))
{
SqlCommand command = connection.CreateCommand();
command.CommandText = #"SELECT USER_NAME, EMAIL, FIRST_NAME, LAST_NAME, TRAVELER_UID FROM DATA_BASE";
connection.Open();
SqlDataReader reader = sqlError.ExecuteReader();
while (reader.Read())
{
MyHome userInformation = new MyHome();
foreach (MyHome item in myHomeInformation)
{
userInformation.myHomeUserName = Utilities.FromDBValue<string>(reader["USER_NAME"]);
userInformation.myHomeEmail = Utilities.FromDBValue<string>(reader["EMAIL"]);
userInformation.myHomeFirstName = Utilities.FromDBValue<string>(reader["FIRST_NAME"]);
userInformation.myHomeLastName = Utilities.FromDBValue<string>(reader["LAST_NAME"]);
userInformation.myHomeTravelerUID = Utilities.FromDBValue<string>(reader["TRAVELER_UID"]);
myHomeInformation.Add(userInformation);
}
}
}
return myHomeInformation;
}
Here is the class that holds the credentials:
class MyHome : IEnumerable<MyHome>
{
public string myHomeUserName { get; set; }
public string myHomeEmail { get; set; }
public string myHomeFirstName { get; set; }
public string myHomeLastName { get; set; }
public string myHomeTravelerUID { get; set; }
}
Replace
private static MyHome GetUserDataFromMyHome(string username)
with
private static List<MyHome> GetUserDataFromMyHome(string username)
the return type should be a list of objects and not just a object
A List<MyHome> is not a MyHome. If you want your function to return a List<MyHome>, declare it as such.
You have to declare the return type of the function to be a List of MyHome.
private static List<MyHome> GetUserDataFromMyHome(string username)
Otherwise whatever is calling this is expecting to get one instance of MyHome.
I have this class:
public class allFields
{
public string EAN { get; set; }
public string title { get; set; }
public string qty { get; set; }
public string price { get; set; }
public DateTime date { get; set; }
}
And a function that return an anonymous type:
public IEnumerable<object> stockEtatQty()
{
List<allFields> afList = new List<allFields>();
var query = from x in ctx.book
where x.qty > 0
select x;
foreach (var item in query)
{
allFields af = new allFields();
af.EAN = item.EAN;
af.title = item.Titre;
af.qty = ""+item.Quantite;
afList.Add(af);
}
var q = from x in afList
select new { EAN=x.EAN, Title=x.title, Quantity=x.qty };
return q; //q is a IEnumerable<'a> where a is new {string EAN, string Title, string Quantity}
}
In my WinForm a use this function as below:
private void QuantityToolStripMenuItem_Click(object sender, EventArgs e)
{
ServiceStock sstock = new ServiceStock();
var q = sstock.stockEtatQty().ToList();// q is a list<object>
string str = "";
foreach (var item in q)
{
str += item + Environment.NewLine;
}
MessageBox.Show(str);
}
The result is:
{ EAN = 1, Title = CSharp Security, Quantity = 970 }
{ EAN = 2, Title = MISC, Quantity = 100 }
...
What I want?
I want not like the result above, but separate each field apart of the item in the loop foreach, e.g get item.EAN, item.Title and item.Quantity.
If there is no solution for my problem I would like to know an alternative,
Thanks for help.
The obvious solution is to create a custom type (let's call it BookInfo) and return a IEnumerable<BookInfo> instead of a IEnumerable<object> (and maybe override ToString if you want to put the formatting into this class itself).
Then you can easily format the output.
public class BookInfo
{
public string EAN {get;set;}
public string Title {get;set;}
public int Quantity {get;set;}
}
public IEnumerable<BookInfo> stockEtatQty()
{
...
var q = from x in afList
select new BookInfo { EAN=x.EAN, Title=x.title, Quantity=x.qty };
return q;
}
private void QuantityToolStripMenuItem_Click(object sender, EventArgs e)
{
ServiceStock sstock = new ServiceStock();
var q = sstock.stockEtatQty();
var message = string.Join(Environment.NewLine,
q.Select(item => String.Format("{0} {1} {2}", item.EAN, item.Title, item.Quantity)));
MessageBox.Show(message);
}
Since the static type information about the object of anonymous type is lost by the time that you exit stockEtatQty() method, you could cast the object to dynamic and access fields like this:
str = string.Join(Environment.NewLine, q.Cast<dynamic>().Select(item =>
string.Format("{0} {1} {2}", item.EAN, item.Title, item.Quantity)
));
The cast to dynamic tells the compiler that EAN, Title, and Quantity need to be resolved at runtime.
Note that I also replaced the foreach loop with a call to string.Join to improve performance: repeated string concatenation creates unnecessary partial string objects, which string.Join avoids. Another solution would be to use StringBuider instead of string concatenation +=.
stockEtatQty is in a project (Service) and QuantityToolStripMenuItem_Click is in another project (View)
Unfortunately, this means that you would not be able to use anonymous types: anonymous types are generated with internal visibility, limiting their use to the assembly in which they are produced. You can use a work-around based on ExpandoObject described in this answer:
var q = afList.Select(x => {
dynamic res = new ExpandoObject();
res.EAN=x.EAN;
res.Title=x.title;
res.Quantity=x.qty;
return res;
});
Create a new class that represents the new object structure and return that.
var q = from x in afList
select new SmallerType { EAN=x.EAN, Title=x.title, Quantity=x.qty };
WinForm Function
foreach (SmallerType item in q)
{
//
}
You can use collection of dynamic objects instead of simple objects as return type of your method:
public IEnumerable<dynamic> stockEtatQty()
Then you will not have IntelliSense but at runtime properties will be found:
foreach (var item in sstock.stockEtatQty())
str += String.Format("{0}", item.EAN) + Environment.NewLine;
But I suggest you to create custom class with EAN, Title and Quantity properties. Or just use your allFields instead of anonymous objects.
Consider also to use StringBuilder for string creation to avoid creating lot of in-memory strings:
var builder = new StringBuilder();
foreach (var item in sstock.stockEtatQty())
builder.AppendFormat("{0}{1}", item.EAN, Environment.NewLine);
MessageBox.Show(builder.ToString());
I am having quite a lot of trouble trying to find a tutorial that will tech me how to use MySqlReader (with MySQL) to insert or receive data through a class object. The construtor is shown as follow.
Where I have used http://json2csharp.com/ to create these class I know how to serialize/deserialize a class object into JSON. my problem really lies in getting the data from a lot of different table in and out of the database connector via MySqlDataReader into a class object.
I don't know if this matter but I am using .Net 2.0 with C# 3.0 (visual studio 2010). Thank you in advance for any comment or reply.
public class Boardingzone
{
public string zone { get; set; }
public string time { get; set; }
}
public class Serial
{
public string rsn { get; set; }
public List<Boardingzone> boardingzone { get; set; }
}
public class RootObject
{
public string name { get; set; }
public List<Serial> serial { get; set; }
}
This is what i have tried so far
string sql = "SELECT Route.RName, RouteSerial.RSN, Stop.StopName, TimeTable.BoardTime FROM TimeTable INNER JOIN Stop ON TimeTable.StopID = Stop.StopID INNER JOIN RouteSerial ON TimeTable.RSN = RouteSerial.RSN INNER JOIN Route ON RouteSerial.RID = Route.RID";
MySqlCommand cmd = new MySqlCommand(sql, conn);
MySqlDataReader rdr = cmd.ExecuteReader();
ArrayList name = new ArrayList();
ArrayList rsn = new ArrayList();
ArrayList zone = new ArrayList();
ArrayList time = new ArrayList();
while (rdr.Read()){
string val = "";
val = rdr.GetValue(0).ToString();
name.Add(val);
val = rdr.GetValue(1).ToString();
rsn.Add(val);
val = rdr.GetValue(2).ToString();
zone.Add(val);
val = rdr.GetValue(3).ToString();
time.Add(val);
}
rdr.Close();
conn.Close();
you are almost doing it right. without going into too much detail, you need to instantiate the classes you want to populate:
var currentRoot = null;
var rootObjects = new List<RootObject>();
while (rdr.Read()){
string name= rdr.GetValue(0).ToString();
string serial = rdr.GetValue(1).ToString();
string bz = rdr.GetValue(2).ToString(); <-- made this up
if( currentRoot == null || currentRoot.name != name )
{
if( currentRoot != null ){
rootObjects.Add(currentRoot);
}
currentRoot = new RootObject();
currentRoot.name = name;
}
currentRoot.serials.Add(new Serial(){zone = bz});
... instantiate other classes
}
rdr.Close();
foreach( var rootObj in rootObjects) doSomethingWithRootObj(rootObj);
hope it helps.
I am not sure what the purpose of populating the ArrayList is, you will have to somehow populate your Model from your arraylist either by mapping or manually.
For the simple case why can you not just populate your model here?
One good approach to this would be to keep your Model in a separate assembly, your DAL can then reference this assembly in order to populate the appropriate classes.
Better still would be if you could expose your model as interfaces then the DAL need only use those interfaces and can have its own implementation - not sure if this is possible with the serialization method you use.