Friday, December 19, 2008

XML manupilation(2) --Linq to XML

Let's use the sample xml file in msdn:

Our goal is to convert this xml file to pipeline delimited txt file based on the node name. We save the xml in C:\sample.xml

We can write code like:

static void Main(string[] args)
{
XElement el = XElement.Load(@"C:\sample.xml");
var query = from c in el.Descendants("Name")
select new
{
TOTAL_TIME_UNITS = (string)c.Parent.Element("TIME").Element("TOTAL_TIME").Attribute("units"),
TOTAL_TIME = (string)c.Parent.Element("TIME").Element("TOTAL_TIME"),

SpecNumber= c.Element("Spec") != null ?(c.Element("Spec").Element("SpecNumber")!=null?(string)c.Element("Spec").Element("SpecNumber"):String.Empty):String.Empty,

SpecElevationImage1 = c.Element("Spec") != null ? (c.Element("Spec").Element("SpecImages") != null ? ((from si in c.Element("Spec").Element("SpecImages").Elements("SpecElevationImage") select si).ToArray().Length > 0 ? (string)(from si in c.Element("Spec").Element("SpecImages").Elements("SpecElevationImage") select si).ToArray()[0] : String.Empty) : String.Empty) : String.Empty,

SpecElevationImage2 = c.Element("Spec") != null ? (c.Element("Spec").Element("SpecImages") != null ? ((from si in c.Element("Spec").Element("SpecImages").Elements("SpecElevationImage") select si).ToArray().Length > 1 ? (string)(from si in c.Element("Spec").Element("SpecImages").Elements("SpecElevationImage") select si).ToArray()[1] : String.Empty) : String.Empty) : String.Empty,

TagI=(from tg in c.Parent.Element("TAGS").Elements("TAG") select tg).ToArray()
}
FileStream file = new FileStream(@"C\result.txt", FileMode.OpenOrCreate, FileAccess.Write);
StreamWriter sw = new StreamWriter(file);

foreach (var item in query)
{

string[, ,] Tag = new string[9, 9, 10];
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++)
for (int k = 0; k < 10; k++)
{
Tag[i,j,k]="";
}

for (int i = 0; i < item.TagI.Length; i++)
{
Tag[i+1,0,0]=(string)((item.TagI)[i].Element("TAG_NAME"));
if ((item.TagI)[i].Element("SUBTAGS") != null)
{
var TagJquery = from c in (item.TagI)[i].Element("SUBTAGS").Descendants("TAG")
select new
{
TagNameJ = (string)c.Element("TAG_NAME"),
TagK = c.Element("SUBTAGS")!=null?(from tg in c.Element("SUBTAGS").Elements("TAG") select tg).ToArray():null

};

for(int j=0;j {
Tag[i+1, j+1, 0] = TagJquery.ToArray()[j].TagNameJ;
if (TagJquery.ToArray()[j].TagK != null)
{
for (int k = 0; k < TagJquery.ToArray()[j].TagK.Length; k++)
{
Tag[i+1, j+1, k+1] = (string)(TagJquery.ToArray()[j].TagK)[k].Element("TAG_NAME");
}
}
}
}
}

sw.WriteLine("{0}|{1}|{2}|{3}|{4}|{5}|{6}|{7}|{8}",item.TOTAL_TIME_UNITS, item.TOTAL_TIME_UNITS,item.SpecNumber, item.SpecElevationImage1, item.SpecElevationImage2,Tag[2,3,4],Tag[1,6,3],Tag[3,1,0]);
sw.Close();
file.Close();
}

The above code gives example how to query xml element and attribute using Linq to XML, notice that the element names or attribute names in the quotes are case sensitive.

This is the following point to notice:
1. The attribute in one node can be more than 1.
2. If one node does not exist, we can use ...Element("nodeA")!=null?(sting)....:String.Empty
3. If in the same node there are mulitple Elements with same name, we can use (from ...select ) query, and there are method ToArray() to convert this query to array, and when using index, it goes to the specific element.
4. If there are node under node, the structure like:

Tags-Tag-TagName
-Tag-TagName
-SubTags-Tag-TagName
-Tag-TagName
-SubTag-Tag-TagName
We can use foreach or for iterator to get the contents out.



Tuesday, December 16, 2008

XML manupilation(1) --xsl(xslt) transformation

The goal of this series is to build a WCF service using Linq to XML to convert xml file to pipeline delimited text file.

If we have xls/xslt sheet, it is very easy for us to do xml converting.

For example we have a xml file:(from http://www.w3schools.com/xml/xml_xsl.asp). And a xsl sheet,simple.xsl.

It is very easy for us to convert or display using our style sheet. For example, we can add < ? xml-stylesheet type="text/xsl" href="simple.xsl" ? > for displaying.

Also using C# for converting is easy, here is a sample code:

using System;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
using System.Xml.Xsl;


public class ConvertXml2Txt {
private static void Main() {
XslTransform transform = new XslTransform();
transform.Load("our.xslt");
transform.Transform("test.xml", "test.txt", null);
}
}

So if we have xsl sheet, it is very easy for us to convert. But the hard thing is that most of the time we don't have xsl sheet, and to write the xsl sheet is very headache.

So in the later series, I am going to use LINQ to XML for exploration.

Monday, December 15, 2008

Auto-implemented properties

"In C# 3.0 and later, auto-implemented properties make property-declaration more concise when no additional logic is required in the property accessors. "

public class Student
{
public string First { get; set; }
...
}

Wednesday, December 3, 2008

FTP Factory

This code is written by Jaimon Mathew.

The author's code is very useful, and I referenced it blow, and added the function of get file date, returned to string type.

When use this class:

public static void main ()
{
FTPFactory ff = new FTPFactory();
ff.setDebug(true);
ff.setRemoteHost(remotehost);
ff.setRemoteUser(remoteuser);
ff.setRemotePass(remotepass);
ff.login();
ff.chdir(chdir);
ff.setBinaryMode(true);

string filename=@"myfile.txt";
ff.GetFileDate(filename);
...
...
}

Attach Jaimon Mathew's code(with the part of public string GetFileDate(string fileName) method I adde) below:

using System;
using System.Net;
using System.IO;
using System.Text;
using System.Net.Sockets;


namespace FTPDownload
{
public class FTPFactory
{

private string remoteHost, remotePath, remoteUser, remotePass, mes;
private int remotePort, bytes;
private Socket clientSocket;

private int retValue;
private Boolean debug;
private Boolean logined;
private string reply;

private static int BLOCK_SIZE = 512;

Byte[] buffer = new Byte[BLOCK_SIZE];
Encoding ASCII = Encoding.ASCII;

public FTPFactory()
{

remoteHost = "localhost";
remotePath = ".";
remoteUser = "anonymous";
remotePass = "jaimon@school2000.co.uk";
remotePort = 21;
debug = false;
logined = false;

}

//
// Set the name of the FTP server to connect to.
//
// Server name
public void setRemoteHost(string remoteHost)
{
this.remoteHost = remoteHost;
}

//
// Return the name of the current FTP server.
//
// Server name
public string getRemoteHost()
{
return remoteHost;
}

//
// Set the port number to use for FTP.
//
// Port number
public void setRemotePort(int remotePort)
{
this.remotePort = remotePort;
}

//
// Return the current port number.
//
// Current port number
public int getRemotePort()
{
return remotePort;
}

//
// Set the remote directory path.
//
// The remote directory path
public void setRemotePath(string remotePath)
{
this.remotePath = remotePath;
}

//
// Return the current remote directory path.
//
// The current remote directory path.
public string getRemotePath()
{
return remotePath;
}

//
// Set the user name to use for logging into the remote server.
//
// Username
public void setRemoteUser(string remoteUser)
{
this.remoteUser = remoteUser;
}

//
// Set the password to user for logging into the remote server.
//
// Password
public void setRemotePass(string remotePass)
{
this.remotePass = remotePass;
}

//
// Return a string array containing the remote directory's file list.
//
//
//
public string[] getFileList(string mask)
{

if (!logined)
{
login();
}

Socket cSocket = createDataSocket();

sendCommand("NLST " + mask);

if (!(retValue == 150 || retValue == 125))
{
throw new IOException(reply.Substring(4));
}

mes = "";

while (true)
{

int bytes = cSocket.Receive(buffer, buffer.Length, 0);
mes += ASCII.GetString(buffer, 0, bytes);

if (bytes < buffer.Length)
{
break;
}
}

char[] seperator = { '\n' };
string[] mess = mes.Split(seperator);

cSocket.Close();

readReply();

if (retValue != 226)
{
throw new IOException(reply.Substring(4));
}
return mess;

}

//Changed Dec 03,2008
public string GetFileDate(string fileName)
{
//if (!logined)
//{
// login();
//}

//sendCommand("MDTM " + fileName);

//if (retValue != 213)
//{
// throw new Exception(reply);
//}

//return this.reply.ToString();

if (null == fileName)
{
throw new ArgumentException("No file name specified");
}
if (fileName.Length <= 0)
{
throw new ArgumentException("No file name specified");
}


string returnDate = DateTime.MinValue.ToString();

if (!logined)
{
login();
}

sendCommand("MDTM " + fileName);


if (retValue == 213)
{
//Console.WriteLine(reply.ToString());
//size = Int64.Parse(reply.Substring(4));
returnDate = this.reply.ToString();
}
else
{
throw new IOException(reply.Substring(4));
}

Console.WriteLine(returnDate + " " + returnDate);
return returnDate.ToString();
}
//private DateTime ConvertFTPDateToDateTime(string input)
//{

// //yyyyMMddhhmmss":

// string dateInfo = input.Substring(4);
// dateInfo = dateInfo.Substring(0, 14);
// int year = Convert.ToInt16(dateInfo.Substring(0, 4));
// int month = Convert.ToInt16(dateInfo.Substring(4, 2));
// int day = Convert.ToInt16(dateInfo.Substring(6, 2));
// int hour = Convert.ToInt16(dateInfo.Substring(8, 2));
// int min = Convert.ToInt16(dateInfo.Substring(10, 2));
// int sec = Convert.ToInt16(dateInfo.Substring(12, 2));

// return new DateTime(year, month, day, hour, min, sec);
//}

//
// Return the size of a file.
//
//
//
public long getFileSize(string fileName)
{

if (!logined)
{
login();
}

sendCommand("SIZE " + fileName);
long size = 0;

if (retValue == 213)
{
size = Int64.Parse(reply.Substring(4));
}
else
{
throw new IOException(reply.Substring(4));
}

return size;

}

//
// Login to the remote server.
//
public void login()
{

clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ep = new IPEndPoint(Dns.GetHostEntry(remoteHost).AddressList[0], remotePort);

try
{
clientSocket.Connect(ep);
}
catch (Exception)
{
throw new IOException("Couldn't connect to remote server");
}

readReply();
if (retValue != 220)
{
close();
throw new IOException(reply.Substring(4));
}
if (debug)
Console.WriteLine("USER " + remoteUser);

sendCommand("USER " + remoteUser);

if (!(retValue == 331 || retValue == 230))
{
cleanup();
throw new IOException(reply.Substring(4));
}

if (retValue != 230)
{
if (debug)
Console.WriteLine("PASS xxx");

sendCommand("PASS " + remotePass);
if (!(retValue == 230 || retValue == 202))
{
cleanup();
throw new IOException(reply.Substring(4));
}
}

logined = true;
Console.WriteLine("Connected to " + remoteHost);

chdir(remotePath);

}

//
// If the value of mode is true, set binary mode for downloads.
// Else, set Ascii mode.
//
//
public void setBinaryMode(Boolean mode)
{

if (mode)
{
sendCommand("TYPE I");
}
else
{
sendCommand("TYPE A");
}
if (retValue != 200)
{
throw new IOException(reply.Substring(4));
}
}

//
// Download a file to the Assembly's local directory,
// keeping the same file name.
//
//
public void download(string remFileName)
{
download(remFileName, "", false);
}

//
// Download a remote file to the Assembly's local directory,
// keeping the same file name, and set the resume flag.
//
//
//
public void download(string remFileName, Boolean resume)
{
download(remFileName, "", resume);
}

//
// Download a remote file to a local file name which can include
// a path. The local file name will be created or overwritten,
// but the path must exist.
//
//
//
public void download(string remFileName, string locFileName)
{
download(remFileName, locFileName, false);
}

//
// Download a remote file to a local file name which can include
// a path, and set the resume flag. The local file name will be
// created or overwritten, but the path must exist.
//
//
//
//
public void download(string remFileName, string locFileName, Boolean resume)
{
if (!logined)
{
login();
}

setBinaryMode(true);

Console.WriteLine("Downloading file " + remFileName + " from " + remoteHost + "/" + remotePath);

if (locFileName.Equals(""))
{
locFileName = remFileName;
}

if (!File.Exists(locFileName))
{
Stream st = File.Create(locFileName);
st.Close();
}

FileStream output = new FileStream(locFileName, FileMode.Open);

Socket cSocket = createDataSocket();

long offset = 0;

if (resume)
{

offset = output.Length;

if (offset > 0)
{
sendCommand("REST " + offset);
if (retValue != 350)
{
//throw new IOException(reply.Substring(4));
//Some servers may not support resuming.
offset = 0;
}
}

if (offset > 0)
{
if (debug)
{
Console.WriteLine("seeking to " + offset);
}
long npos = output.Seek(offset, SeekOrigin.Begin);
Console.WriteLine("new pos=" + npos);
}
}

sendCommand("RETR " + remFileName);

if (!(retValue == 150 || retValue == 125))
{
throw new IOException(reply.Substring(4));
}

while (true)
{

bytes = cSocket.Receive(buffer, buffer.Length, 0);
output.Write(buffer, 0, bytes);

if (bytes <= 0)
{
break;
}
}

output.Close();
if (cSocket.Connected)
{
cSocket.Close();
}

Console.WriteLine("");

readReply();

if (!(retValue == 226 || retValue == 250))
{
throw new IOException(reply.Substring(4));
}

}

//
// Upload a file.
//
//
public void upload(string fileName)
{
upload(fileName, false);
}

//
// Upload a file and set the resume flag.
//
//
//
public void upload(string fileName, Boolean resume)
{

if (!logined)
{
login();
}

Socket cSocket = createDataSocket();
long offset = 0;

if (resume)
{

try
{

setBinaryMode(true);
offset = getFileSize(fileName);

}
catch (Exception)
{
offset = 0;
}
}

if (offset > 0)
{
sendCommand("REST " + offset);
if (retValue != 350)
{
//throw new IOException(reply.Substring(4));
//Remote server may not support resuming.
offset = 0;
}
}

sendCommand("STOR " + Path.GetFileName(fileName));

if (!(retValue == 125 || retValue == 150))
{
throw new IOException(reply.Substring(4));
}

// open input stream to read source file
FileStream input = new FileStream(fileName, FileMode.Open);

if (offset != 0)
{

if (debug)
{
Console.WriteLine("seeking to " + offset);
}
input.Seek(offset, SeekOrigin.Begin);
}

Console.WriteLine("Uploading file " + fileName + " to " + remotePath);

while ((bytes = input.Read(buffer, 0, buffer.Length)) > 0)
{

cSocket.Send(buffer, bytes, 0);

}
input.Close();

Console.WriteLine("");

if (cSocket.Connected)
{
cSocket.Close();
}

readReply();
if (!(retValue == 226 || retValue == 250))
{
throw new IOException(reply.Substring(4));
}
}

//
// Delete a file from the remote FTP server.
//
//
public void deleteRemoteFile(string fileName)
{

if (!logined)
{
login();
}

sendCommand("DELE " + fileName);

if (retValue != 250)
{
throw new IOException(reply.Substring(4));
}

}

//
// Rename a file on the remote FTP server.
//
//
//
public void renameRemoteFile(string oldFileName, string newFileName)
{

if (!logined)
{
login();
}

sendCommand("RNFR " + oldFileName);

if (retValue != 350)
{
throw new IOException(reply.Substring(4));
}

// known problem
// rnto will not take care of existing file.
// i.e. It will overwrite if newFileName exist
sendCommand("RNTO " + newFileName);
if (retValue != 250)
{
throw new IOException(reply.Substring(4));
}

}

//
// Create a directory on the remote FTP server.
//
//
public void mkdir(string dirName)
{

if (!logined)
{
login();
}

sendCommand("MKD " + dirName);

if (retValue != 250)
{
throw new IOException(reply.Substring(4));
}

}

//
// Delete a directory on the remote FTP server.
//
//
public void rmdir(string dirName)
{

if (!logined)
{
login();
}

sendCommand("RMD " + dirName);

if (retValue != 250)
{
throw new IOException(reply.Substring(4));
}

}

//
// Change the current working directory on the remote FTP server.
//
//
public void chdir(string dirName)
{

if (dirName.Equals("."))
{
return;
}

if (!logined)
{
login();
}

sendCommand("CWD " + dirName);

if (retValue != 250)
{
throw new IOException(reply.Substring(4));
}

this.remotePath = dirName;

Console.WriteLine("Current directory is " + remotePath);

}

//
// Close the FTP connection.
//
public void close()
{

if (clientSocket != null)
{
sendCommand("QUIT");
}

cleanup();
Console.WriteLine("Closing...");
}

//
// Set debug mode.
//
//
public void setDebug(Boolean debug)
{
this.debug = debug;
}

private void readReply()
{
mes = "";
reply = readLine();
retValue = Int32.Parse(reply.Substring(0, 3));
}

private void cleanup()
{
if (clientSocket != null)
{
clientSocket.Close();
clientSocket = null;
}
logined = false;
}

private string readLine()
{

while (true)
{
bytes = clientSocket.Receive(buffer, buffer.Length, 0);
mes += ASCII.GetString(buffer, 0, bytes);
if (bytes < buffer.Length)
{
break;
}
}

char[] seperator = { '\n' };
string[] mess = mes.Split(seperator);

if (mes.Length > 2)
{
mes = mess[mess.Length - 2];
}
else
{
mes = mess[0];
}

if (!mes.Substring(3, 1).Equals(" "))
{
return readLine();
}

if (debug)
{
for (int k = 0; k < mess.Length - 1; k++)
{
Console.WriteLine(mess[k]);
}
}
return mes;
}

private void sendCommand(String command)
{

Byte[] cmdBytes = Encoding.ASCII.GetBytes((command + "\r\n").ToCharArray());
clientSocket.Send(cmdBytes, cmdBytes.Length, 0);
readReply();
}

private Socket createDataSocket()
{

sendCommand("PASV");

if (retValue != 227)
{
throw new IOException(reply.Substring(4));
}

int index1 = reply.IndexOf('(');
int index2 = reply.IndexOf(')');
string ipData = reply.Substring(index1 + 1, index2 - index1 - 1);
int[] parts = new int[6];

int len = ipData.Length;
int partCount = 0;
string buf = "";

for (int i = 0; i < len && partCount <= 6; i++)
{

char ch = Char.Parse(ipData.Substring(i, 1));
if (Char.IsDigit(ch))
buf += ch;
else if (ch != ',')
{
throw new IOException("Malformed PASV reply: " + reply);
}

if (ch == ',' || i + 1 == len)
{

try
{
parts[partCount++] = Int32.Parse(buf);
buf = "";
}
catch (Exception)
{
throw new IOException("Malformed PASV reply: " + reply);
}
}
}

string ipAddress = parts[0] + "." + parts[1] + "." +
parts[2] + "." + parts[3];

int port = (parts[4] << 8) + parts[5];

Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ep = new IPEndPoint(Dns.GetHostEntry(ipAddress).AddressList[0], port);

try
{
s.Connect(ep);
}
catch (Exception)
{
throw new IOException("Can't connect to remote server");
}

return s;
}

}
}

Monday, November 24, 2008

Some Sql Statements Tips

ex 1.
EXEC OPENDATASOURCE('SQLOLEDB','Data Source=sername;User ID=jerry;Password=password').master.dbo.xp_cmdShell 'C:\myprogram.exe "parameter1" "parameter2"'

if 64 bits, should use:
select * from
openquery(server, 'Select * from mydb.dbo.mytable')


ex 2.
If we want to convert an table to xml, using sql, there are simple method to do this things:

select * from customer for xml auto, elements, root('customer')

ex 3.
EXEC sp_addlinkedserver 'MyServer', N'SQL Server'

ex 4.
select column_name, * from information_schema.columns where table_name='Mytable'
select table_name from information_schema.tables
(include views)

ex 5.
SELECT [name] from [sys].[tables]
(not include views)

exe 6.
EXEC sp_MSForEachTable 'TRUNCATE TABLE ?'
( EXEC sp_MSForEachTable 'DROP TABLE ?' this one need caution)

exe 7.
update table SET col = REPLACE(col, CHAR(9), ' ')
update table SET col = REPLACE(col, CHAR(13) + CHAR(10), ' ')
update table SET col = REPLACE(col, CHAR(10), ' ')

Silverlight+ADO.net Data Service (2)

Last time, we talk about how to build the ado.net data service server side development. There are plenty of posts on how to build the ado.net dat service server side application. Scott Gu's blog is one of the most classical one. For the client side, we are going to use a data grid as the component.

In client side, reference, add service reference.

Let's put in the page.xaml some code to place the buttons, data grid, text box et al, and you can design your own in Expression Blend to make the controls beautiful.

ex1. Get Data
private void ButtonSelect_Click(object sender, RoutedEventArgs e)
{
ServiceReference.DataEntities ws = new ServiceReference.DataEntities(new Uri("MyDataService.svc", UriKind.Relative));
var query = (from p in ws.CustomerSet select p);
DataServiceQuery userQuery = (DataServiceQuery)query;
userQuery.BeginExecute(new AsyncCallback(OnLoadComplete), query);
}
void OnLoadComplete(IAsyncResult result)
{
DataServiceQuery query = (DataServiceQuery)result.AsyncState;
dataGrid.ItemsSource = query.EndExecute(result).ToList();
}

The above example is show on my APM essay.

ex 2. Insert Data
We can follow msdn: http://msdn.microsoft.com/en-us/library/cc903944(VS.95).aspx to write code.
We drag four text box to the page.xaml, and an insert button. The code a like below:


//Insert Record by Text Box
private void InsertClick(object sender, RoutedEventArgs e)
{
ServiceReference.DataEntities ws = new ServiceReference.DataEntities(new Uri("MyDataService.svc", UriKind.Relative));
Customer ds = new Customer();

ds.ProductID = Int32.Parse(PIDText.Text);
ds.ProductName = PNText.Text;
ds.Price = PRText.Text;
ds.ShippingConfirmNumber = SNText.Text;
ws.AddToDataSources(ds);
ws.BeginSaveChanges(OnSaveCompleted, ws);

}

void OnSaveCompleted(IAsyncResult result)
{

ServiceReference.DataEntities wsd = (ServiceReference.DataEntities)result.AsyncState;
ObservableCollection obds = new ObservableCollection();
obds.Add(result.AsyncState as Customer);
wsd.EndSaveChanges(result);

}
For this code, there are new data type ObservableCollection, it is a dynamic data type coming after WPF. It allows us to do dynamic data binding. For more details, we can go to msdn help document for this data type.

ex 3. Update and Delete

//Update DataGrid Record
private void UpdateClick(object sender, RoutedEventArgs e)
{

DataEntities de = new DataEntities(new Uri("MyDataService.svc", UriKind.Relative));
Customerds = (Customer)dataGrid.SelectedItem;
de.AttachTo("CustomerSet", ds);
de.UpdateObject(ds);
de.BeginSaveChanges(OnUpdate, de);
}

void OnUpdate(IAsyncResult result)
{
ServiceReference.DataEntities wsd = (ServiceReference.DataEntities)result.AsyncState;
ObservableCollection obds = new ObservableCollection();
obds.Add(result.AsyncState as Customer);
wsd.EndSaveChanges(result);
}


//Delete DataGrid Record
private void DeleteClick(object sender, RoutedEventArgs e)
{
ServiceReference.DataEntities ws = new ServiceReference.DataEntities(new Uri("MyDataService.svc", UriKind.Relative));
Customer ds = (Customer)dataGrid.SelectedItem;
ws.AttachTo("CustomerSet", ds);
ws.DeleteObject(ds);
ws.BeginSaveChanges(OnUpdate, ws);
}
void OnDelete(IAsyncResult result)
{
ServiceReference.DataEntities wsd = (ServiceReference.DataEntities)result.AsyncState;
ObservableCollection obds = new ObservableCollection();
obds.Add(result.AsyncState as Customer);
wsd.EndSaveChanges(result);

}

Notice the msdn code does not work for the link: http://msdn.microsoft.com/en-us/library/cc903956(VS.95).aspx

There are 3 things the msdn code should be changed:
(1) We should use an AttachTo() method in BeginSaveChanges block. And attach our created data class to the data set.
(2) ws.BeginSaveChanges(OnUpdate, ws); The second parameter should be the service reference object or entity proxy object, what we updated here are the service reference, not the data class we created.
(3) In the EndSaveChanges block, we should use ObservableCollection, the dynamic collection data type.

Friday, November 21, 2008

Silverlight+ADO.net Data Service (1)

Firstly let's go from test page, there are numerous post how how to build a ado.net data service.

In order to use ado.net data service, you need to install many packages, for these, just go to the microsoft offical website to download and install them.


And we create tables in our database quickly, We just create customers, products, Shippings table.


Let's new a silverlight application project (you need to also install the silverlight 2 packages), and in server side add ado.net entity data model (call it mymodel.edmx, call the entity name: MyStoreEntities1). Add an ADO.net Data Service, called it MyDataService.svc.

The defaulted MyDataService class has the following code:

public class MyDataService : DataService< /* TODO: put your data source class name here */ >
{
// This method is called only once to initialize service-wide policies.
public static void InitializeService(IDataServiceConfiguration config)
{ // TODO: set rules to indicate which entity sets and service operations
//are visible, datable, etc.
// Examples:
// config.SetEntitySetAccessRule("MyEntityset", EntitySetRights.AllRead);
// config.SetServiceOperationAccessRule("MyServiceOperation",
//erviceOperationRights.All);
}
}

Change the code to be:

public class MyDataService : DataService<>
{
// This method is called only once to initialize service-wide policies.
public static void InitializeService(IDataServiceConfiguration config)
{ // TODO: set rules to indicate which entity sets and service operations
//are visible, datable, etc.
// Examples:
config.SetEntitySetAccessRule("*", EntitySetRights.All);
config.SetServiceOperationAccessRule("*", ServiceOperationRights.All);
}
}

We set all the rights to be all, and EntitySet, ServiceOperation to be "*", so that in client side we have right to deal with the data in server side.

And do one more thing, in MyModel.edmx designer changed the class Products to be Product, Customers to be Customer, so the in furture use, the entity data set name and entity class name will not be confused. Entity Data Set name will changed to CustomerSet, ProductSet and the Entity class be Customer and Product.

In this case the design of server side are finshed.

The Asynchronous Programming Model (APM)

"A synchronous method call waits for the method to complete before continuing with program flow, whereas an asynchronous method call will return immediately so that the program can perform other operations while the called method completes its work. "

The asynchronous design pattern used in the .NET Framework is characterized by the use of BeginXXXX and EndXXXX methods, where XXXX is the name of the synchronous version of the method.

For example when dealing with file stream, there is BeginRead/EndRead method:

ex 1.

byte[] buffer=new byte[100];
string filename=...;
FileStream strm=...;

//Make asynchrounous call
IAsyncResult result=strm.BeginRead(buffer,0,buffer.Lentgh,null,null);

//do some work here while you wait

//Calling EndRead will block until the Async work is complete
int numBytes=strm.EndRead(result);

strm.Close();

The BeginRead is similar to Read Method, but it returns an IAsyncResult instead of the number of bytes read. At the end of the operation, you will call the EndRead with the IAsyncResult object and it will return the byetes read.

There are 3 styles of models to do asynchrous call, wait-until-done model(ex 1), polling model( similar to wait-until-done model, except in the "main" thread, call IsCompleted property on IAsyncResult object to see if complete), and callback mode.

Let's look at Callback Model:

ex 2.

static byte[] buffer=new byte[100];

static void TestCallbackAPM()
{
string filename=...;
FileStream strm=...;

//Make asynchronous call
IAsyncResult result=strm.BeginRead(buffer,0,buffer.Length, new AsyncCallBack(CompleteRead), strm);
}

static void CompleteRead(IAsyncResult result)
{
FileStream strm=(FileStream)result.AsyncState;

//finished, we can call EndRead and it will return without blocking
int numBytes=strm.EndRead(result);

strm.Close();
}

In our silverlight and ADO.net data service learning, we have similar code, they are using the callback model:

ex 3.
//Get Data
private void ButtonSelect_Click(object sender, RoutedEventArgs e)
{
ServiceReference.DataEntities ws = new ServiceReference.DataEntities(new Uri("WebDataService.svc", UriKind.Relative));
var query = (from p in ws.DataSources select p);
DataServiceQuery userQuery = (DataServiceQuery)query;
userQuery.BeginExecute(new AsyncCallback(OnLoadComplete), query);
}
void OnLoadComplete(IAsyncResult result)
{
DataServiceQuery query = (DataServiceQuery)result.AsyncState;
dataGrid.ItemsSource = query.EndExecute(result).ToList();
}


//Insert Record by Text Box
private void InsertClick(object sender, RoutedEventArgs e)
{
ServiceReference.DataEntities ws = new ServiceReference.DataEntities(new Uri("WebDataService.svc", UriKind.Relative));
DataSources ds = new DataSources();
ds.ProductID = Int32.Parse(PIDText.Text);
ds.SourceID = SIDText.Text;
ds.SourceGroupID = SGIDText.Text;
ds.SourceName = SNText.Text;
ws.AddToDataSources(ds);
ws.BeginSaveChanges(OnSaveChangesCompleted, ws);
}

void OnSaveChangesCompleted(IAsyncResult result)
{
ServiceReference.DataEntities wsd = (ServiceReference.DataEntities)result.AsyncState;
ObservableCollection obds = new ObservableCollection();
obds.Add(result.AsyncState as DataSources);
wsd.EndSaveChanges(result);
}

Static class, static member and singleton pattern

static class can only have static memebers, and the static class cannot be instantiated.so when we have a class:

public static classA{};

ClassA ca=new ClassA() is not permitted.

static members, such as static field, static constructor, static property, static method, they blong to the class, even this class is not static and the class can be instantiated, these static members are not belong to the instance of this class.

For example, we have a non-static class:

public classB
{

//static field
public static int i;

//static property
private static int j;
public static int J
{
get
{
return j;
}
set
{
value=j;
}
}

//static method
public static void Method()

{
Console.WriteLine(...);
}

}

When we use these members, for example we can have the instance of ClassB
ClassB cb=new ClassB();
And when use these static members, we can only use:

//static field
ClassB.i;

//static property
ClassB.J;

//static method
ClassB.Method();

The following code will guarantee a singleton design pattern:

class Singleton
{
public static readonly Singleton instance=new Singleton();
private Singleton() {}
}

This code have 2 point: the constructor is private, and there only have one instance.

The code is equallent to the following code:

class Singleton
{
public static readonly Singleton instance;

//static constructor, the content is excute before the instance is claimed as static

static Singleton()
{
instance=new Singleton();
}

//private constructor guarantee the constructor are not able to use out side of the class
private Singleton(){}
}

When we use the instance of this class, we can only write as: Singleton.Instance.