January 31st, 2009
Have you ever tried to use .NET Xml Serialization with a Hashtable or Dictionary only to discover that you get a fun error that says you can’t serialize objects that implement IDictionary? I wasn’t too thrilled. I think I’m spoiled, because these days I’m addicted to using XmlSerializer and it usually just works. Until now.
All I wanted was to store a quick little XML file structured like so: (though a lot larger)
<?xml version="1.0" encoding="utf-8" ?>
<Fields>
<Field>
<Position>1</Position>
<Name>Account Number</Name>
<Required>true</Required>
</Field>
<Field>
<Position>2</Position>
<Name>Address Line 1</Name>
<Required>true</Required>
</Field>
<Field>
<Position>3</Position>
<Name>Address Line 2</Name>
<Required>false</Required>
</Field>
<Field>
<Position>4</Position>
<Name>City</Name>
<Required>true</Required>
</Field>
<Field>
<Position>5</Position>
<Name>State</Name>
<Required>true</Required>
</Field>
<Field>
<Position>6</Position>
<Name>Zip</Name>
<Required>true</Required>
</Field>
</Fields>
Then I wanted to read it into a class that had a generic Dictionary. The key would be the position, and the value would be an object that stores the name and required fields (there are actually a couple more, but I’m keeping it simple here). Honestly, should that be so hard? I want to be able to find the field at any position easily using the position as the key.
Here’s what I came up with. It works for this class, but since I’m overriding ReadXml and WriteXml, it obviously isn’t very extensible or pretty. Also, technically IXmlSerializable isn’t for public consumption, though I think enough people have cried that it might be fine to use now. In my opinion, this is rather breakable. I can’t say I’d use it for much more than this, but maybe someone else will get some use or a hint for their own utility class here.
This is fully testable code, as long as you change the file path and have the xml file there. There is no error trapping or anything, this code is just to demo the concept. Have fun!
using System;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using System.IO;
using System.Text;
using System.Diagnostics;
namespace ConsoleApplication1
{
public class Program
{
public class Field
{
public string Name;
public string Required;
}
public class Fields : IXmlSerializable
{
public Dictionary<int, Field> fieldList;
public Fields()
{
fieldList = new Dictionary<int, Field>();
}
public void WriteXml(XmlWriter writer)
{
writer.WriteRaw(Environment.NewLine);
foreach (int key in fieldList.Keys)
{
writer.WriteStartElement("Field");
writer.WriteRaw(Environment.NewLine);
writer.WriteStartElement("Position");
writer.WriteString(key.ToString());
writer.WriteEndElement();
writer.WriteRaw(Environment.NewLine);
writer.WriteStartElement("Name");
writer.WriteString(fieldList[key].Name);
writer.WriteEndElement();
writer.WriteRaw(Environment.NewLine);
writer.WriteStartElement("Required");
writer.WriteString(fieldList[key].Required);
writer.WriteEndElement();
writer.WriteRaw(Environment.NewLine);
writer.WriteEndElement();
writer.WriteRaw(Environment.NewLine);
}
}
public void ReadXml(XmlReader reader)
{
// start with the first main node
XmlNodeType type = reader.MoveToContent();
if (type == XmlNodeType.Element && reader.LocalName == "Fields")
{
// read in each Field element
reader.ReadStartElement("Fields");
while (type == XmlNodeType.Element && reader.LocalName == "Field")
{
reader.ReadStartElement("Field");
Field field = new Field();
int position = reader.ReadElementContentAsInt("Position", "");
field.Name = reader.ReadElementContentAsString("Name", "");
field.Required = reader.ReadElementContentAsString("Required", "");
fieldList.Add(position, field);
reader.ReadEndElement();
}
}
}
public XmlSchema GetSchema()
{
return (null);
}
}
static void Main(string[] args)
{
// let's take it for a test drive with no file
// using the debug output console to display the XML string
Trace.Listeners.Clear();
Trace.Listeners.Add(new DefaultTraceListener());
// construct an object
Fields fields = new Fields();
Field field = new Field();
field.Name = "Account Number";
field.Required = "true";
fields.fieldList.Add(1, field);
Field field2 = new Field();
field2.Name = "Address Line 1";
field2.Required = "true";
fields.fieldList.Add(2, field2);
// serialize it out to the debug window
XmlSerializer serializer = new XmlSerializer(typeof(Fields));
StringBuilder sb = new StringBuilder();
serializer.Serialize(new StringWriter(sb), fields);
Debug.WriteLine(sb.ToString());
// now read it back in (set a breakpoint to verify it reads it)
Fields fields2 = serializer.Deserialize(new StringReader(sb.ToString())) as Fields;
// let's try it with the file now (set a breakpoint to verify it reads it)
Fields fields3 = serializer.Deserialize(new StringReader(File.ReadAllText(@"C:\XMLFile1.xml"))) as Fields;
// just for giggles
Debug.WriteLine(fields3.fieldList[3].Name);
}
}
}
Tags: csharp, IXmlSerializable, XML Serialization
Posted in .NET | No Comments »
November 17th, 2008
The results of my messing around with .NET DateTime. Hope this is useful for someone else!
using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
// fun with DateTime
// right now, including the time
Console.WriteLine(DateTime.Now);
// today's date, no time (midnite)
Console.WriteLine(DateTime.Today);
// stripping a time off a DateTime (so it's midnite, useful for database queries)
DateTime startDate = DateTime.Parse("10/31/2008 5:25 pm"); // pretend this came from a database or something
Console.WriteLine(startDate); // see the time displayed
startDate = startDate.Date; // this is just the date portion with no time
Console.WriteLine(startDate); // see, just has the date now
// difference, in number of days, between two dates, human-logic, that is, the 5th is 2 days after the 3rd, no matter what time it was
DateTime endDate = DateTime.Parse("11/03/2008 4:25 pm"); // pretend this came from a database or something, too
int days = (startDate.Date - endDate.Date).Duration().Days; // absolute value, order of start and end dates is not important
Console.WriteLine(days);
int days2 = (startDate.Date - endDate.Date).Days; // real value, order of start and end dates IS important
Console.WriteLine(days2);
// difference between dates with time portion, such as .25 hours (time and attendance, payroll)
startDate = DateTime.Parse("11/17/2008 8:00 am");
endDate = DateTime.Parse("11/17/2008 4:45 pm");
TimeSpan interval = endDate - startDate; // order matters
Console.WriteLine(interval);
// hey, don't we get a lunch break?
DateTime startLunch = DateTime.Parse("11/17/2008 12:00 pm");
DateTime endLunch = DateTime.Parse("11/17/2008 12:25 pm");
TimeSpan lunchInterval = endLunch - startLunch;
Console.WriteLine(interval - lunchInterval); // 20 minutes of overtime! (yeah, right)
Console.Read();
}
}
}
Tags: C#.NET, console, csharp, DateTime
Posted in .NET | No Comments »
October 24th, 2008
I’ve seen this question posed in a few different forms. One guy wanted to have the user click on a GridView row and have a new window open with an external URL. Another wanted the click to pass a bound ID to the new window. Someone else wanted to have the click fire off a third party script.
You can either wait until the whole thing is loaded, then iterate through the rows, or you can attach a script during the binding. Most GridViews are databound at some point, and it’s quicker to just use that event than to iterate through who knows how many rows after the fact.
Here is an example ASP.NET GridView and SqlDataSource (using Northwind).
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="CategoryID"
DataSourceID="SqlDataSource1" OnRowDataBound="GridView1_RowDataBound">
<Columns>
<asp:BoundField DataField="CategoryID" HeaderText="CategoryID" InsertVisible="False"
ReadOnly="True" SortExpression="CategoryID" />
<asp:BoundField DataField="CategoryName" HeaderText="CategoryName" SortExpression="CategoryName" />
<asp:BoundField DataField="Description" HeaderText="Description" SortExpression="Description" />
</Columns>
</asp:GridView>
<asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:NorthwindConnectionString %>"
SelectCommand="SELECT [CategoryID], [CategoryName], [Description] FROM [Categories]">
</asp:SqlDataSource>
Here is how you could attach a script that opens an arbitrary URL when the user clicks the row. My example passes the (data bound) category name to Google as a search.
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
string script = String.Format("window.open('http://www.google.com/search?q={0}')", ((DataRowView)e.Row.DataItem)["CategoryName"]);
e.Row.Attributes.Add("onclick", script);
}
}
Here is how you could open a new window and pass the GridView row’s DataKey to it. This example has just the one key, CategoryId.
protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
string url = System.Web.VirtualPathUtility.ToAbsolute("~/Default9.aspx") + "?CategoryId=" +
Server.UrlEncode(Convert.ToString(GridView1.DataKeys[e.Row.DataItemIndex].Value));
string script = "window.open('" + url + "')";
e.Row.Attributes.Add("onclick", script);
}
}
Tags: C#.NET, csharp, DataKeys, GridView, onclick, RowDataBound
Posted in .NET | 16 Comments »
October 15th, 2008
We have a new coder at work who is in love with the Boolean inclusive OR operator (pipe symbol | or as I have called it until now, bitwise OR), and he uses it for conditional expressions. I knew there had to be a difference between | and the conditional OR operator ||, but I wasn’t sure what it was. The code seemed to work fine, but it was nagging me.
So I finally got around to looking it up.
A behavior that I take for granted in C# is the result of using the conditional operators (two pipe symbols || or 2 ampersand symbols && ) with boolean expressions. For an OR, the first expression that evaluates to true stops the evaluations. For AND, the first one that evaluates to false stops them.
This makes a big difference when doing checking for nulls or other conditions that would cause runtime exceptions without having to nest the checks.
This code runs fine, even with no dataset.
DataSet ds = null;
// code here to load dataset, etc
if (ds != null &&
ds.Tables != null &&
ds.Tables[0] != null &&
ds.Tables.Count > 0)
{
//do something with the table
}
This code throws a runtime exception, because ds is null, so you get a NullReferenceException when it proceeds to go on and hit ds.Tables.
DataSet ds = null;
// code here to load dataset, etc
if (ds != null &
ds.Tables != null &
ds.Tables[0] != null &
ds.Tables.Count > 0)
{
//do something with the table
}
As an aside, I believe the default for VB.NET OR and AND is to evaluate all sides all the time as Boolean Inclusive, since I’ve been flummoxed by null reference exceptions I didn’t expect when trying similar coding. One of these days I’ll look that up, too. =)
Edit: VB.NET supports AndAlso as well as OrElse as the equivalents.
Got more tips or goodies for us? Please share them in the comments!
Tags: bitwise OR, boolean inclusive OR, C#.NET, conditional OR, csharp, operators
Posted in .NET | 2 Comments »
September 19th, 2008
While the SqlDataSource control is a nifty little thing, oftentimes a developer has to have more control over the page lifecycle (asp.net) or doesn’t want to use a BindingSource (winforms). Instead, you just want to query your database and get a DataSet back. Maybe you’re building a data access layer, or you’re using someone else’s framework, or it’s just biting you in the butt that binding with SqlDataSource fires off events in a completely unintuitive order.
We’ve seen how to execute a query that just returns one thing with ExecuteScalar. Now, though, we need entire results. And for whatever reason, we want a DataSet, not a SqlDataReader.
Luckily for us, querying and getting results into a DataSet using SqlDataAdapter is extremely easy, and works the same both in web forms and winforms. The following code is a full working example for querying Northwind (I just can’t get used to AdventureWorks yet, sorry) and getting a DataSet back. You can Bind a DataGridView to it, or a GridView, or loop the tables, or whatever.
Notes:
1. You have to have a using System.Data and using System.Data.SqlClient.
2. Your connection string should normally be stored in Settings (winforms) or web.config (web forms). In this example, I was playing with winforms, so it’s a Setting. For web forms, you’d use ConfigurationManager.ConnectionStrings.
private void button1_Click(object sender, EventArgs e)
{
DataSet myDataSet = new DataSet();
using (SqlConnection sqlconnection = new SqlConnection(Properties.Settings.Default.Northwind))
{
SqlCommand command = new SqlCommand("select * from products", sqlconnection);
SqlDataAdapter adapter = new SqlDataAdapter(command);
adapter.Fill(myDataSet);
}
}
It’s really that easy. Happy coding!
If you found this post helpful, please spread the love and share using one of the bookmark links below. Thanks!
Tags: C#.NET, csharp, database queries, DataSet, SqlDataAdapter
Posted in .NET | 3 Comments »