I am trying to do a tracker application in wp8. I want to save the values in database using Sqlite. All the values r working (time, distance and pace). After pressing Stop button, I want the values to be posted in a table view using Sqlite as u can see in the second screen. Any good suggestions or links are appreciated. Thank u.
Try this nokia developer site here.
Gives you a small tutorial how to use sqlite on windows phones.
This piece of code gives you the answer?
private void Insert_Click_1(object sender, RoutedEventArgs e)
{
// Create a new task.
Task task = new Task()
{
Title = TitleField.Text,
Text = TextField.Text,
CreationDate = DateTime.Now
};
/// Insert the new task in the Task table.
dbConn.Insert(task);
/// Retrieve the task list from the database.
List<Task> retrievedTasks = dbConn.Table<Task>().ToList<Task>();
/// Clear the list box that will show all the tasks.
TaskListBox.Items.Clear();
foreach (var t in retrievedTasks)
{
TaskListBox.Items.Add(t);
}
}
hm, i see this is a retrieval piece of code. Maybee this site helps you further.
The following example is an insert:
public void Initialize()
{
using ( var db = new SQLite.SQLiteConnection( _dbPath ) )
{
db.CreateTable<Customer>();
//Note: This is a simplistic initialization scenario
if ( db.ExecuteScalar<int>(
"select count(1) from Customer" ) == 0 )
{
db.RunInTransaction( () =>
{
db.Insert( new Customer() {
FirstName = "Jon", LastName = "Galloway" } );
db.Insert( new Customer() {
FirstName = "Jesse", LastName = "Liberty" } );
} );
}
else
{
Load();
}
}
}
I'm assuming your table is.
public class HistoryTable
{
public string date { get; set; }
public string time { get; set; }
public double pace { get; set; }
public double distance { get; set; }
}
Insert values using this statement.
string date = DateTime.Now.ToShortDateString();
string time = DateTime.Now.ToShortTimeString();
double pace = 16;
double distance = 4;
SQLiteConnection conn = new SQLiteConnection(System.IO.Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "Database.sqlite"));
conn.Execute("Insert into HistoryTable values(?,?,?,?)", date, time, pace, distance);
Fetch your data as below statement, I'm assuming that you know how to bind the data in listbox if there is need. I'm taking the values in textbox.
SQLiteConnection conn = new SQLiteConnection(System.IO.Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "Database.sqlite"));
var result = conn.Table<HistoryTable>();
foreach (var val in result)
{
TimeSpan pace = TimeSpan.FromMinutes(val.pace);
TextBoxDate.Text = val.date;
TextBoxTime.Text = val.time;
TextBoxPace.Text = pace.Minutes + ":" + pace.Seconds;
TextBoxDistance.Text = val.distance + " Km";
}
The reason why I used Pace as double because I can use this double value as total minutes and can be changed as timespan(in minutes and seconds). For any other queries you can ask any time.
To get exactly the same format as you ask in your question you should use like this also.
string date = string.Format("{0:00}.{1:00}.{2:00}", DateTime.Now.Day, DateTime.Now.Month, DateTime.Now.Year);
string time = DateTime.Now.ToString("HH:mm:ss");
double pace = 16.5;
double distance = 4;
SQLiteConnection conn = new SQLiteConnection(System.IO.Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "Database.sqlite"));
conn.Execute("Insert into HistoryTable values(?,?,?,?)", date, time, pace, distance);
At the time of fetching info, please change the above steps by this.
TextBoxDistance.Text = string.Format("{0:0.00}Km", val.distance);
Related
I'm developing an application in windows form,I have json data in my mysql database ,column = data
'{"money":0,"goal":0,"advantage":0}'
I'm trying to pull this data into tags in the form,label1.text="money";label2.text="goal";label3.text="advantage";
I could not find enough resources like this and I am inexperienced in this regard.
I uploaded the Json nouget package to my form.
output ='{"money":0,"goal":0,"advantage":0}'
How can I get json data as I want.
Thanks for your help.
MySqlConnection baglan = new MySqlConnection("Server=localhost;Database=json;user=root;Pwd=;");
private void Form1_Load(object sender, EventArgs e)
{
baglan.Open();
MySqlCommand kom = new MySqlCommand("SELECT * FROM player_accounts", baglan);
int count = Convert.ToInt32(kom.ExecuteScalar());
if (count != 0)
{
MySqlDataReader oku = kom.ExecuteReader();
while (oku.Read())
{
listBox1.Items.Add(oku["data"].ToString());
}
baglan.Close();
}
}
If the JSON data is always the same, the 1st thing I would create is a model class to house the data in C# land.
So, something like this::
public class Data
{
public decimal money { get; set; }
public int goal { get; set; }
public int advantage { get; set; }
}
Then, in your function, I would convert the JSON data to this new object and return from the function. For this you will need to go to nuget and install Newtonsoft.Json
Now you can write something like this:
var dbData = JsonConvert.DeserializeObject<Data>(oku["data"]);
instead of your line:
listBox1.Items.Add(oku["data"].ToString());
The function would look something like this:
private List<Data> GetDatabaseData()
{
var resultList = new List<Data>();
MySqlConnection baglan = new MySqlConnection("Server=localhost;Database=json;user=root;Pwd=;");
baglan.Open();
MySqlCommand kom = new MySqlCommand("SELECT * FROM player_accounts", baglan);
int count = Convert.ToInt32(kom.ExecuteScalar());
if (count != 0)
{
MySqlDataReader oku = kom.ExecuteReader();
while (oku.Read())
{
var data JsonConvert.DeserializeObject<Data>(ku["data"].ToString());
resultList.Add(data);
}
baglan.Close();
}
return resultList;
}
You can then use the function like this:
var dataList = GetDatabaseData();
foreach( var item in dataList )
{
listBox1.Items.Add(item.money);
}
Having written this question out, and created a MCVE, it sounds a bit like homework, but really it isn't...
I'm unfortunately too old to be set homework.
I'm trying to write a small app to populate a database I'm working on with a 'pyramid' structure.
There's one original member, who refers 10 members.
Each of these referrals can have 10 referrals. Each of those, has 10 referrals. And so on...
I'm trying to fill the database with a maximum number of members (which is supplied)
If I set the maximum number of members to 100,000 - this works.
200,000 however, throws a StackOverflow exception.
I'm pretty sure it's down to me not terminating the 'fanning out' early enough. But I can't for the life of me figure out where.
Note, my MCVE below is using Dapper for simplicity sake, hence the simple INSERT INTO statement
public class MemberPopulator
{
private readonly SqlConnection connection;
private const string MemberSql = #"
INSERT INTO Members (FirstName, LastName, ReferralId, Active, Created)
VALUES (#FirstName, #LastName, #ReferralId, #Active, #Created);
SELECT CAST(SCOPE_IDENTITY() as int)";
private int TotalMemberCount;
private const int MaxMemberCount = 200000;
public MemberPopulator()
{
connection = new SqlConnection("Data Source=localhost;Initial Catalog=MyTestDb;Integrated Security=True");
}
public void CreateMembers()
{
//clear members
connection.Execute("TRUNCATE TABLE Members");
//create the 'original' member (top of pyramid)
var originalMemberId = connection.Query<int>(MemberSql, new Member
{
FirstName = "FirstName Goes Here",
ReferralId = 0
}).Single();
//now we have 1 total members
TotalMemberCount = 1;
//recursively create members, starting with original member,
RecursiveCreate(new[] { originalMemberId });
}
private void RecursiveCreate(IEnumerable<int> referralMemberIds)
{
//don't recurse if we've already got enough members
if (TotalMemberCount >= MaxMemberCount)
return;
foreach (var referralId in referralMemberIds)
{
//Create 10 members
var refs = CreateReferredMembers(referralId, 10);
RecursiveCreate(refs);
}
}
private IEnumerable<int> CreateReferredMembers(int referralId, int numberOfReferrals)
{
var referredMemberIds = new List<int>();
for (var i = 0; i < numberOfReferrals; i++)
{
if (TotalMemberCount >= MaxMemberCount)
break;
var member = new Member
{
FirstName = "FirstName Goes Here",
ReferralId = referralId
};
var memberId = connection.Query<int>(MemberSql, member).Single();
referredMemberIds.Add(memberId);
TotalMemberCount++;
}
return referredMemberIds;
}
}
The stack in C# is set to 1MB for 32bit applications or 4MB for 64bit applications by default. This is suitable for most applications. In case you need more please follow the guidance in the net (for example this one).
In case you do not know exactly the level of recursion I would suggest to simulate recursion by using a Stack or Queue datatype.
public class MemberPopulator
{
private readonly SqlConnection connection;
private const string MemberSql = #"
INSERT INTO Members (FirstName, LastName, ReferralId, Active, Created)
VALUES (#FirstName, #LastName, #ReferralId, #Active, #Created);
SELECT CAST(SCOPE_IDENTITY() as int)";
private int TotalMemberCount;
private const int MaxMemberCount = 200000;
public MemberPopulator()
{
connection = new SqlConnection("Data Source=localhost;Initial Catalog=MyTestDb;Integrated Security=True");
}
public void CreateMembers()
{
//clear members
connection.Execute("TRUNCATE TABLE Members");
//create the 'original' member (top of pyramid)
var originalMemberId = connection.Query<int>(MemberSql, new Member
{
FirstName = "FirstName Goes Here",
ReferralId = 0
}).Single();
//now we have 1 total members
TotalMemberCount = 1;
//recursively create members, starting with original member,
NonRecursiveCreate(originalMemberId);
}
private void NonRecursiveCreate(int root)
{
Queue<int> members = new Queue<int>();
members.Enqueue(root);
while (members.Any() && TotalMemberCount < MaxMemberCount)
{
var referralId = members.Dequeue();
//Create 10 members
var refs = CreateReferredMembers(referralId, 10);
foreach (int i in refs)
{
members.Enqueue(i);
}
}
}
private IEnumerable<int> CreateReferredMembers(int referralId, int numberOfReferrals)
{
var referredMemberIds = new List<int>();
for (var i = 0; i < numberOfReferrals; i++)
{
if (TotalMemberCount >= MaxMemberCount)
break;
var member = new Member
{
FirstName = "FirstName Goes Here",
ReferralId = referralId
};
var memberId = connection.Query<int>(MemberSql, member).Single();
referredMemberIds.Add(memberId);
TotalMemberCount++;
}
return referredMemberIds;
}
}
I am using Google.Apis.AdSense to get AdSense Data from C# windows form..
i can get adsense info and Metric Report, using Metric i can optians some values such as
["PAGE_VIEWS",
"INDIVIDUAL_AD_IMPRESSIONS",
"CLICKS","AD_REQUESTS_CTR",
"INDIVIDUAL_AD_IMPRESSIONS_CTR",
"COST_PER_CLICK","AD_REQUESTS_RPM",
"INDIVIDUAL_AD_IMPRESSIONS_RPM",
"AD_REQUESTS_COVERAGE","EARNINGS"]
here a code for this process:
public AdsenseReportsGenerateResponse GenerateReport(Account adSenseAccount, DateTime startDate, DateTime endDate)
{
var reportRequest = service.Accounts.Reports.Generate(
adSenseAccount.Id, startDate.ToString(DateFormat), endDate.ToString(DateFormat));
reportRequest.Metric = new List<string>
{
"PAGE_VIEWS","INDIVIDUAL_AD_IMPRESSIONS", "CLICKS","AD_REQUESTS_CTR","INDIVIDUAL_AD_IMPRESSIONS_CTR",
"COST_PER_CLICK","AD_REQUESTS_RPM","INDIVIDUAL_AD_IMPRESSIONS_RPM", "AD_REQUESTS_COVERAGE","EARNINGS"
};
reportRequest.Dimension = new List<string> { "DATE" };
reportRequest.Sort = new List<string> { "+DATE" };
// Run report.
var reportResponse = reportRequest.Execute();
ReportUtils.FillGapsDates(reportResponse, startDate, endDate);
if (!reportResponse.Rows.IsNullOrEmpty())
{
ReportUtils.DisplayHeaders(reportResponse.Headers);
Console.WriteLine("Showing data from {0} to {1}", reportResponse.StartDate, reportResponse.EndDate);
return reportResponse;
}
else
{
return null;
}
}
First issue:
Although the data are not identical, they are similar. Why?
Second issue (important):
How or where can i get CTR Value?
Currently, we calculated the following equation:
INDIVIDUAL_AD_IMPRESSIONS_CTR * 2 * 100
Is this correct?
I'm working in C# (.Net 4) and I am trying to do several things:
I have 2 files ("Offline.csv","online.csv"), and I'm having those files make one "master" file (called "Attendance.csv")
Both offline.csv and online.csv contain similar data---
My Offline.csv file has:
(ID),(TimeInMin),(DateWithoutSlashes yyymmdd)
01,10,20151201
01,05,20151202
02,11,20151201
03,11,20151202
My Online.csv file has
(ID),(TimeInMin),(DateWithoutSlashes yyymmdd)
01,70,20151201
02,20,20151202
03,22,20151202
After my program is ran, the Attendance.csv should look something like:
(Same headers)
01,80,20151201
01,05,20121502 (notice the date from offline.csv, which doesn't exist in the online.csv)
02,31,20151201
03,33,20151202
So what I'm trying to do is:
Compare the data from both the offline.csv and online.csv files. If data matches on the "ID" and "Date" columns, add the minutes together (column 2) and put them as a row in the Attendance.csv file
However, IF the offline.csv contains rows that the online.csv doesn't have, then put all those other records into the Attendance.csv on their own. Perform the same action with the online.csv, being mindful to not duplicate minutes that were already merged together from step #1
I don't know if that all makes sense, but I hope it does :X
I have been beating my head against the wall all day with this, and I don't know what else to look at.
With all that said, here is what I have so far:
I have created my own class, called "aoitime", it looks as follows:
public class aoitime
{
public string ID { get; set; }
public string online { get; set; }
public string offline { get; set; }
public string dtonline { get; set; }
public string dtoffline { get; set; }
public string date { get; set; }
}
I then use IEnumerable in a different function, looks similar to ...
IEnumerable<aoitime> together =
from online in onlinefile
let onlineFields = online.Split(',')
from id in offlinefile
let offlineFields = id.Split(',')
where (onlineFields[0] == offlineFields[0] && onlineFields[2] == offlineFields[2]) || (!offlineFields[1].Contains(""))
orderby onlineFields[0]
select new aoitime
{
ID = onlineFields[0],
online = onlineFields[1],
offline = offlineFields[1],
dtonline = onlineFields[2],
dtoffline = offlineFields[2],
date = onlineFields[2]
};
StreamWriter Attendance = new StreamWriter(destination);
Attendance.Write("SIS_NUMBER,MINUTES,DATE" + Environment.NewLine);
foreach (aoitime att in together)
{
int date = int.Parse(att.date);
int dateonline = int.Parse(att.dtonline);
int dateoffline = int.Parse(att.dtoffline);
int online = int.Parse(att.online);
int offline = int.Parse(att.offline);
int total = (online + offline);
Console.WriteLine("Writing total time now: "+online);
Attendance.Write(att.ID + "," + total + "," date + Environment.NewLine);
}
I then tried creating another IEnumerable class spawn that looks similar to the one above, but instead using "where offlineFields[2] != onlineFields[2]" but I get unpredictable results. I just don't know where else to look or what else to do.
Please be gentle, I'm very much new to programming in general (I promise this isn't for a classroom assignment :-)
thanks so much for any advice and reading this book!
You are almost there. I wrote this code, so hopefully you will be able to learn something from it.
First you only need one entity class for this. Note the ToString method. You will see how it's used later.
public class Attendance
{
public int Id { get; set; }
public int TimeInMinutes { get; set; }
public string Date { get; set; }
public override string ToString()
{
return string.Format("{0},{1},{2}", Id, TimeInMinutes, Date);
}
}
Now the code to parse your files and create the new file. Read my comments in the code.
var onlineEntries = File.ReadAllLines(#"c:\online.txt");//read online file
var validOnlineEntries = onlineEntries.Where(l => !l.Contains("(")); //remove first line
var onlineRecords = validOnlineEntries.Select(r => new Attendance()
{
Id = int.Parse(r.Split(new[] {","}, StringSplitOptions.None)[0]),
TimeInMinutes = int.Parse(r.Split(new[] {","}, StringSplitOptions.None)[1]),
Date = r.Split(new[] {","}, StringSplitOptions.None)[2],
}).ToList();//popultae Attendance class
var offlineEntries = File.ReadAllLines(#"c:\offline.txt"); //read online file
var validOfflineEntries = offlineEntries.Where(l => !l.Contains("(")); //remove first line
var offlineRecords = validOfflineEntries.Select(r => new Attendance()
{
Id = int.Parse(r.Split(new[] { "," }, StringSplitOptions.None)[0]),
TimeInMinutes = int.Parse(r.Split(new[] { "," }, StringSplitOptions.None)[1]),
Date = r.Split(new[] { "," }, StringSplitOptions.None)[2],
}).ToList();//popultae Attendance class
var commonRecords = (from n in onlineRecords
join f in offlineRecords on new {n.Date, n.Id } equals new {f.Date, f.Id} //if Date and Id are equal
select new { n.Id, TimeInMinutes = (n.TimeInMinutes + f.TimeInMinutes), n.Date }).OrderBy(x => x.Id).Distinct().ToList(); //add Online and Off line time
var newRecords = commonRecords.Select(r => new Attendance()
{
Id = r.Id,
TimeInMinutes = r.TimeInMinutes,
Date = r.Date,
}); //Poulate attendance again. So we can call toString method
onlineRecords.AddRange(offlineRecords); //merge online and offline
var recs = onlineRecords.Distinct().Where(r => !newRecords.Any(o => o.Date == r.Date && o.Id == r.Id)).ToList(); //remove already added items from merged online and offline collection
newRecords.AddRange(recs);//add filtered merged collection to new records
newRecords = newRecords.OrderBy(r => r.Id).ToList();//order new records by id
File.WriteAllLines(#"C:\newFile.txt", newRecords.Select(l => l.ToString()).ToList()); //write new file.
Just to add this as an answer, I am selecting #Kosala-w's suggestion as an answer. My code now looks very identical to what he posted, except I modified the ID to a string format because the integers used for the IDs are pretty lenghty.
I thank both people who answered this question, and I appreciate the SO community! Have a good day :-)
public class Attendance
{
public string Id { get; set; }
public int TimeInMinutes { get; set; }
public int Code { get; set; }
public string Date { get; set; }
public override string ToString()
{
return string.Format("{0},{1},{2}", Id, TimeInMinutes, Date);
}
}
I also have more rows that I have to handle in the Attendance sheet than I stated in my original question (I didn't worry about those because I wasn't concerned that I'd have a hard time getting what I needed.)
Anyway, the code below is what I used, again, thanks Kosala.
private void createAttendance()
{
try
{
txtStatus.ResetText();
txtStatus.Text += "Creating Attendance file. Please wait.";
string destination = (#"C:\asdf\Attendance.csv");
barStatus.Caption = "Processing Attendance file. Please wait.";
if (File.Exists(destination))
File.Delete(destination);
var validOnlineEntries = File.ReadAllLines(#"C:\asdf\online.csv");//read online file
//var validOnlineEntries = onlineEntries.Where(l => !l.Contains("(")); //remove first line
var onlineRecords = validOnlineEntries.Select(r => new Attendance()
{
Id = (r.Split(new[] { "," }, StringSplitOptions.None)[0] + ",202" + "," + txtYear.Text),
TimeInMinutes = int.Parse(r.Split(new[] { "," }, StringSplitOptions.None)[1]),
Date = r.Split(new[] { "," }, StringSplitOptions.None)[2],
}).ToList();//populate Attendance class
var validOfflineEntries = File.ReadAllLines(#"C:\asdf\offline.csv"); //read online file
//var validOfflineEntries = offlineEntries.Where(l => !l.Contains("(")); //remove first line
var offlineRecords = validOfflineEntries.Select(r => new Attendance()
{
Id = (r.Split(new[] { "," }, StringSplitOptions.None)[0] + ",202" + "," + txtYear.Text),
TimeInMinutes = int.Parse(r.Split(new[] { "," }, StringSplitOptions.None)[1]),
Date = r.Split(new[] { "," }, StringSplitOptions.None)[2],
}).ToList();//populate Attendance class
var commonRecords = (from n in onlineRecords
join f in offlineRecords on new { n.Date, n.Id } equals new { f.Date, f.Id } //if Date and Id are equal
select new { n.Id, TimeInMinutes = (n.TimeInMinutes + f.TimeInMinutes), n.Date }).OrderBy(x => x.Id).Distinct().ToList(); //add Online and Off line time
var newRecords = commonRecords.Select(r => new Attendance()
{
Id = r.Id,
TimeInMinutes = r.TimeInMinutes,
Date = r.Date,
}).ToList(); //Populate attendance again. So we can call toString method
onlineRecords.AddRange(offlineRecords); //merge online and offline
var recs = onlineRecords.Distinct().Where(r => !newRecords.Any(o => o.Date == r.Date && o.Id == r.Id)).ToList(); //remove already added items from merged online and offline collection
newRecords.AddRange(recs);//add filtered merged collection to new records
newRecords = newRecords.OrderBy(r => r.Id).ToList();//order new records by id
StreamWriter Attendance = new StreamWriter(destination);
//Attendance.Write("SIS_NUMBER,SCHOOL_CODE,SCHOOL_YEAR,ABSENCE_DATE,ABSENCE_REASON1,ABSENCE_REASON2,MINUTES_ATTEND,NOTE,ABS_FTE1,ABS_FTE2" + Environment.NewLine);
Attendance.Write("SIS_NUMBER,SCHOOL_CODE,SCHOOL_YEAR,MINUTES_ATTEND,ABSENCE_DATE,ABSENCE_REASON2,ABSENCE_REASON1,NOTE,ABS_FTE1,ABS_FTE2" + Environment.NewLine);
Attendance.Dispose();
File.AppendAllLines(destination, newRecords.Select(l => l.ToString()).ToList()); //write new file.
Convert_CSV_To_Excel();
}
catch(Exception ex)
{
barStatus.Caption = ("ERROR: "+ex.Message.ToString());
}
}
I plan to do some more fine tuning, but this sure got me in the right direction!
The first thing that I'd do is define a simpler class to hold your aoitimes. For example:
public class aoitime
{
public string ID { get; set; }
public int TimeInMinutes { get; set; }
public string DateWithoutSlashes { get; set; }
}
Then, you'll want to parse the string from the csv file into that class. I figure that that's an implementation detail that you can probably figure out on your own. If not, leave a comment and I can post more details.
Next, the tricky part is that you want not only a join, but you want the exceptions as well. The join logic is fairly simple:
var matches = from offline in offlineItems
join online in onlineItems
on
new {offline.ID, offline.DateWithoutSlashes} equals
new {online.ID, online.DateWithoutSlashes}
select new aoitime
{
ID = offline.ID,
TimeInMinutes = offline.TimeInMinutes + online.TimeInMinutes,
DateWithoutSlashes = offline.DateWithoutSlashes
};
(Notice there that you're using anonymous objects in the "ON" join condition). But the hard part is how to get the exceptions. LINQ is set up to do inner joins or equijoins, but I'm not sure about outer joins. At least I haven't seen it.
So one solution might be to use the LINQ join to get the matches and then another LINQ query to get those that don't match and then combine those two collections and write them out to a file.
Another solution might be to go back to basics and do the iteration logic yourself. LINQ is just elegant iteration logic and if it doesn't do what you need it to do, you might need to do it yourself.
For example, let's say that you have your collection of online and offline items and you want to iterate through them and do the comparison:
List<aoitime> offlineItems = <some method that produces this list>
List<aoitime> onlineItems = <some method that produces this list>
List<aoitime> attendanceItems = new List<aoitime>();
//For simplicity, assuming that you have the same number of elements in each list
for (int i = 0; i < offlineItems.Count; i++)
{
aoitime offline = offlineItems[i];
aoitime online = onlineItems[i];
if(offline.ID == online.ID && offline.DateWithoutSlashes = online.DateWithoutSlashes)
{
//Create your new object and add it to the attendance items collection.
}
else
{
//Process the exceptions and add them individually to the attendance items collection.
}
}
So you do the iteration and processing yourself and have control over the whole process. Does that make sense? If not, let me know in a comment and I can add more.
I'm trying to format XML from a MySQL query to emulate what a client frontend is expecting for input. I have no control over what the client requires, so I have to match what I've gotten from Wireshark captures. I am not married to the idea of adding columns to the dataset to do this, and I can probably just do a search and replace for the additions to the XML, however, I have a large number of very similar, yet different queries & outputs to write, and I'd prefer to do something that scales well. Unfortunately it'll be throw away code because when I write the new front end client for this, we won't be tracking a lot of the data the current legacy system does like client IP address, or the supposedly unique "ActionID" both of which you'll see referenced below, nor will I have to do anything with XML, it'll all be MySQL driven queries.
My output should be in a form like this:
<PCBDatabaseReply>
<SearchResult>
<SBE_PCB_Data PCBID="53">
<Termination ActionID="97DF" User="UName:192.168.255.255" Date="2012-09-26T13:15:51" PCBID="53">
<Reason>Other</Reason>
</Termination>
</SBE_PCB_Data>
</SearchResult>
</PCBDatabaseReply>
The results from my query look like this:
EventType User Date PCBID Reason
Termination UName 2012-09-26T13:15:51 53 Other
My output XML currently looks like this:
<PCBDatabaseReply>
<Termination User="UName" Date="2012-09-26T13:15:51" PCBID="53">
<EventType>Termination</EventType>
<Reason>Other</Reason>
</Termination>
</PCBDatabaseReply>
Using this code:
string mysqlConnection = "server=server;\ndatabase=database;\npassword=password;\nUser ID=user;";
MySqlConnection connection = new MySqlConnection(mysqlConnection);
connection.Open();
string command = "SELECT eventtypes.EventType, events.User, DATE_FORMAT(events.DateTime,'%Y-%m-%dT%T') AS Date, pcbid.PCBID, getReasons.ItemValue AS Reason " +
"FROM events " +
"INNER JOIN pcbid ON events.PCBID = pcbid.PCBID " +
"INNER JOIN eventtypes " +
"ON events.EventType_ID = eventtypes.EventType_ID " +
"LEFT JOIN getReasons " +
"ON getReasons.Event_ID = events.Event_ID " +
"WHERE eventtypes.EventType = 'termination'";
//create fake "ActionID"
var random = new Random();
string ActionID = String.Format("{0}\"{1:X4}\"", "ActionID=", random.Next(0xffff));
MySqlDataAdapter adapter = new MySqlDataAdapter(command, connection);
DataSet dataSet = new DataSet();
adapter.Fill(dataSet);
//change upper level node name to what's expected in client-speak
dataSet.DataSetName = "PCBDatabaseReply";
//change first child node name to client-speak eventType
dataSet.Tables[0].TableName = dataSet.Tables[0].Rows[0][0].ToString();
StringWriter writer = new StringWriter();
var ds1 = dataSet.Tables[0];
DataColumn dcEventType = ds1.Columns[0];
DataColumn dcUser = ds1.Columns[1];
DataColumn dcDate = ds1.Columns[2];
DataColumn dcPCBID = ds1.Columns[3];
dcEventType.ColumnMapping = MappingType.Element;
dcUser.ColumnMapping = MappingType.Attribute;
dcDate.ColumnMapping = MappingType.Attribute;
dcPCBID.ColumnMapping = MappingType.Attribute;
dataSet.Tables[0].WriteXml(writer, true);
Console.WriteLine(writer.ToString());
I need to inject several things
At the top beneath <PCBDatabaseReply>:
<SearchResult>
<SBE_PCB_Data PCBID="53">
In the Termination tag: (from the fake ActionID in the code)
ActionID="0xnnnn" & append ":192.168.255.255" to the end of the user name
And then close with the appropriate tags:
</SBE_PCB_Data>
</SearchResult>
I have tried adding a dummy column for the "SBE_PCB_Data" tag, which didn't work.
DataColumn dcSBE_PCB_Data = new DataColumn("SBE_PCB_Data", System.Type.GetType("System.String"), "SBE_PCB_Data", MappingType.Element);
dcSBE_PCB_Data.DefaultValue = "SBE_PCB_Data";
//add to the dataset
dataSet.Tables[0].Columns.Add(dcSBE_PCB_Data);
//move it to the zeroth position
dcSBE_PCB_Data.SetOrdinal(0);
This just makes it show up as:
<SBE_PCB_Data>SBE_PCB_Data</SBE_PCB_Data>
I need it to wrap around the rest of the XML as an ancestor node.
How best to inject the XML I need into the results?
EDIT: refactored according to excellent example below
**EDIT: updated with final code
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Xml.Linq;
using MySql.Data.MySqlClient;
namespace TerminationResults
{
public class SearchResult
{
//all possible event detail tags (test items are excluded)
public string EventType { get; set; }
public string User { get; set; }
public string Date { get; set; }
public string PCBID { get; set; }
public string EAReason { get; set; }
public string ETReason { get; set; }
public string Notes { get; set; }
public string Reason { get; set; }
public string SBEJobNumber { get; set; }
public string SBEModelNumber { get; set; }
public string SBEPN { get; set; }
public string SBESerialNumber { get; set; }
//create fake IP address since we no longer track it
public string UserAndIP
{
get { return String.Format("{0}:192.168.255.255", User); }
set {}
}
//create fake actionID since the originals weren't inserted into the database because they weren't unique.
public string ActionId
{
get { return String.Format("{0:X4}", new Random().Next(0xffff)); }
set {}
}
}
internal class Program
{
private static void Main(string[] args)
{
var searchResults = GetSearchResults();
var xml = TransformList(searchResults);
Console.WriteLine(xml);
Console.ReadLine();
}
public static IEnumerable<SearchResult> GetSearchResults()
{
List<SearchResult> searchResults = new List<SearchResult>();
try
{
const string mysqlConnection = #"server=server;
database=database;
password=password;
User ID=username;";
MySqlConnection conn = new MySqlConnection(mysqlConnection);
conn.Open();
using (conn)
{
string cmd = #"SELECT eventtypes.EventType, events.User,
DATE_FORMAT(events.DateTime,'%Y-%m-%dT%T') AS Date,
pcbid.PCBID,
getEAReasons.ItemValue AS EAReason,
getETReasons.ItemValue AS ETReason,
getReasons.ItemValue AS Reason,
getNotes.ItemValue AS Notes,
getSBEJobNumbers.ItemValue AS SBEJobNumber,
getSBEModelNumbers.ItemValue AS SBEModelNumber,
getSBEPNs.ItemValue as SBEPN,
getSBESerialNumbers.ItemValue as SBESerialNumber
FROM events
INNER JOIN pcbid ON events.PCBID = pcbid.PCBID
INNER JOIN eventtypes
ON events.EventType_ID = eventtypes.EventType_ID
LEFT JOIN getEAReasons
ON getEAReasons.Event_ID = events.Event_ID
LEFT JOIN getETReasons
ON getETReasons.Event_ID = events.Event_ID
LEFT JOIN getReasons
ON getReasons.Event_ID = events.Event_ID
LEFT JOIN getNotes
ON getNotes.Event_ID = events.Event_ID
LEFT JOIN getSBEJobNumbers
ON getSBEJobNumbers.Event_ID = events.Event_ID
LEFT JOIN getSBEModelNumbers
ON getSBEModelNumbers.Event_ID = events.Event_ID
LEFT JOIN getSBEPNs
ON getSBEPNs.Event_ID = events.Event_ID
LEFT JOIN getSBESerialNumbers
ON getSBESerialNumbers.Event_ID = events.Event_ID
WHERE eventtypes.EventType = 'termination'";
try
{
using (MySqlDataAdapter adapter = new MySqlDataAdapter(cmd, conn))
{
DataSet dataSet = new DataSet();
adapter.Fill(dataSet);
DataTable ds = dataSet.Tables[0];
for (int row = 0; row < ds.Rows.Count; row++ )
{
SearchResult result = new SearchResult()
{
EventType = ds.Rows[row]["EventType"].ToString(),
User = ds.Rows[row]["User"].ToString(),
Date = ds.Rows[row]["Date"].ToString(),
PCBID = ds.Rows[row]["PCBID"].ToString(),
EAReason = ds.Rows[row]["EAReason"].ToString().Any() ? ds.Rows[row]["EAReason"].ToString() : null,
ETReason = ds.Rows[row]["ETReason"].ToString().Any() ? ds.Rows[row]["ETReason"].ToString() : null,
Notes = ds.Rows[row]["Notes"].ToString().Any() ? ds.Rows[row]["Notes"].ToString() : null,
Reason = ds.Rows[row]["Reason"].ToString().Any() ? ds.Rows[row]["Reason"].ToString() : null,
SBEJobNumber = ds.Rows[row]["SBEJobNumber"].ToString().Any() ? ds.Rows[row]["SBEJobNumber"].ToString() : null,
SBEModelNumber = ds.Rows[row]["SBEModelNumber"].ToString().Any() ? ds.Rows[row]["SBEModelNumber"].ToString() : null,
SBEPN = ds.Rows[row]["SBEPN"].ToString().Any() ? ds.Rows[row]["SBEPN"].ToString() : null,
SBESerialNumber = ds.Rows[row]["SBESerialNumber"].ToString().Any() ? ds.Rows[row]["SBESerialNumber"].ToString() : null
};
searchResults.Add(result);
}
}
}
catch (MySqlException ex)
{
Console.WriteLine(ex);
}
catch(Exception ex)
{
Console.WriteLine(ex);
}
}
}
catch (MySqlException ex)
{
Console.WriteLine(ex);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
return searchResults;
}
public static XElement TransformSearchResult (SearchResult result)
{
return new XElement("SBE_PCB_Data",
new XAttribute("PCBID", result.PCBID),
new XElement(result.EventType,
new XAttribute("ActionID", result.ActionId),
new XAttribute("User", result.UserAndIP),
new XAttribute("Date", result.Date),
new XAttribute("PCBID", result.PCBID),
result.EAReason == null ? null : new XElement("EAReason", result.EAReason),
result.ETReason == null ? null : new XElement("ETReason", result.ETReason),
result.Reason == null ? null : new XElement("Reason", result.Reason),
result.Notes == null ? null : new XElement("Note", result.Notes),
result.SBEJobNumber == null ? null : new XElement("SBEJobNumber", result.SBEJobNumber),
result.SBEModelNumber == null ? null : new XElement("SBEModelNumber", result.SBEModelNumber),
result.SBEPN == null ? null : new XElement("SBEPN", result.SBEPN),
result.SBESerialNumber == null ? null : new XElement("SBESerialNumber", result.SBESerialNumber)
)
);
}
public static XElement TransformList (IEnumerable<SearchResult> listOfResults)
{
return new XElement("PCBDatabaseReply",
new XElement("SearchResult",
from r in listOfResults
select TransformSearchResult(r)));
}
}
}
Had to do some tweaking to get this to run, but the concept is sound, and I like that it's extensible. It doesn't quite give the right output yet, but I can tweak that as well.
Ok, Let's refactor this.
Lets not try and do this directly from your dataset, you are trying to do to many things in your method here, it's messy hard to maintain and very hard to unit test.
The first thing we should do is create a SearchResult class that we can work with more easily, this is also a convenient place to put in our Business rules (Ip added to User and random ActionId) it also means that we can easily mock up data into this class without having to hit the database, we can then test our transform logic as a unit test, not an integration test (which are slower, and have more dependencies)
public class SearchResult
{
public string EventType {get ;set;}
public string User {get ; set;}
public DateTime Date {get;set;}
public int PCBID {get;set;}
public string Reason {get;set;}
public string UserAndIP
{
get
{
return String.Format("{0}:192.168.255.255",User);
}
}
public string ActionId
{
get
{
return String.Format("{0:X4}", new Random().Next(0xffff));
}
}
}
So lets rewrite the query to now populate a list of SearchResult's instead of a dataset
public IEnumerable<SearchResult> GetSearchResults()
{
using(var conn = GetYourConnection())
{
conn.open();
using(var cmd = conn.CreateCommand())
{
cmd.CommandText = GetYourQueryString();
using(var reader = cmd.ExecuteReader())
{
while(reader.Read())
{
var result = new SearchResult
{
.... populate from reader...
}
yield return result;
}
}
}
}
}
So now that we have a SearchResult class and a query method that gives us a list of them, lets transform that to your required XML.
Firstly, I'll make some assumtions that are not 100% clear from your question. (if these are not correct, it will be easy enough to modify)
I'll assume that we are creating a search result tag for each search
result returned from our query. And that these will be contained in
the PCBDatabaseReply tag.
The xml tag "Termination" is the value of the Event Type, so I'll
assume that tag should be the EventType value.
Lets use Linq to XML to create the XML from the list of SearchResults
Firstly We'll create a method that transforms individual SearchResults (the contents of the SearchResult tag)
public XElement TransformSearchResult(SearchResult result)
{
return new XElement("SearchResult",
new XElement("SBE_PCB_Data", new XAttribute("PCBID", result.PCBID)),
new XElement(result.EventType,
new XAttribute("ActionID", result.ActionId),
new XAttribute("User", result.UserAndIP),
new XAttribute("Date", result.Date),
new XAttribute("PCBID", result.PCBID)),
new XElement("Reason", result.Reason));
}
Secondly we'll create the method to transform the list
public XElement TransformList(IEnumerable<SearchResult> listOfResults)
{
return new XElement("PCBDatabaseReply",
from r in listOfResults
select TransformSearchResult(r));
}
Now our main calling method simply becomes...
var searchResults = GetSearchResults();
var xml = TransformList(searchResults);