Tuesday, April 26, 2011

Detect browser closing through clicks on the [X] button

I had been searching through the internet to find out how to control this issue: You have a web application where you validate every user that logs in, if somebody tries to log in from another place with the same user name, he should be denied access because the user is at the system right at that moment.
Well, the way I'm controlling this, is using a table where I put all the users� info. I�ll make this simple. Look at the following example:
Collapse
Table : employee
-------------------
username
password
active
Notice that if the user logs in the application, the field "active" will turn to 1� if the user logs off, the field will turn to 0.
Well ... I have this web application where I have a button to sign out. When I press the button, it invokes a function to update my database to turn �active�=0, but the problem is what if I press the X button of the navigator? I mean, there�s no way to detect using C#, how to catch when I close the browser in order to update the database, right? Everyone told me that the event onUnload (C#) works for detecting it� wrong! This event is not triggered when I close the window but it does when I browse another web page. So it doesn't help me. Others told me that Global.Asax catches a window closing with Session_End.. Wrong! Global.Asax works only when you tell the application to end or when the session expires � Believe me.. I tried a lot of things�
Well I guess I have found a solution to this problem� I took a long time searching though the internet looking for something until I got this link and based on the answer provided in that link, I decided to do this. I have Login.aspx, RETURN.aspx (it has three pages, too, left and principal, it's a frames page�) and the spyWin.aspx , (special thanks to PE_CF_DEV from experts-exchange for the code, I just took your code and mixed it with C#). Work with two session variables, one to save the user login (Session[�USER�]) and the other to check if it's the right moment to update the database (Session[�RETURN�]).
  1. In your login page �login.aspx, you have to put this in the LOAD event:
    Collapse
    if ( Convert.ToString(Session["USER"]).Length > 0 )
    {
        if (Session["RETURN"]!=null)
        {
            if ( Convert.ToInt32(Session["RETURN"]) == 1)
            {
                Session["RETURN"] = 0;
                �<Code to reset database>�
            }
            else
            {
                Response.Redirect("RETURN.aspx",false); 
            }
        }
    }
    This will first check if I had logged in recently, if the session is still alive and is trying to enter at Login.aspx, it will be redirected to RETURN.aspx.
  2. Put this code in the pressed event of the button to log out (this button is located in the left frame):
    Collapse
    Session.Abandon(); 
    Response.Write("<script>javascript: parent.opener=''; " + 
                                    "parent.close();</script>");
    The first line closes the session, this is caught by the event Session_end of GLOBAL.ASAX.
    Collapse
    protected void Session_End(Object sender, EventArgs e)
    {
        if ( Convert.ToString(Session["USER"]).Length > 0 )
        {
            <� Your code to update database>
            Session["USER"] = null;
        }
    }
    The second one, invokes a JavaScript event to close the window. Usually the browser detects when you close it, it sends you a message (message �do you want to close this window?�). The next point will solve this so that no message window appears.
  3. Put this in the LOAD event of RETURN.aspx (not the pages that form the frames page, I mean, not the left, top, or the principal page).
    Collapse
    if (Session["USER"]==null)
    {
        Response.Redirect("Login.aspx",false);
    }
    else { Session["RETURN"] = 0; }
    This will be validated if there's still a user with an active session. I�ll explain a little later, the use of Session["RETURN"].
    At the moment, I can only say that this session variable is initialized.
  4. Put this in the HTML section (in the <HEAD> ) of Index.aspx ( not the pages that form the frames page, I mean, not the left, top, or the principal page).
    Collapse
    <script language="javascript">
    function launch_spyWin() 
    {
        spyWin = open('spyWin.aspx','spyWin',
           'width=100,height=100,left=2000,top=0,status=0');
        spyWin.blur();
    }
    onunload = launch_spyWin;
    </script>
    This will solve the point explained in step 2). My code is based on the code from PE_CF_DEV.
  5. With JavaScript we know that the event �onUnload� will run the function regardless of the page being closed or the user navigating to another page. OK, now write this in the LOAD event of spyWin.aspx.
    Collapse
    if (Convert.ToInt32(Session["RETURN"])==0)
    {
        Session["RETURN"] = 1 ;
        
        string script = "<script type='text/javascript' " + 
                "language="'javascript'"> " +
                " function check_opener() { " +
                " if (opener && opener.closed){ " +
                " parent.opener=''; " +
                " parent.close(); " +
                " window.open('Login.aspx') " +
                " } " +
                " else{ " +
                " parent.opener=''; " +
                " parent.close(); " +
                " } " +
                "} " +
                " onload = function() { " +
                " self.blur(); " +
                " setTimeout('check_opener()',0); " +
                " } " +
                " </script>";
        this.Controls.Add(new LiteralControl(script));
        
    }
    else
    {
        Session["RETURN"] = 0 ;
    }
    This spyWindow is a non-visible popup window that will be triggered when the main window is closed. When I change Session["RETURN"] = 1, the Login.aspx catches this variable and then updates the database (the same function of Session_End of Global.asax)..
Problem solved! Now my life is easier...
Hope this helps someone�

update the database on browser close?

The only way is by using AJAX. Here's an example.
//////////////////////////////////////////////////////////////////////////
// Page1.aspx
//////////////////////////////////////////////////////////////////////////

<script type="text/javascript">
<!--
var g_databaseRecordKey = '<%= DatabaseRecordKey %>';
var g_isPostBack = false;
function callAjax(webUrl, queryString)
{
 var xmlHttpObject = null;
 try
 {
  // Firefox, Opera 8.0+, Safari...
  xmlHttpObject = new XMLHttpRequest();
 }
 catch(ex)
 {
  // Internet Explorer...
  try
  {
   xmlHttpObject = new ActiveXObject('Msxml2.XMLHTTP');
  }
  catch(ex)
  {
   xmlHttpObject = new ActiveXObject('Microsoft.XMLHTTP');
  }
 }
 if ( xmlHttpObject == null )
 {
  window.alert('AJAX is not available in this browser');
  return;
 }
 xmlHttpObject.open("GET", webUrl + queryString, false);
 xmlHttpObject.send();
 var valueSent = xmlHttpObject.responseText;
 return valueSent;
}
function doUpdate()
{
 var webUrl = 'AjaxPage.aspx';
 // Add any additional values needed for the update to the QueryString...
 var queryString = '?CallRequest=UpdateDatabase&DbKey=' + g_databaseRecordKey;
 var returnCode = callAjax(webUrl, queryString);
 //alert('returnCode: ' + returnCode);
}
function windowOnUnload()
{
 if ( g_isPostBack == true )
  return; // Let the page unload
 //alert('window.onunload fired');
 doUpdate();
}
window.onunload = windowOnUnload;
// -->
</script>
//////////////////////////////////////////////////////////////////////////
// Page1.aspx.cs
//////////////////////////////////////////////////////////////////////////

protected string DatabaseRecordKey = "";
protected void Page_Load(object sender, EventArgs e)
{
 // Set to the unique database record key for this user...
 DatabaseRecordKey = "1234";
 this.ClientScript.RegisterOnSubmitStatement(this.GetType(), "OnSubmitScript", "g_isPostBack = true;");
}
//////////////////////////////////////////////////////////////////////////
// AjaxPage.aspx.cs
//////////////////////////////////////////////////////////////////////////

protected string UpdateDatabase(string dbKey)
{
 string returnValue = "OK";
 // Update the database here setting the returnValue variable to "Failed" if not successful...
 return returnValue;
}
private void Page_Load(object sender, System.EventArgs e)
{
 string callRequest = (this.Request["CallRequest"] == null) ? string.Empty : this.Request["CallRequest"];
 string returnValue = string.Empty;
 if ( callRequest == "UpdateDatabase" )
 {
  string dbKey = (this.Request["DbKey"] == null) ? string.Empty : this.Request["DbKey"];
  returnValue = UpdateDatabase(dbKey);
 }
 this.Response.ClearHeaders();           
 this.Response.Clear();
 this.Response.Write(returnValue);
 this.Response.End();
}

Monday, April 25, 2011

Searching XML file using ASP.NET and C#

I have found how to search a particular element from the XML file.

The XML file is:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<company>
<employee id="109">
<fname>John</fname>
<lname>Graph</lname>
<age>28</age>
</employee>
<employee id="110">
<fname>Anna</fname>
<lname>Laoe</lname>
<age>32</age>
</employee>
<employee id="111">
<fname>Ashley</fname>
<lname>Kazzl</lname>
<age>34</age>
</employee>
<employee id="113">
<fname>Greg</fname>
<lname>Muray</lname>
<age>24</age>
</employee>
<employee id="114">
<fname>Fannie</fname>
<lname>Dow</lname>
<age>36</age>
</employee>
</company>

The code to search the particular employee record from the existing xml file is as follows:
(I don't know if this is a better way to search a XML file, so plz. help me in optimizing the code)


 string empid1 = Request.QueryString["empid"];
        Response.Write("<br>XML file searching using asp.net<br><br>");
        XmlDocument doc = new XmlDocument();

        string xmlFile = System.Web.HttpContext.Current.Server.MapPath("~/App_Data/emp.xml");
        doc.Load(xmlFile);
      int  empid=int.Parse(empid1);// convert the empid1 to int

        XmlNodeList xmlnode = doc.GetElementsByTagName("employee");
        for (int i = 0; i < xmlnode.Count; i++)
        {
                      
                XmlAttributeCollection xmlattrc = xmlnode[i].Attributes;
                if (int.Parse(xmlattrc[0].Value) == empid)
                {
                    Response.Write("<br>Employee-ID: " + xmlattrc[0].Value);
                    Response.Write("<br>First Name: " + xmlnode[i].ChildNodes[0].InnerText);
                    Response.Write("<br>Last Name: " + xmlnode[i].ChildNodes[1].InnerText);
                    Response.Write("<br>Age: " + xmlnode[i].ChildNodes[2].InnerText);
                    Response.Write("<br>");
                }
        }




Second Case 
----------------
I got the solution to Update the data for a Particular Node (in this case to update the data of particular Employee)
I am pasting the complete C# file of that page

using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml;

public partial class xml1 : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
//Page_Load will just grab the Employee-Id of particular Employee and will search for that EmployeeID in the XML file. If it find a appropriate match then it will
//display all the Details of that Employee viz. Empid(Empid is not editable), First Name(editable), Last Name(editable), Age(editable)
        if (!IsPostBack)
        {
            string empid1 = Request.QueryString["empid"];
            // Response.Write(empid1); WF
            //  Response.Write("<br>XML file searching using asp.net<br><br>");
            XmlDocument doc = new XmlDocument();

            string xmlFile = System.Web.HttpContext.Current.Server.MapPath("~/App_Data/emp.xml");
            doc.Load(xmlFile);
            int empid = int.Parse(empid1);

            XmlNodeList xmlnode = doc.GetElementsByTagName("employee");
            for (int i = 0; i < xmlnode.Count; i++)
            {


                XmlAttributeCollection xmlattrc = xmlnode[i].Attributes;
                if (int.Parse(xmlattrc[0].Value) == empid)
                {
                    lblEmpid.Text = xmlattrc[0].Value;
                    txtFirstName.Text = xmlnode[i].ChildNodes[0].InnerText; //Assign First Name from XML file to txtFirstName Text Box
                    txtLastName.Text = xmlnode[i].ChildNodes[1].InnerText; //Assign Last Name from XML file to txtLastName Text Box
                    txtAge.Text = xmlnode[i].ChildNodes[2].InnerText;// //Assign Age from XML file to txtAge Text Box
                 
                }
            }

        }
    }
    protected void Button1_Click(object sender, EventArgs e)
    {
        //This Code will Update the XML file and will replace the data in the Xmlnode.
        // We can change the First Name, Last Name , Age of a PArticular Employee by using this code.
        string empid1 = lblEmpid.Text.ToString();
        XmlDocument doc = new XmlDocument();
        string fname = txtFirstName.Text;
        string lname = txtLastName.Text;
        string age = txtAge.Text;
        string xmlFile = System.Web.HttpContext.Current.Server.MapPath("~/App_Data/emp.xml");
        doc.Load(xmlFile);
        int empid = int.Parse(empid1);

        XmlNodeList xmlnode = doc.GetElementsByTagName("employee");
        for (int i = 0; i < xmlnode.Count; i++)
        {


            XmlAttributeCollection xmlattrc = xmlnode[i].Attributes;
            if (int.Parse(xmlattrc[0].Value) == empid)
            {
              
              
                xmlnode[i].ChildNodes[0].InnerText = fname;//ChildNodes[0] is First Name
                xmlnode[i].ChildNodes[1].InnerText = lname;
                xmlnode[i].ChildNodes[2].InnerText = age;
                doc.Save(xmlFile);// The New Data is Saved in the XML file by replacing old data for that particular employee.
            }
        }
    }
}


Case 3
--------
Instead of looping each employee for get a particular employee using ID, we can use XPath.
You can change the following code

XmlNodeList xmlnode = doc.GetElementsByTagName("employee");
        for (int i = 0; i < xmlnode.Count; i++)
        {


            XmlAttributeCollection xmlattrc = xmlnode[i].Attributes;
            if (int.Parse(xmlattrc[0].Value) == empid)
            {
                
                
                xmlnode[i].ChildNodes[0].InnerText = fname;//ChildNodes[0] is First Name
                xmlnode[i].ChildNodes[1].InnerText = lname;
                xmlnode[i].ChildNodes[2].InnerText = age;
                doc.Save(xmlFile);// The New Data is Saved in the XML file by replacing old data for that particular employee.
            }
        }
 AS (Which uses XPath)
XmlNodeList xmlnode = doc.SelectNodes("//employee[@id=" + empid + "]");
        for (int i = 0; i < xmlnode.Count; i++)   // Loops only once if record exists
        {


            //XmlAttributeCollection xmlattrc = xmlnode[i].Attributes;  //-- This Code is not needed
            //if (int.Parse(xmlattrc[0].Value) == empid) //-- This code is not needed
            //{
                
                
                xmlnode[i].ChildNodes[0].InnerText = fname;//ChildNodes[0] is First Name
                xmlnode[i].ChildNodes[1].InnerText = lname;
                xmlnode[i].ChildNodes[2].InnerText = age;
                doc.Save(xmlFile);// Even this line can be out of the for loop...
            //}
        }


CASE 3
---------
I got solution to add (append) an element to in the existing XML file at the end of file.
When I add the (employee) element to the end of the file , I search the last  empid and increment it by 1 and then insert this current empid in the empid attribute of the current employee (element) being added to the end of the file.
Limitations: I cannot see if there are any duplicates. (but this xml file does not contain any mission critical data).
I am pasting the code in this section (Please advice if I can write my code in a better way [:)])


 string fname, lname, age;
        fname = txtFirstName.Text.ToString();
        lname = txtLastName.Text.ToString();
        age = txtAge.Text.ToString();
        XmlDocument doc = new XmlDocument();
        string xmlFile = System.Web.HttpContext.Current.Server.MapPath("~/App_Data/emp.xml");
        doc.Load(xmlFile);
        //First we have to get the empid of the last employee and increment it by 1 for the new employee
        XmlNodeList xmlnode = doc.GetElementsByTagName("employee");
        //Response.Write("<script type='text/javascript'>alert('" + xmlnode.Count + "')</javascript>");
        XmlNode empnode = doc.CreateElement("employee");
       // Response.Write(xmlnode.Count);//WF
     
      XmlAttributeCollection xmlattrc = xmlnode[xmlnode.Count-1].Attributes;

       //Response.Write(xmlattrc[0].Value);//WF
      
      
      
       int lastempid = int.Parse(xmlattrc[0].Value);//Get the Emp-Id of the last Employee
       int currempid = lastempid + 1;
         
        XmlNode empid = doc.CreateNode(XmlNodeType.Attribute, "id", "");
        empid.Value =  currempid.ToString();
     
        empnode.Attributes.SetNamedItem(empid);
        XmlNode empfname = doc.CreateElement("fname");
        empfname.InnerText = fname;
        empnode.AppendChild(empfname);
        XmlNode emplname = doc.CreateElement("lname");
        emplname.InnerText = lname;
        empnode.AppendChild(emplname);
        XmlNode empage = doc.CreateElement("age");
        empage.InnerText = age;
        empnode.AppendChild(empage);
         doc.DocumentElement.AppendChild(empnode);
         doc.Save(xmlFile);


/////////////////

The XML file after New data is added:

<?xml version="1.0" encoding="ISO-8859-1"?>
<company>
  <employee id="109">
    <fname>John</fname>
    <lname>Graph</lname>
    <age>28</age>
  </employee>
  <employee id="110">
    <fname>Anna</fname>
    <lname>Laoe</lname>
    <age>32</age>
  </employee>
  <employee id="111">
    <fname>Allan</fname>
    <lname>Woods</lname>
    <age>25</age>
  </employee>
  <employee id="113">
    <fname>Greg</fname>
    <lname>Muray</lname>
    <age>24</age>
  </employee>
  <employee id="114">
    <fname>Fannie</fname>
    <lname>Dow</lname>
    <age>36</age>
  </employee>
  <employee id="115">
    <fname>Hello</fname>
    <lname>World</lname>
    <age>11</age>
  </employee>
  <employee id="116">
    <fname>Hello</fname>
    <lname>World</lname>
    <age>11</age>
  </employee>
</company>

Still I have to write the code to delete a particular node (employee) from the XML File.

CASE 4
----
I found the Solution for deleting a particular node (employee). from the XML file :
the employee node is deleted by first getting its empid and then deleting all the information associated with that empid(like fname,lname,age)

Following is the code:

string fname, lname, age;
            fname = txtFirstName.Text.ToString();
            lname = txtLastName.Text.ToString();
            age = txtAge.Text.ToString();
            string empid1 =Request.Querystring["empid"]; // the empid is passed in the querystring
            XmlDocument doc = new XmlDocument();
            string xmlFile = System.Web.HttpContext.Current.Server.MapPath("~/App_Data/emp.xml");
            doc.Load(xmlFile);
            int empid = int.Parse(empid1);
            XmlNode deletenode = doc.SelectSingleNode("//employee[@id=" + empid + "]");
            deletenode.ParentNode.RemoveChild(deletenode); //In this line all the information (i.e. child node) of employee are deleted.
            doc.Save(xmlFile);
(Please suggest if I can do the same thing in a better manner)

http://forums.asp.net/t/1318271.aspx/1

Saturday, April 23, 2011

Creat an XML file based on an XSD schema

I have an XSD file pre defined as shown below:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="unqualified">
 <xs:element name="Synchro">
  <xs:complexType>
   <xs:sequence>
    <xs:element name="Titles" minOccurs="0">
     <xs:complexType>
      <xs:sequence maxOccurs="unbounded">
       <xs:element name="Name" type="xs:string"/>
      </xs:sequence>
     </xs:complexType>
    </xs:element>
 </xs:complexType>
</xs:schema>
Using a dataset I populate it as follows:
// Create an instance of a dataset
DataSet syncDS = new DataSet();

// Assign the schema to the dataset
syncDS.ReadXmlSchema(@"c:\temp\Synchro.xsd");

// Populate the tittles table
syncDS.Tables["Titles"].Rows.Add("Ms");
syncDS.Tables["Titles"].Rows.Add("Dr");
syncDS.Tables["Titles"].Rows.Add("Professor");
syncDS.Tables["Titles"].Rows.Add("Mr");
syncDS.Tables["Titles"].Rows.Add("Miss");
syncDS.Tables["Titles"].Rows.Add("Sir");
syncDS.Tables["Titles"].Rows.Add("Mrs");
syncDS.Tables["Titles"].Rows.Add("Reverend");

syncDS.WriteXml(@"c:\temp\syncro.xml");
ANSWER
--------------
I have worked out how to solve the problem but I'm not convinced it's 
the most efficient way of doing things but at least I know it works.
 
 Below, I've included some sample code:
// create the root element
XElement root = new XElement("Synchro", null);

// Add the attributes to the root node
root.Add(new XAttribute("SchemaVersion", "1.0.1")); // this is hard coded as it must be changed when the XSD structure changes
root.Add(new XAttribute("SynchroDate", DateTime.Today.ToString("yyyy-MM-dd")));

// Now populate the titles element

// Create the Titles element
XElement Titles = new XElement("Titles", null);

//Hard code for test case
//Titles.Add(new XElement("Name", "Ms"));
//Titles.Add(new XElement("Name", "Dr"));
//Titles.Add(new XElement("Name", "Professor"));
//Titles.Add(new XElement("Name", "Mr"));
//Titles.Add(new XElement("Name", "Miss"));
//Titles.Add(new XElement("Name", "Sir"));
//Titles.Add(new XElement("Name", "Mrs"));
//Titles.Add(new XElement("Name", "Reverend"));

// Iterate through the values from a datatable 
foreach (SyncDataSet.TitlesRow tRow in SyncData.SyncTitles)
{
    // Create a new element node for each title
    Titles.Add(new XElement("Name", tRow.TitleName));
}

// Add the Titles element to the root node
root.Add(Titles);

// Validate the root node has been populated correctly against the schema
XDocument doc = new XDocument(root);

XmlSchemaSet schemas = new XmlSchemaSet();
schemas.Add(XmlSchema.Read(new StreamReader(Path.Combine(SettingsEx.Instance.SchemasFolder, "BAHCC_Synchro.xsd")), null));

// set the valid flag to true prior to validation and let the event handler set it to false if it's not valid
bool OutputValid = true;

// Validate against the schema and set the Output valid flag to false if the xmlSync_validation event handler is called.
doc.Validate(schemas, xmlSync_validation);

if (OutputValid == true)
{
    // Output the XML to a file
    root.Save(@"C:\temp\Synchro.xml"));
}
This produces an output of :
<?xml version="1.0" standalone="yes"?>
<NewDataSet>
  <Titles>
    <Name>Ms</Name>
  </Titles>
  <Titles>
    <Name>Dr</Name>
  </Titles>
  <Titles>
    <Name>Professor</Name>
  </Titles>
  <Titles>
    <Name>Mr</Name>
  </Titles>
  <Titles>
    <Name>Miss</Name>
  </Titles>
  <Titles>
    <Name>Sir</Name>
  </Titles>
  <Titles>
    <Name>Mrs</Name>
  </Titles>
  <Titles>
    <Name>Reverend</Name>
  </Titles>
</NewDataSet>
but what I was expecting something like this:
<Synchro SchemaVersion="1.0.1" SynchroDate="2009-12-11">
    <Titles>
      <Name>Ms</Name>
      <Name>Dr</Name>
      <Name>Professor</Name>
      <Name>Mr</Name>
      <Name>Miss</Name>
      <Name>Sir</Name>
      <Name>Mrs</Name>
      <Name>Reverend</Name>
    </Titles>
  </Synchro> for ref:-
http://social.msdn.microsoft.com/Forums/en-US/adodotnetdataset/thread/4df9785a-8b1f-48c7-9914-1ead9da3ce82/ 

Creating XML Files using C#

When you want to export data, you can choose formats such as XML or csv for the data files. The XML format is more widely used for its flexibility and is desirable by many systems to be able to process the data. This article explains how to create xml files from C# classes.
Situation: Where would you use it:
- When you have to export data from one system to another system on a different platform
- When you have to setup scheduled jobs to export data
- When you want to keep a backup of key data on a regular basis. Though the database will give (better) alternatives, this technique will help you in shared hosting environments where you may want to keep a good backup of users who have signed up on your site, on a daily basis, for example.
This article touches the following technological aspects:
- C# class library
- Creating XML Schema (XSD)
- Generating C# Class file from XSD
- Generating XML files from C# object using XmlSerializer
Generate xml to export data
Figure 1: Generate xml to export data
Example:
Suppose you have the following data which need to be exported in the form of XML files.
Customers:
CustomerID
CompanyName
ContactName
Address
City
PostalCode
Country
Phone
Fax
Each customer may have one-to-many Orders.
Orders:
OrderID
CustomerID
OrderDate
RequiredDate
ShippedDate
ShipAddress
ShipCity
ShipPostalCode
ShipCountry
To export this data on a daily basis, you would create XML files in the following format:
<Customers>
<CustomerID />
<CompanyName />

<Orders>
<OrderID />
<OrderDate />

</Orders>
<Orders />
<Orders />

</Customers>
<Customers />
<Customers />

Here are the steps to create it:
1) Create new project in Visual Studio
Creating New Project
Figure: Creating New Project
2) Add a class library to the project
Adding a class library to the solution
Figure: Adding a class library to the solution
3) Add a schema (.xsd) file to the class library project
Adding schema file to the project
Figure: Adding schema file to the project
4) Add schema elements. There are different ways in which you can add elements. One simple way is to drag and drop the database tables from the server explorer into the .xsd’s design area (when MySchema.xsd is opened with XML Schema Editor).
For this exercise, add Customers and Orders tables from the Northwind database from the server explorer.
The completed schema will look like this in the designer.
Customers and Orders schema
Figure: Customers and Orders schema
See the project source code to view the XSD code (ie with XML tags)
5) Generate the C# Class from xsd
At the VS Command prompt, enter xsd /c MySchema.xsd. This will create MySchema.cs. Rename it to MySchemaClass.cs, add it to the MyDataLayer class library project, update the namespace of MySchemaClass.cs
If your schema’s top element is named ExportData, (<xs:element name=ExportData>) MyDataLayer class will have a class named ExportData (public partial class ExportData).
(I’ve also updated the OrderDate datatype to be string in the generated class, to produce a formatted output in the XML)
6) Create the object from ExportData class and fill the object’s properties with data (from Database or from any other source).
eg:
private ExportData _exportXmlData = new ExportData();
(See the source code for full details).
7) Use the XmlSerializer to create XML files
eg:
FileStream fs = null;
try
{
XmlSerializer serializer = new XmlSerializer(typeof(ExportData));
fs = new FileStream(_fileName, FileMode.Create, FileAccess.Write);
serializer.Serialize(fs, _exportXmlData);
}
catch
{
throw;
}
finally
{
if (fs != null)
{
fs.Close();
}
}
(See the source code for full details).
8 ) Run the project. It will create the XML files containing data, which can be exported (via ftp or saved directly to a shared drive).
A typical customer data in the XML file produced by this example will have the following format:
<Customers>
<CustomerID>2</CustomerID>
<CompanyName>Company Name 2</CompanyName>
<ContactName>Contact Name 2</ContactName>
<City>Dallas 2</City>
<Orders>
<OrderID>2000</OrderID>
<OrderDate>Tuesday, August 19, 2008</OrderDate>
<RequiredDate>2008-08-21T14:33:31.8125-05:00</RequiredDate>
<ShipName>UPS 2</ShipName>
</Orders>
<Orders>
<OrderID>2001</OrderID>
<OrderDate>Tuesday, August 19, 2008</OrderDate>
<RequiredDate>2008-08-21T14:33:31.8125-05:00</RequiredDate>
<ShipName>UPS 2</ShipName>
</Orders>
</Customers>

Feel free to comment your thoughts / questions / suggestions below.

Friday, April 22, 2011

LINQ to XSD with FetchXML

you can get a copy of FetchXml Schema here. Once you’ve installed the package, here are quick steps to create a LINQ to XSD project. (Or use the overview document for detailed steps.)
  1. In VS2008, create a new Console App under project type “LINQ to XSD Preview
  2. Add a new Schema document into your project. (.xsd file)
  3. Copy and paste the FetchXml Schema into your newly create XSD document.
  4. Select Properties of the Schema, under Build Action, select LinqToXsdSchema.
  5. Build your solution, and you now have strongly typed XML mapping objects. (Open Object Browser[Ctrl +W, J] and you can see these new classes)
This is a typical sample of FetchXml.
01<fetch mapping='logical'>
02 <entity name='account'><all -attributes/>
03    <link -entity name='systemuser' to='owninguser'>
04 <filter type='and'>
05 <condition attribute = 'lastname' operator='eq'
06 value='crmlastname'/>
07 </filter>
08 <filter type='or'>
09 <condition attribute='firstname' operator='eq'
10 value='crmfirstname'/>
11 <condition attribute='nickname' operator='eq'
12 value='crmnickname'/>
13 </filter>
14 </link>
15 </entity>
16</fetch>
For example, you needed to look for all children <condition> nodes where the parent node is <filter type=’or’>, you can use XDocument class to do something like this.
1XDocument xDoc = XDocument.Load(@"..\..\sample fetch xml.xml");
2var conditions = from fe in xDoc.Elements("fetch")
3                 from e in fe.Elements("entity")
4                 from le in e.Elements("link-entity")
5                 from f in le.Elements("filter")
6                     where f.Attribute("type").Value == "or"
7                 from c in f.Elements("condition")
8                 select c;
There’s nothing wrong with this approach, except that it can be error prone. With LINQ to XSD, you get a object mapping to XML in a typed manner and provides a pleasant OO programming style for developers. There’s two approaches to query and we’ll be looking at Typed Query first.
Typed Query
The above LINQ to XML Query can now be structured using LINQ to XSD like so…
1fetch fetch = fetch.Load(@"..\..\sample fetch xml.xml");
2var conditions = from e in fetch.entity
3                 from le in e.linkentity
4                 from f in le.filter where f.type=="or"
5                 from c in f.condition
6                 select c;
Typed XML is made possible and makes it much easier to program against. Code is cleaner, easier to maintain, and breaking-changes can be found at compile time when we change the schema and rebuild the solution.
UnTyped Query
There will be times when you get an unfamiliar XML and need to do a ‘blind’ query. In situations like this, you can use the XRoot class in the LINQ to XSD library, and then look for elements you want, using the UnTyped property.
01// assuming we don't know what the structure of the xml is like, we use XRoot
02var xroot = XRoot.Load(@"..\..\sample fetch xml.xml");
03 
04// find the <fetch> element nodes in the xml
05var nodes = xroot.XDocument.Descendants("fetch");
06 
07if (nodes!= null && nodes.Count() > 0)
08{
09 //find all condition elements
10 var conditions = ((fetch)nodes.First()).Untyped.Descendants("condition");
11}
Create Typed XML
Lastly I want to show you how easy it is to create an XML using this Typed-XML approach, using C# object initializer syntax. Of course you could do it in an imperative style, but I prefer the former (I’m weird). The code below creates the sample FetchXml shown in the snippet earlier.
01var fetch = new fetch {
02    mapping = "logical",
03    entity = new entity[] {
04                  new entity {
05                      name="account",
06                      allattributes = new allattributes[] { new allattributes() },
07                      linkentity = new linkentity[] {
08                        new linkentity {
09                            name="systemuser",
10                            to="owinguser",
11                            filter = new filter[] {
12                                new filter {
13                                    type="and",
14                                    condition = new condition[] {
15                                        new condition {
16                                            attribute="lastname",
17                                            @operator="eq",
18                                            value1 = "crmlastname"
19                                        }
20                                    },
21                                },
22                                new filter {
23                                    type="or",
24                                    condition = new condition[] {
25                                        new condition {
26                                            attribute="firstname",
27                                            @operator="pp",
28                                            value1 = "crmfirstname"
29                                        },
30                                        new condition {
31                                            attribute="nickname",
32                                            @operator="eq",
33                                            value1 = "crmnickname"
34                                        },
35                                    }
36                                }
37                            },
38                        }
39                      },
40                  },
41                }
42};
I really like the Typed-XML syntax you get out of LINQ to XSD. It makes programming against XML easier and simply more enjoyable. Greatest benefits are the elimination of mis-spelling of element/attribute names, data-type constraint checks and casting validation. However at this point, there’s no real full-validation support against the entire schema and I hope this will be a feature added in next release (if there’s one).