<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-1576883349115542732</id><updated>2012-02-16T15:28:50.129-08:00</updated><category term='provisioning'/><category term='Directory'/><category term='control'/><category term='engineer'/><category term='Software Engineering'/><category term='Active'/><category term='web'/><category term='development'/><category term='SQL Server'/><category term='DataBind'/><category term='AJAX'/><category term='DataGrid'/><category term='FilteredTextBoxExtender'/><category term='app_id'/><category term='event'/><category term='DataTable'/><category term='Apple'/><category term='ImportRow'/><category term='ASP.NET'/><category term='Documentum'/><category term='DirectoryServices'/><category term='WMI'/><category term='iTouch'/><category term='web service'/><category term='Mac'/><category term='performance'/><category term='TextBox'/><category term='playlist'/><category term='Capability Maturity Model'/><category term='LINQ'/><category term='S4M'/><category term='ReorderList'/><category term='soap'/><category term='java'/><category term='process'/><category term='recon key'/><category term='GridView'/><category term='AutoIncrement'/><category term='XML'/><category term='DevXpress'/><category term='DataBound'/><category term='GridControl'/><category term='thread'/><category term='XCode'/><category term='DataRow'/><category term='AutoIncrementSeed'/><category term='C#'/><category term='iPhone'/><category term='Carnagie Melon'/><category term='AutoIncrementStep'/><category term='Application Builder'/><category term='WCF'/><category term='XPath'/><category term='certificate'/><category term='exception'/><category term='HttpException'/><category term='Row'/><category term='DropDownList'/><category term='DFC'/><category term='WPF'/><category term='DAB'/><category term='CMMI'/><category term='.NET'/><title type='text'>Derrick's Software Engineering blog</title><subtitle type='html'>Common problems and solutions to software engineering problems, with the goal of sharing knowledge to improve software development productivity and thus make software projects more predictable and controllable.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://derricksweng.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1576883349115542732/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://derricksweng.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Derrick</name><uri>http://www.blogger.com/profile/14369897513473171282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>12</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1576883349115542732.post-2441738796118120106</id><published>2011-04-30T14:55:00.000-07:00</published><updated>2011-04-30T15:03:36.969-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DataTable'/><category scheme='http://www.blogger.com/atom/ns#' term='HttpException'/><category scheme='http://www.blogger.com/atom/ns#' term='DropDownList'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>ASP.NET Drop down list and strongly typed DataTable</title><content type='html'>I had code where I populate the contents of an ASP.NET DropDownList with a strongly typed DataTable returned from an ASP.NET web service.  In this code I bound the DataTable to the DataSource of the DropDownList and did a DataBind() in the Page_Load() method.  This worked perfectly fine on my local box.&lt;br /&gt;&lt;br /&gt;However, when I loaded up the ASP.NET page in the production data center, I got:&lt;br /&gt;&lt;br /&gt;System.Web.HttpException: A&lt;br /&gt;DropDownList cannot have multiple items selected&lt;br /&gt;&lt;br /&gt;To solve this problem, after I bound the DataTable to the DropDownList, I iterated through each ListItem and set its Selected property to false:&lt;br /&gt;&lt;br /&gt;foreach (ListItem myItem in myDropDownList)&lt;br /&gt;{&lt;br /&gt;    myItem.Selected = false;&lt;br /&gt;}&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1576883349115542732-2441738796118120106?l=derricksweng.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derricksweng.blogspot.com/feeds/2441738796118120106/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1576883349115542732&amp;postID=2441738796118120106' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1576883349115542732/posts/default/2441738796118120106'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1576883349115542732/posts/default/2441738796118120106'/><link rel='alternate' type='text/html' href='http://derricksweng.blogspot.com/2011/04/aspnet-drop-down-list-and-strongly.html' title='ASP.NET Drop down list and strongly typed DataTable'/><author><name>Derrick</name><uri>http://www.blogger.com/profile/14369897513473171282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1576883349115542732.post-8004330488057783159</id><published>2010-03-03T14:40:00.001-08:00</published><updated>2010-03-03T14:40:59.708-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='soap'/><category scheme='http://www.blogger.com/atom/ns#' term='XML'/><category scheme='http://www.blogger.com/atom/ns#' term='web service'/><title type='text'>Calling an ASP.NET web service from a Java application</title><content type='html'>First, the Java application must contain a class that will encapsulate the functionality to invoke a XML web service via SOAP calls.  The code is below:&lt;br /&gt;&lt;br /&gt;import java.net.*;&lt;br /&gt;import java.io.*;&lt;br /&gt;import org.w3c.dom.Document;&lt;br /&gt;import org.w3c.dom.*;&lt;br /&gt;&lt;br /&gt;import javax.xml.parsers.DocumentBuilderFactory;&lt;br /&gt;import javax.xml.parsers.DocumentBuilder;&lt;br /&gt;import org.xml.sax.InputSource;&lt;br /&gt;import org.xml.sax.SAXException;&lt;br /&gt;import org.xml.sax.SAXParseException;&lt;br /&gt;&lt;br /&gt;public class WebServiceInvoker {&lt;br /&gt; private String HTTPContinue = "^.*HTTP/[0-9].[0-9] 1[0-9][0-9].*$";&lt;br /&gt; private String HTTPOk = "^.*HTTP/[0-9].[0-9] 200.*$";&lt;br /&gt; private String HTTPSuccess = "^.*HTTP/[0-9].[0-9] 2[0-9][0-9].*$";&lt;br /&gt; private String HTTPRedirect = "^.*HTTP/[0-9].[0-9] 3[0-9][0-9].*$";&lt;br /&gt; private String HTTPClientError = "^.*HTTP/[0-9].[0-9] 4[0-9][0-9].*$";&lt;br /&gt; private String HTTPServerError = "^.*HTTP/[0-9].[0-9] 5[0-9][0-9].*$";&lt;br /&gt;&lt;br /&gt; private String _namespace;&lt;br /&gt; private String _host;&lt;br /&gt; private String _path;&lt;br /&gt; private int _port;&lt;br /&gt;&lt;br /&gt; private int _timeout;&lt;br /&gt;&lt;br /&gt; /**&lt;br /&gt;  * @param host The server hosting the web service&lt;br /&gt;  * @param port The port on the server which will accept the call (default 80)&lt;br /&gt;  * @param wsPath The relative path of the web service on the server&lt;br /&gt;  * @param namespace The namespace defined in the webservice ("http://tempuri.org")&lt;br /&gt;  */&lt;br /&gt; public WebServiceInvoker(String host, int port, String wsPath,&lt;br /&gt;   String namespace) {&lt;br /&gt;  _host = host;&lt;br /&gt;  _port = port;&lt;br /&gt;  _path = wsPath;&lt;br /&gt;  _namespace = namespace;&lt;br /&gt;  _timeout = 60000;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public void setTimeout(int timeout) {&lt;br /&gt;  _timeout = timeout;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public int getTimeout() {&lt;br /&gt;  return _timeout;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public String invokeRPC(String remoteProcedure, ParameterCollection params)&lt;br /&gt;   throws Exception {&lt;br /&gt;  String result = "";&lt;br /&gt;&lt;br /&gt;  try {&lt;br /&gt;   String xmlData = "&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;"&lt;br /&gt;     + "&lt;soap12:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap12=\"http://www.w3.org/2003/05/soap-envelope\"&gt;"&lt;br /&gt;     + "&lt;soap12:Body&gt;" + "&lt;" + remoteProcedure + " xmlns=\""&lt;br /&gt;     + _namespace + "\"&gt;";&lt;br /&gt;   for (int i = 0; i &lt; params.size(); ++i) {&lt;br /&gt;    String[] sParam = params.Get(i);&lt;br /&gt;    if (sParam.length == 2) {&lt;br /&gt;     String paramName = sParam[0];&lt;br /&gt;     String paramValue = GeneralUtil.FixSpecialChar_forXML(sParam[1]);&lt;br /&gt;&lt;br /&gt;     xmlData += "&lt;" + paramName + "&gt;";&lt;br /&gt;     xmlData += paramValue;&lt;br /&gt;     xmlData += "&lt;/" + paramName + "&gt;";&lt;br /&gt;    } else {&lt;br /&gt;     System.out.println(xmlData);&lt;br /&gt;     throw new Exception("Poorly formatted parameter returned from Parameter Collection.  WsTester.testWs");&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   xmlData += "&lt;/" + remoteProcedure + "&gt;" + "&lt;/soap12:Body&gt;"&lt;br /&gt;     + "&lt;/soap12:Envelope&gt;";&lt;br /&gt;   InetAddress addr = InetAddress.getByName(_host);&lt;br /&gt;   Socket sock = new Socket(addr, _port);&lt;br /&gt;   sock.setSoTimeout(_timeout);&lt;br /&gt;&lt;br /&gt;   // Send header&lt;br /&gt;   BufferedWriter wr = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream(), "UTF-8"));&lt;br /&gt;   wr.write("POST " + _path + " HTTP/1.1\r\n");&lt;br /&gt;   wr.write("Host: " + _host + "\r\n");&lt;br /&gt;   //wr.write("Content-Type: application/soap+xml; charset=\"utf-8\"; action=\"http://[URL to ASP.NET web service]\" \r\n");&lt;br /&gt;   wr.write("Content-Type: application/soap+xml; charset=\"utf-8\"; action=\"\" \r\n");&lt;br /&gt;   wr.write("Content-Length: " + xmlData.length() + "\r\n");&lt;br /&gt;   wr.write("\r\n");&lt;br /&gt;&lt;br /&gt;   // Send data&lt;br /&gt;   System.out.println("Soap Message:\n" + xmlData);&lt;br /&gt;   wr.write(xmlData);&lt;br /&gt;   wr.flush();&lt;br /&gt;&lt;br /&gt;   // Response&lt;br /&gt;   BufferedReader rd = new BufferedReader(new InputStreamReader(sock.getInputStream()));&lt;br /&gt;   String line = "";&lt;br /&gt;   String header = "";&lt;br /&gt;   boolean bContinue = true;&lt;br /&gt;   int byteCount = 0;&lt;br /&gt;   // Read Header&lt;br /&gt;   while (bContinue) {&lt;br /&gt;    do {&lt;br /&gt;     line = rd.readLine();&lt;br /&gt;     header += line + " ";&lt;br /&gt;    } while (!line.matches(""));&lt;br /&gt;&lt;br /&gt;    if (header.matches(HTTPContinue)) {&lt;br /&gt;     // Connection's established. Wait for the next header&lt;br /&gt;     header = "";&lt;br /&gt;     bContinue = true;&lt;br /&gt;    } else if (header.matches(HTTPOk)) {&lt;br /&gt;     // HTTP Ok. Retreive the data&lt;br /&gt;     byteCount = parseContentLength(header);&lt;br /&gt;     header = "";&lt;br /&gt;     bContinue = false;&lt;br /&gt;    } else if (header.matches(HTTPSuccess)) {&lt;br /&gt;     // Some other non-error success code&lt;br /&gt;     // Try again to see what happens, or wait until the socket&lt;br /&gt;     // times out&lt;br /&gt;     header = "";&lt;br /&gt;     bContinue = true;&lt;br /&gt;    } else if (header.matches(HTTPRedirect)) {&lt;br /&gt;     // Shouldn't encounter this one, but if we do I have no idea&lt;br /&gt;     // how to handle it&lt;br /&gt;     bContinue = false;&lt;br /&gt;     throw new Exception("HTTP Redirect encountered:\r\n"&lt;br /&gt;       + header);&lt;br /&gt;    } else if (header.matches(HTTPClientError)) {&lt;br /&gt;     // Client error, most likely a Server not Found (404) or&lt;br /&gt;     // Forbidden (403, bad credentials).&lt;br /&gt;     //header = "";&lt;br /&gt;     bContinue = false;&lt;br /&gt;     throw new Exception("HTTP Client Error encountered:\r\n"&lt;br /&gt;       + header);&lt;br /&gt;    } else if (header.matches(HTTPServerError)) {&lt;br /&gt;     // Server errors. Internal Service Error (500) type errors&lt;br /&gt;     byteCount = parseContentLength(header);&lt;br /&gt;     char c[] = new char[byteCount];&lt;br /&gt;     rd.read(c, 0, byteCount);&lt;br /&gt;&lt;br /&gt;     //header = "";&lt;br /&gt;     bContinue = false;&lt;br /&gt;&lt;br /&gt;     throw new Exception("HTTP Server Error encountered:\r\n" + header + "\r\n" + String.valueOf(c));&lt;br /&gt;    } else {&lt;br /&gt;     throw new Exception("Unknown HTTP header:\r\n" + header);&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   if (byteCount != 0) {&lt;br /&gt;    // Read Data&lt;br /&gt;    char c[] = new char[byteCount];&lt;br /&gt;    rd.read(c, 0, byteCount);&lt;br /&gt;&lt;br /&gt;    DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();&lt;br /&gt;    DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();&lt;br /&gt;&lt;br /&gt;    StringReader reader = new StringReader(String.valueOf(c)+ "\r\n");&lt;br /&gt;    InputSource source = new InputSource(reader);&lt;br /&gt;&lt;br /&gt;    Document doc = docBuilder.parse(source);&lt;br /&gt;    doc.getDocumentElement().normalize();&lt;br /&gt;&lt;br /&gt;    NodeList list = doc.getElementsByTagName(remoteProcedure + "Result");&lt;br /&gt;    if (list.getLength() &gt; 0) {&lt;br /&gt;     result = list.item(0).getFirstChild().getNodeValue();&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  catch (Exception ex) {&lt;br /&gt;   System.out.println(ex.toString());&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  return result;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private int parseContentLength(String header) {&lt;br /&gt;  try {&lt;br /&gt;   int start = header.indexOf("Content-Length: ");&lt;br /&gt;   int end = header.indexOf(" ", start + 16);&lt;br /&gt;   String contentLine = header.substring(start, end);&lt;br /&gt;   contentLine = contentLine.replaceAll("^Content-Length: ", "");&lt;br /&gt;   contentLine = contentLine.trim();&lt;br /&gt;&lt;br /&gt;   return Integer.valueOf(contentLine).intValue();&lt;br /&gt;  } catch (Exception ex) {&lt;br /&gt;   return 0;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;Also create a class called ParameterCollection which will hold the parameters to the web method invoked by the Java invoker:&lt;br /&gt;&lt;br /&gt;public class ParameterCollection {&lt;br /&gt; private String[][] _params;&lt;br /&gt; int _index;&lt;br /&gt; int _size = 10;&lt;br /&gt; &lt;br /&gt; public ParameterCollection()&lt;br /&gt; {&lt;br /&gt;  _params = new String[_size][2];&lt;br /&gt;  _index = 0;&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; public void Add(String param_name, String param_value)&lt;br /&gt; {&lt;br /&gt;  if (_index &gt;= _size)&lt;br /&gt;  {&lt;br /&gt;   IncreaseSize();&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  _params[_index][0] = param_name;&lt;br /&gt;  _params[_index][1] = param_value;&lt;br /&gt;  &lt;br /&gt;  ++ _index;&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; public String[] Get(int index) throws Exception&lt;br /&gt; {&lt;br /&gt;  String[] retVal = new String[2];&lt;br /&gt;  &lt;br /&gt;  if (index &gt;= _index)&lt;br /&gt;  {&lt;br /&gt;   // Out of bounds&lt;br /&gt;   throw new Exception("Parameter Collection index is out of bounds: ParameterCollection.Get(" + index + ")");&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  retVal[0] = _params[index][0];&lt;br /&gt;  retVal[1] = _params[index][1];&lt;br /&gt;&lt;br /&gt;  return retVal;&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; public int size()&lt;br /&gt; {&lt;br /&gt;  return _index;&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; private void IncreaseSize()&lt;br /&gt; {&lt;br /&gt;  String[][] sOld = _params;&lt;br /&gt;  _size *= 2;&lt;br /&gt;  &lt;br /&gt;  _params = new String[_size][2];&lt;br /&gt;&lt;br /&gt;  for (int i = 0; i &lt; _size/2; ++i)&lt;br /&gt;  {&lt;br /&gt;   _params[i][0] = sOld[i][0];&lt;br /&gt;   _params[i][1] = sOld[i][1];&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Next, after using the Visual Studio ASP.NET web service project wizard to create a web service project, add the following attribute to the class declaration of the class representing the web service:&lt;br /&gt;&lt;br /&gt;[SoapDocumentService(RoutingStyle=SoapServiceRoutingStyle.RequestElement)]&lt;br /&gt;&lt;br /&gt;The class declaration should look something like:&lt;br /&gt;[WebService(Namespace = "http://tempuri.org/")]&lt;br /&gt;    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]&lt;br /&gt;    [ToolboxItem(false)]&lt;br /&gt;    [SoapDocumentService(RoutingStyle=SoapServiceRoutingStyle.RequestElement)]&lt;br /&gt;    public class Service1 : System.Web.Services.WebService&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;In the java class that will invoke the web service, create an instance of the WebServiceInvoker class:&lt;br /&gt;&lt;br /&gt;WebServiceInvoker proxy = new WebServiceInvoker(webserviceservername, port, webserviceurl);&lt;br /&gt;&lt;br /&gt;Create a list of parameters and populate appropriately:&lt;br /&gt;ParameterCollection() oColl = new ParameterCollection();&lt;br /&gt;oCall.Add(“parameter1”, “value1”);&lt;br /&gt;&lt;br /&gt;Then use this as a parameter to the invokeRPC method of the web service proxy class:&lt;br /&gt;&lt;br /&gt;Proxy.invokeRPC(“Name of web method to call”, oCall);&lt;br /&gt;&lt;br /&gt;In IIS, make sure the ASP.NET web service accepts anonymous connections, and set which user the anonymous connections will use to access resources.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1576883349115542732-8004330488057783159?l=derricksweng.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derricksweng.blogspot.com/feeds/8004330488057783159/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1576883349115542732&amp;postID=8004330488057783159' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1576883349115542732/posts/default/8004330488057783159'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1576883349115542732/posts/default/8004330488057783159'/><link rel='alternate' type='text/html' href='http://derricksweng.blogspot.com/2010/03/calling-aspnet-web-service-from-java.html' title='Calling an ASP.NET web service from a Java application'/><author><name>Derrick</name><uri>http://www.blogger.com/profile/14369897513473171282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1576883349115542732.post-1433776794148628157</id><published>2009-10-23T14:45:00.000-07:00</published><updated>2009-10-23T14:47:39.630-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='XCode'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='development'/><category scheme='http://www.blogger.com/atom/ns#' term='Mac'/><category scheme='http://www.blogger.com/atom/ns#' term='iTouch'/><category scheme='http://www.blogger.com/atom/ns#' term='app_id'/><category scheme='http://www.blogger.com/atom/ns#' term='certificate'/><category scheme='http://www.blogger.com/atom/ns#' term='provisioning'/><category scheme='http://www.blogger.com/atom/ns#' term='Apple'/><title type='text'>Deploying applications to iPhone/iTouch via XCode</title><content type='html'>To complete all these steps, you must be set up as the Agent of you iPhone Developer Portal.  I’m also assuming that you paid the subscription fee so that you are allowed access.&lt;br /&gt;&lt;br /&gt;Go to &lt;a href="http://developer.apple.com/iphone"&gt;http://developer.apple.com/iphone&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Log in as yourself.  Then click on the iPhone Developer Program Portal link.&lt;br /&gt;&lt;br /&gt;1)  &lt;strong&gt;Get the Development Certificate&lt;/strong&gt;:&lt;br /&gt;Create the certificate request using the Keychain Access utility on your Mac.&lt;br /&gt;Submit it to the iPhone Developer Program website.&lt;br /&gt;Download the generated certificate.&lt;br /&gt;&lt;br /&gt;2)  &lt;strong&gt;Register your device&lt;/strong&gt;:&lt;br /&gt;Go to the Devices tab in the Program Portal and register your device.&lt;br /&gt;&lt;br /&gt;3)  &lt;strong&gt;Create the App ID&lt;/strong&gt;:&lt;br /&gt;Click on the New App ID button.&lt;br /&gt;Enter a description or name for your application.&lt;br /&gt;If this is the first time creating an App ID, leave the bundle seed ID to Generate New.&lt;br /&gt;Put * for the Bundle Identifier.&lt;br /&gt;You will see an ID for the App created under the ID column beside the application name of your choice.  Note this alphanumeric string as you will need it for later.  For this example, let’s assume the App ID is 123D4EFGHI.&lt;br /&gt;&lt;br /&gt;4)  &lt;strong&gt;Create the Provisioning Profile&lt;/strong&gt;:&lt;br /&gt;Click on New Profile.&lt;br /&gt;Enter a profile name that you will remember.&lt;br /&gt;Check all the certificates you wish included, including the certificate you created in step 1.&lt;br /&gt;Select the App ID you created in step 3.&lt;br /&gt;Check the devices you wish to install this application on for development purposes.&lt;br /&gt;Download the provisioning profile to your Mac.&lt;br /&gt;&lt;br /&gt;5)  &lt;strong&gt;Install the Development Certificate&lt;/strong&gt;:&lt;br /&gt;Start Keychain Access on your Mac.&lt;br /&gt;Doubleclick on the login keychain on the top left hand panel, which should also be your default keychain.&lt;br /&gt;Go to the File menu and select Import Items…&lt;br /&gt;Select the Development Certificate downloaded in step 1, and make sure the Destination Keychain is set to login.&lt;br /&gt;To ensure the certificate is correct, view it in the Certificates Category for the login keychain.  There should be a widget beside it that when you click on it, a private key should appear below the certificate.&lt;br /&gt;&lt;br /&gt;6)  &lt;strong&gt;Installing the Provisioning Profile&lt;/strong&gt;:&lt;br /&gt;Start XCode, go to the Window menu item and click on Organizer.&lt;br /&gt;Make sure you are on the Summary tab.&lt;br /&gt;Under the Devices topic, you should see your device if it has been registered properly and is now attached to your Mac.&lt;br /&gt;The summary panel is split into 2 halves: the top half is for your device and the bottom half is called Provisioning.&lt;br /&gt;Click on the + icon in the bottom half and open the provisioning profile downloaded from step 4.&lt;br /&gt;Next, in the iPhone Development topic, there should be a subtopic named Provisioning Profiles.&lt;br /&gt;Click on Provisioning Profiles.&lt;br /&gt;The panel on the right should be divided into 3 sections, with the top section having 2 columns: Name and Expiration Date.&lt;br /&gt;Find the provisioning profile you downloaded in step 4 and drag it into the top section.&lt;br /&gt;&lt;br /&gt;7)  &lt;strong&gt;XCode Project Settings&lt;/strong&gt;:&lt;br /&gt;Go to Project menu item in XCode and choose Edit Project Settings.&lt;br /&gt;In the settings window, select the Build tab.&lt;br /&gt;Make sure the Base SDK in the Architectures section matches your device.&lt;br /&gt;In Code Signing, go to the Code Signing Identity section and select Any iPhone OS Device.&lt;br /&gt;The value should be the exact same as your Developer certificate’s CN, and should be of the form: iPhone Developer: Firstname Lastname (32SDKRR55I)&lt;br /&gt;Go back to the Project menu and select Edit Active Target.&lt;br /&gt;Click on the Properties tab.&lt;br /&gt;In the Identifier field, make sure it starts with the App ID you created in step 3: for example: 123D4EFGHI.com.apple.samplecode&lt;br /&gt;&lt;br /&gt;8)  &lt;strong&gt;Deploy to the iPhone device&lt;/strong&gt;&lt;br /&gt;Click on the Build and Go button.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1576883349115542732-1433776794148628157?l=derricksweng.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derricksweng.blogspot.com/feeds/1433776794148628157/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1576883349115542732&amp;postID=1433776794148628157' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1576883349115542732/posts/default/1433776794148628157'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1576883349115542732/posts/default/1433776794148628157'/><link rel='alternate' type='text/html' href='http://derricksweng.blogspot.com/2009/10/deploying-applications-to-iphoneitouch.html' title='Deploying applications to iPhone/iTouch via XCode'/><author><name>Derrick</name><uri>http://www.blogger.com/profile/14369897513473171282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1576883349115542732.post-5310177913945306139</id><published>2009-06-07T17:42:00.000-07:00</published><updated>2009-06-07T17:45:37.577-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='exception'/><category scheme='http://www.blogger.com/atom/ns#' term='LINQ'/><title type='text'>Troubleshooting LINQ exceptions.</title><content type='html'>Once I was testing a deployment I made of an application that used LINQ to SQL, and I received the following exception when I ran it:&lt;br /&gt;&lt;br /&gt;Exception thrown: at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection) at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection) at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj) at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj) at &lt;br /&gt;System.Data.SqlClient.SqlDataReader.ConsumeMetaData() at System.Data.SqlClient.SqlDataReader.get_MetaData() at &lt;br /&gt;System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString) at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, &lt;br /&gt;RunBehavior runBehavior, Boolean returnStream, Boolean async) at System.Data.SqlClient.SqlCommand.RunExecuteReader&lt;br /&gt;(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result) at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method) at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method) at System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior) at System.Data.Common.DbCommand.ExecuteReader() at System.Data.Linq.SqlClient.SqlProvider.Execute(Expression query, QueryInfo queryInfo, IObjectReaderFactory factory, Object[] parentArgs, Object[] userArgs, ICompiledSubQuery[] subQueries, Object lastResult) at System.Data.Linq.SqlClient.SqlProvider.ExecuteAll(Expression query, QueryInfo[] &lt;br /&gt;queryInfos, IObjectReaderFactory factory, Object[] userArguments, ICompiledSubQuery[] subQueries) at System.Data.Linq.SqlClient.SqlProvider.System.Data.Linq.Provider.IProvider.Execute(Expression query) at System.Data.Linq.DataQuery`1.System.Collections.Generic.IEnumerable.GetEnumerator() at &lt;br /&gt;System.Collections.Generic.List`1..ctor(IEnumerable`1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source) at Canwest.Broadcasting.Windows.Forms.Translator.translatePlaylistsForChannels() at &lt;br /&gt;Canwest.Broadcasting.Windows.Forms.Translator.translateAllPlaylistFiles() at &lt;br /&gt;Canwest.Broadcasting.Web.PlaylistAsrun.PlaylistAsrunService.RunTranslation() in &lt;br /&gt;H:\ProgramCode\Win32_Applications\PlaylistAsrunTranslator\PlaylistAsrunASPNETWebService\PlaylistAsrunService.asmx.cs:line 151&lt;br /&gt;&lt;br /&gt;This exception was being thrown at the point where I was dumping the results of a LINQ to SQL result set to a List&lt;T&gt; by using the ToList&lt;T&gt;() method.  When digging deeper, I realized the result set being returned was based on a template in the LINQ designer (dbml) file, which expected the SQL table to have one more column then the table actually contained.  I added the missing column to the table and the error message went away.&lt;br /&gt;&lt;br /&gt;The lesson is: when receiving exceptions like the above, check to make sure the database tables on the database server match what is shown in the LINQ designer view.  One can just use SQL Management Studio to make a graphical comparison.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1576883349115542732-5310177913945306139?l=derricksweng.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derricksweng.blogspot.com/feeds/5310177913945306139/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1576883349115542732&amp;postID=5310177913945306139' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1576883349115542732/posts/default/5310177913945306139'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1576883349115542732/posts/default/5310177913945306139'/><link rel='alternate' type='text/html' href='http://derricksweng.blogspot.com/2009/06/troubleshooting-linq-exceptions.html' title='Troubleshooting LINQ exceptions.'/><author><name>Derrick</name><uri>http://www.blogger.com/profile/14369897513473171282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1576883349115542732.post-2984991371986351695</id><published>2009-05-25T18:10:00.000-07:00</published><updated>2009-05-25T18:11:22.313-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DataTable'/><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='AutoIncrement'/><category scheme='http://www.blogger.com/atom/ns#' term='AutoIncrementStep'/><category scheme='http://www.blogger.com/atom/ns#' term='AutoIncrementSeed'/><title type='text'>Behaviour of the AutoIncrementSeed property</title><content type='html'>Oftentimes we will need to reset the seed to 0 when refilling a DataTable with new data.  Due to the implementation of set_AutoIncrementSeed, we cannot just set AutoIncrementSeed = 0.  We must do the following:&lt;br /&gt;AutoIncrementStep = -1;&lt;br /&gt;AutoIncrementSeed = 1;&lt;br /&gt;AutoIncrementStep = 1;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1576883349115542732-2984991371986351695?l=derricksweng.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derricksweng.blogspot.com/feeds/2984991371986351695/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1576883349115542732&amp;postID=2984991371986351695' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1576883349115542732/posts/default/2984991371986351695'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1576883349115542732/posts/default/2984991371986351695'/><link rel='alternate' type='text/html' href='http://derricksweng.blogspot.com/2009/05/behaviour-of-autoincrementseed-property.html' title='Behaviour of the AutoIncrementSeed property'/><author><name>Derrick</name><uri>http://www.blogger.com/profile/14369897513473171282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1576883349115542732.post-4540778002913782150</id><published>2009-04-11T16:41:00.000-07:00</published><updated>2009-08-26T18:32:55.024-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DirectoryServices'/><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='WMI'/><category scheme='http://www.blogger.com/atom/ns#' term='thread'/><category scheme='http://www.blogger.com/atom/ns#' term='Directory'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='Active'/><title type='text'>Programmatically renaming a computer using C#</title><content type='html'>Recently I was presented with an interesting problem: to develop an application that would rename multiple computers for the end user.&lt;br /&gt;&lt;br /&gt;The user requirements were straight-forward enough:&lt;br /&gt;1)       Display a list of all computers in active directory from the SMS database.&lt;br /&gt;2)      Allow the user to select multiple computers and assign their new names.&lt;br /&gt;3)      Rename all these computers.&lt;br /&gt;&lt;br /&gt;However, the technical requirements and subsequent architecture were much more involved.  The tool required the ability to rename multiple computers at once, and had to do all the operations in-process so as to return error information to the user.  After some googling, I figured the easiest way to accomplish this was to spawn multiple threads, each of which would run a separate computer renaming operation, then inform the user to reboot their machine if the renaming was successful.&lt;br /&gt;&lt;br /&gt;The easiest way to create the multiple threads was to use a ThreadPool, and then load it up with all the renaming operations as individual work items.  This part was simple enough, because there was no need to coordinate between the multiple threads.  The program only had to wait until all the threads had finished running.&lt;br /&gt;&lt;br /&gt;The challenge came in renaming the machines in-process.  The easiest way to do this would be through directory services, but I could not determine how to get the correct information from the SMS databases to put together the proper LDAP URL with OUs.  In addition, for Directory Services to work on a machine, it must be at least XP service pack 3 level, which my IT department could not guarantee for all machines in the company.  So after many days of painful googling and experimentation, I came up with a 3 step approach:&lt;br /&gt;1)       Add a new local user to the computer to be renamed and make it part of the local administrators group using Directory Services.&lt;br /&gt;2)      Rename the active directory object corresponding to the computer to the new name via Directory Services.&lt;br /&gt;3)      Unjoin the computer from the domain, than use the local user from step 1 to rename it and rejoin it to the domain, all with WMI (Windows Management Instrumentation).&lt;br /&gt;&lt;br /&gt;Why the 3 step approach?  Since WMI works with Windows operating systems below XP service pack 3, it was the required choice for the renaming portion.  However, the WMI renaming bit works by remotely invoking the Rename method of the target computer’s local Win32_ComputerSystem object, and that method only runs if the computer is unjoined to the domain.  Therefore, in order to call the Rename method after unjoining the computer from the domain, the WMI ManagementObject must connect and authenticate to the target computer using a local administrator, hence the need for step 1.  To rejoin to the domain after the renaming operation, the application needs a domain user account that has permissions to join machines to active directory, and the target computer must find an active directory object that matches its name.  Hence the need for step 2.&lt;br /&gt;&lt;br /&gt;Enough of the high level explanation of how the tool will work; after all, I’m sure if you really are reading this blog, you are looking for source code, right?  Here’s the source for step 1:&lt;br /&gt;&lt;br /&gt;        public Boolean addUserByDirectoryServices(String machineName, String pcAdministrator, String pcAdministratorPassword)&lt;br /&gt;        {&lt;br /&gt;            Boolean rc = true;&lt;br /&gt;&lt;br /&gt;            try&lt;br /&gt;            {&lt;br /&gt;                String connString = "WinNT://" + machineName;&lt;br /&gt;&lt;br /&gt;                using (DirectoryEntry de = new DirectoryEntry(connString, pcAdministrator, pcAdministratorPassword))&lt;br /&gt;                {&lt;br /&gt;&lt;br /&gt;                    //if (de.Children.Find(m_PcAdministrator) != null)&lt;br /&gt;                    //{&lt;br /&gt;                    //    de.Close();&lt;br /&gt;                    //    de.Dispose();&lt;br /&gt;                    //    return true;&lt;br /&gt;                    //}&lt;br /&gt;&lt;br /&gt;                    DirectoryEntry user = de.Children.Add(m_PcAdministrator, "user");&lt;br /&gt;                    user.Invoke("SetPassword", new Object[] { m_PcAdministratorPassword });&lt;br /&gt;                    user.CommitChanges();&lt;br /&gt;&lt;br /&gt;                    de.RefreshCache();&lt;br /&gt;&lt;br /&gt;                    DirectoryEntry adminGroup = de.Children.Find("Administrators", "group");&lt;br /&gt;                    if (null != adminGroup)&lt;br /&gt;                    {&lt;br /&gt;                        adminGroup.Invoke("Add", new Object[] { user.Path.ToString() });&lt;br /&gt;                    }&lt;br /&gt;&lt;br /&gt;                    de.Close();&lt;br /&gt;                    de.Dispose();&lt;br /&gt;&lt;br /&gt;                }&lt;br /&gt;&lt;br /&gt;                rc = true;&lt;br /&gt;               &lt;br /&gt;&lt;br /&gt;            }&lt;br /&gt;            catch (Exception e)&lt;br /&gt;            {&lt;br /&gt;                String msg = e.Message;&lt;br /&gt;                m_error_msg += "Adding local user error: " + msg;&lt;br /&gt;                String stacktrace = e.StackTrace;&lt;br /&gt;                m_stacktrace += "\nAdding local user dump: " + stacktrace;&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            return rc;&lt;br /&gt;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;In the beginning, you will notice I use the URL WinNT:// as opposed to LDAP:// to locate the machine via Directory Services.  This is because I could not compute the proper LDAP query string.  You will also notice I commented out a check to determine if I already added the user, and just catch the exception.  I did this because I found that the check to see if the user already existed always threw an exception, whether the user existed or not.  Therefore, in using this method, just catch exceptions that are thrown and ignore them, or display them to the user.  I also encapsulate the DirectoryEntry object representing the target machine in an using block, and call its Dispose() method at the end because before, I was constantly getting errors stating I had multiple connections open on the target machine, which were not supported.  Those errors can also be ignored it you get them; they bear no significance as to whether the new local user was created or not.&lt;br /&gt;&lt;br /&gt;For step 2, here is the code to rename the object in active directory, also using the Directory Services methods and API:&lt;br /&gt;&lt;br /&gt;        public Boolean renameMachineByDirectoryServices(String oldname, String newname, String administrator, String administratorPassword)&lt;br /&gt;        {&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;            Boolean rc = true;&lt;br /&gt;&lt;br /&gt;            try&lt;br /&gt;            {&lt;br /&gt;                DirectoryEntry machineNode = null;&lt;br /&gt;                machineNode = new DirectoryEntry("WinNT://" + oldname);&lt;br /&gt;                machineNode.Username = administrator;&lt;br /&gt;                machineNode.Password = administratorPassword;&lt;br /&gt;                machineNode.AuthenticationType = AuthenticationTypes.Secure;&lt;br /&gt;                machineNode.Rename("CN=" + newname);&lt;br /&gt;                machineNode.CommitChanges();&lt;br /&gt;&lt;br /&gt;               &lt;br /&gt;            }&lt;br /&gt;            catch (Exception e)&lt;br /&gt;            {&lt;br /&gt;                String msg = e.Message;&lt;br /&gt;                String stacktrace = e.StackTrace;&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            return rc;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;This was a simple and straightforward method to code.  All it did was rename the object in active directory.  Also, any exceptions thrown can be ignored; they have no bearing on whether the operation was successful or not.  If you don’t believe me, try it.&lt;br /&gt;&lt;br /&gt;Finally, the code for step 3 was much more complicated:&lt;br /&gt;&lt;br /&gt;        public Boolean renameRemotePC(String oldName, String newName, String domain)&lt;br /&gt;        {&lt;br /&gt;&lt;br /&gt;            Boolean rc = true;&lt;br /&gt;&lt;br /&gt;            try&lt;br /&gt;            {&lt;br /&gt;&lt;br /&gt;                ManagementPath remoteControlObject = new ManagementPath();&lt;br /&gt;                remoteControlObject.ClassName = "Win32_ComputerSystem";&lt;br /&gt;                remoteControlObject.Server = oldName;&lt;br /&gt;                remoteControlObject.Path = oldName + "\\root\\cimv2:Win32_ComputerSystem.Name='" + oldName + "'";&lt;br /&gt;                remoteControlObject.NamespacePath = "\\\\" + oldName + "\\root\\cimv2";&lt;br /&gt;&lt;br /&gt;                ConnectionOptions conn = new ConnectionOptions();&lt;br /&gt;                conn.Authentication = AuthenticationLevel.PacketPrivacy;&lt;br /&gt;                conn.Username = oldName + "\\" + m_PcAdministrator;&lt;br /&gt;                conn.Password = m_PcAdministratorPassword;&lt;br /&gt;&lt;br /&gt;                ManagementScope remoteScope = new ManagementScope(remoteControlObject, conn);&lt;br /&gt;&lt;br /&gt;                ManagementObject remoteSystem = new ManagementObject(remoteScope, remoteControlObject, null);&lt;br /&gt;               &lt;br /&gt;                ManagementBaseObject outParams;&lt;br /&gt;&lt;br /&gt;                ManagementBaseObject unjoinFromDomain = remoteSystem.GetMethodParameters("UnjoinDomainOrWorkgroup");&lt;br /&gt;                unjoinFromDomain.SetPropertyValue("Password", m_domain_admin_password);&lt;br /&gt;                unjoinFromDomain.SetPropertyValue("UserName", m_domain_admin);&lt;br /&gt;                outParams = remoteSystem.InvokeMethod("UnjoinDomainOrWorkgroup", unjoinFromDomain, null);               &lt;br /&gt;&lt;br /&gt;                ManagementBaseObject newRemoteSystemName = remoteSystem.GetMethodParameters("Rename");&lt;br /&gt;                InvokeMethodOptions methodOptions = new InvokeMethodOptions();&lt;br /&gt;&lt;br /&gt;                newRemoteSystemName.SetPropertyValue("Name", newName);&lt;br /&gt;                newRemoteSystemName.SetPropertyValue("UserName", m_PcAdministrator);&lt;br /&gt;                newRemoteSystemName.SetPropertyValue("Password", m_PcAdministratorPassword);&lt;br /&gt;&lt;br /&gt;                methodOptions.Timeout = new TimeSpan(0, 10, 0);&lt;br /&gt;                outParams = remoteSystem.InvokeMethod("Rename", newRemoteSystemName, null);&lt;br /&gt;&lt;br /&gt;                ManagementBaseObject joinFromDomain = remoteSystem.GetMethodParameters("JoinDomainOrWorkgroup");&lt;br /&gt;                joinFromDomain.SetPropertyValue("Name", domain);&lt;br /&gt;                joinFromDomain.SetPropertyValue("Password", m_domain_admin_password);&lt;br /&gt;                joinFromDomain.SetPropertyValue("UserName", m_domain_admin);&lt;br /&gt;                joinFromDomain.SetPropertyValue("FJoinOptions", 1);&lt;br /&gt;                outParams = remoteSystem.InvokeMethod("JoinDomainOrWorkgroup", joinFromDomain, null);               &lt;br /&gt;&lt;br /&gt;            }&lt;br /&gt;            catch (ManagementException MgEx)&lt;br /&gt;            {&lt;br /&gt;                String mgs = MgEx.Message;&lt;br /&gt;                String coredump = MgEx.StackTrace;&lt;br /&gt;                m_error_msg += "\nRenaming PC Error: " + mgs;&lt;br /&gt;                m_stacktrace += "\nRenaming PC dump: " + coredump;&lt;br /&gt;            }&lt;br /&gt;            catch (Exception e)&lt;br /&gt;            {&lt;br /&gt;                String mgs = e.Message;&lt;br /&gt;                String coredump = e.StackTrace;&lt;br /&gt;                m_error_msg += "\nRenaming PC Error: " + mgs;&lt;br /&gt;                m_stacktrace += "\nRenaming PC dump: " + coredump;&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            return rc;&lt;br /&gt;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;The first piece of this method was creating a ManagementObject that could connect to the target computer and instantiate an object of class Win32_ComputerSystem on that machine.  The first thing I learnt was that in order to do so, the ConnectionOptions had to be set to use an authentication level of PacketPrivacy.  This was the only way I was allowed to connect to another machine using WMI.  After that, it was just a matter of instantiating ManagementBaseObjects for the required methods:  UnjoinDomainOrWorkgroup, Rename, and JoinDomainOrWorkgroup.  The required parameters and recommended values you can find in the code, so I won’t go into too much detail here, except that once again, exceptions can be safely ignored.  Don’t ask me why, but they can.&lt;br /&gt;&lt;br /&gt;All in all, I felt this application was a great way to learn about different aspects of IT administration and how to automate them.  It was my first exposure to WMI and Directory Services development, and given the increasing focus on security nowadays, probably not my last.  As a wrap up, I felt this assignment was also a good application of software engineering principles, as I had to gather the user requirements and technical requirements on my own, then architect and build the solution, test it, and deploy it.&lt;br /&gt;&lt;br /&gt;The class that contains the methods above, which I called WMIWrapper, can be found in my codeplex project &lt;a href="http://www.codeplex.com/tv"&gt;http://www.codeplex.com/tv&lt;/a&gt;.  Of course these methods can be used in any IT environment, but so far I have only been asked to do this by a television broadcaster.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1576883349115542732-4540778002913782150?l=derricksweng.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derricksweng.blogspot.com/feeds/4540778002913782150/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1576883349115542732&amp;postID=4540778002913782150' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1576883349115542732/posts/default/4540778002913782150'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1576883349115542732/posts/default/4540778002913782150'/><link rel='alternate' type='text/html' href='http://derricksweng.blogspot.com/2009/04/programmatically-renaming-computer.html' title='Programmatically renaming a computer using C#'/><author><name>Derrick</name><uri>http://www.blogger.com/profile/14369897513473171282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1576883349115542732.post-3891256100671011859</id><published>2009-03-21T08:01:00.000-07:00</published><updated>2009-03-21T08:03:06.871-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='TextBox'/><category scheme='http://www.blogger.com/atom/ns#' term='event'/><category scheme='http://www.blogger.com/atom/ns#' term='AJAX'/><category scheme='http://www.blogger.com/atom/ns#' term='control'/><category scheme='http://www.blogger.com/atom/ns#' term='FilteredTextBoxExtender'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='ReorderList'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><category scheme='http://www.blogger.com/atom/ns#' term='DataBound'/><category scheme='http://www.blogger.com/atom/ns#' term='DataBind'/><title type='text'>Binding the AJAX FilteredTextboxExtender control to a Textbox control located within a ReorderList control.</title><content type='html'>I recently ran into a challenge where I had to filter out non-numeric input from a textbox control that was contained in a ReorderList’s ItemTemplate section.  We wanted to filter out the keystrokes as the user was typing them instead of waiting for a postback to check the data in the field.&lt;br /&gt;&lt;br /&gt;At first I tried dragging the control onto the page and setting the TargetControlID to the ID of the textboxes but when new items were added to the ReorderList, those ID’s would change.  Thus my FilteredTextboxExtender could never find the Textbox controls it was supposed to bind to.&lt;br /&gt;&lt;br /&gt;To resolve this, I inserted the FilteredTextboxExtenders dynamically so their TargetControlID’s would always match the correct TextBox controls.  First, I created an event handler for the ReorderList’s DataBound event, as it was that event that always generated new ID’s for the Textbox controls.  Then, in the event handler, I searched the ReorderList’s Items member for the TextBox controls based on the ID’s assigned to them in the markup, using the FindControl() method.  Afterwards, I would instantiate the FilteredTextboxExtenders and set their TargetControlID properties to the UniqueID properties of the Textbox controls.  The code would look like this:&lt;br /&gt;&lt;br /&gt; TextBox multiplier = (TextBox)IngredientDataList.Items[countOfItems - 1].FindControl("New_IngredientMultiplier");&lt;br /&gt;if (null != multiplier)&lt;br /&gt;{&lt;br /&gt;    String idToValidate = multiplier.UniqueID;&lt;br /&gt;    validateMultiplier = new FilteredTextBoxExtender();&lt;br /&gt;    validateMultiplier.TargetControlID = idToValidate;&lt;br /&gt;    validateMultiplier.FilterType = FilterTypes.Custom  FilterTypes.Numbers;&lt;br /&gt;    validateMultiplier.ValidChars = ".";&lt;br /&gt;    IngredientDataList.Controls.Add(validateMultiplier);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;In this example, the bitwise OR operator is used to set the FilterType because the FilterType property is a bit flag, and in this case I had to allow for decimal numbers, so I only let the users enter numbers and a decimal point.  As another aside, notice how in my if statement I put the null before the variable I’m checking?  This is a defensive programming concept I learnt while at an interview at Microsoft.  The purpose for this is if I accidentally forget the exclamation mark, the compilation would fail and I would catch the error immediately.  However, if the variable was in front and I forgot the exclamation mark, the line would read:&lt;br /&gt;If (multiplier = null)&lt;br /&gt;Which would always evaluate to true because now it is an assignment as opposed to a condition.&lt;br /&gt;&lt;br /&gt;What’s important is that I set the TargetControlID property to the UniqueID property of the Textbox controls, as opposed to just the ID property.  It must be UniqueID because this property is assigned by ASP.NET so the FilteredTextBox control will be bound to the proper control after a ReorderList.DataBind() call.  The ID property is assigned by the developer and may not actually be the ID of the control if a new item is inserted into the ReorderList.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1576883349115542732-3891256100671011859?l=derricksweng.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derricksweng.blogspot.com/feeds/3891256100671011859/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1576883349115542732&amp;postID=3891256100671011859' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1576883349115542732/posts/default/3891256100671011859'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1576883349115542732/posts/default/3891256100671011859'/><link rel='alternate' type='text/html' href='http://derricksweng.blogspot.com/2009/03/binding-ajax-filteredtextboxextender.html' title='Binding the AJAX FilteredTextboxExtender control to a Textbox control located within a ReorderList control.'/><author><name>Derrick</name><uri>http://www.blogger.com/profile/14369897513473171282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1576883349115542732.post-4043021612961686572</id><published>2009-02-28T08:02:00.000-08:00</published><updated>2009-02-28T08:05:09.961-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DataTable'/><category scheme='http://www.blogger.com/atom/ns#' term='DataGrid'/><category scheme='http://www.blogger.com/atom/ns#' term='GridControl'/><category scheme='http://www.blogger.com/atom/ns#' term='DataRow'/><category scheme='http://www.blogger.com/atom/ns#' term='Row'/><category scheme='http://www.blogger.com/atom/ns#' term='GridView'/><category scheme='http://www.blogger.com/atom/ns#' term='DevXpress'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='ImportRow'/><title type='text'>Handling multiple selects in a grid control that supports grouping and sorting</title><content type='html'>Currently in an application I am coding, I have a grid control that displays rows from a database table and allows me to group by column values in the table.  For example, if the grid is displaying columns C1, C2 and C3, I can group the rows by the values of C1 by dragging that column name to the top panel of the grid control.  So if C1 was the column for city, I can group all the Toronto records together.&lt;br /&gt;&lt;br /&gt;This application must also support multiple selects.  For example, when I click on 2-3 rows of the grid, I must be able to extract these rows in another class for processing.  The grid control I am using, known as DevXpress, contains a method int[] GetSelectedRows(), which returns the row indices of all the rows in the grid selected by the user.  So if I fill the grid with a DataSet based on a database table and select multiple rows, I can get the row indices and use those to extract the desired rows from the DataSet  and use them to  load another DataTable.  Try this out on a regular DevXpress GridControl without the grouping and notice it returns the correct rows.  The C# code is sort of like this:&lt;br /&gt;&lt;br /&gt;DataTable dt2 = gridCtrlDataSet.m_dataTable.Clone();&lt;br /&gt;Int[] selectedRows = gridCtrl.GetSelectedRows();&lt;br /&gt;Foreach (int index in selectedRows)&lt;br /&gt;{&lt;br /&gt;     Dt2.ImportRow(gridCtrlDataSet.m_dataTable.Rows[index]);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;Note I specifically clone the DataSet’s DataTable and assign it to the target DataTable.  This is required if you wish to use the DataTable.ImportRow() method.&lt;br /&gt;&lt;br /&gt;In the above example, I specifically stated to not use any column groupings.  I did this to illustrate a point.  Try grouping the rows by one of the column values by dragging the column name onto the top panel of the GridControl.  Select a few rows on the GridControl and use GetSelectedRows() to get their indices, and use these indices to get the desired rows from the DataSet bound to the Database table.  If you look at the rows, you will notice they are not the same ones as you selected in the grouped GridControl.  That’s because when the GridControl was grouped, the indices of the rows in the GridControl’s GridView changed, so they no longer corresponded with the indices in the bound DataSet.&lt;br /&gt;&lt;br /&gt;How do we get the selected rows from the DataSet?&lt;br /&gt;Before binding the DataSet to the database table, create an extra column to hold the row indices, and make it an identity column that auto-increments.  Then, after calling the GetSelectedRows() method to get the indices of the selected DataGrid rows, get the index column and use those indices to get the correct rows from the DataSet.  Upon doing this, you will find that you can get the rows you really selected in the GridControl.  The C# code is sort of like this:&lt;br /&gt;&lt;br /&gt;DataTable dt2 = gridCtrlDataSet.m_dataTable.Clone();&lt;br /&gt;Int[] selectedRows = gridCtrl.GetSelectedRows();&lt;br /&gt;Foreach (int i in selectedRows)&lt;br /&gt;{&lt;br /&gt;    Int index = gridCtrView.DataRowView[i].Row[“ID”];&lt;br /&gt;    Dt2.ImportRow(gridCtrlDataSet.m_dataTable.Rows[index]);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;What is the moral of the story?&lt;br /&gt;Be sure to understand the behaviour of your GridControls before assuming what their methods for return selected rows actually do return.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1576883349115542732-4043021612961686572?l=derricksweng.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derricksweng.blogspot.com/feeds/4043021612961686572/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1576883349115542732&amp;postID=4043021612961686572' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1576883349115542732/posts/default/4043021612961686572'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1576883349115542732/posts/default/4043021612961686572'/><link rel='alternate' type='text/html' href='http://derricksweng.blogspot.com/2009/02/handling-multiple-selects-in-grid.html' title='Handling multiple selects in a grid control that supports grouping and sorting'/><author><name>Derrick</name><uri>http://www.blogger.com/profile/14369897513473171282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1576883349115542732.post-616478821004798539</id><published>2009-01-23T19:00:00.000-08:00</published><updated>2009-01-24T08:55:18.648-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WPF'/><category scheme='http://www.blogger.com/atom/ns#' term='DAB'/><category scheme='http://www.blogger.com/atom/ns#' term='Documentum'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='Application Builder'/><category scheme='http://www.blogger.com/atom/ns#' term='DFC'/><category scheme='http://www.blogger.com/atom/ns#' term='WCF'/><title type='text'>Value Assistance Management without DAB Pattern</title><content type='html'>&lt;a href="http://2.bp.blogspot.com/_Fj8twxGO7qk/SXqF7-saDtI/AAAAAAAAAAU/BomePNCjBp8/s1600-h/ValueAssistanceAdminPattern.jpg"&gt;&lt;img id="BLOGGER_PHOTO_ID_5294691577415470802" style="DISPLAY: block; MARGIN: 0px auto 10px; WIDTH: 320px; CURSOR: hand; HEIGHT: 294px; TEXT-ALIGN: center" alt="" src="http://2.bp.blogspot.com/_Fj8twxGO7qk/SXqF7-saDtI/AAAAAAAAAAU/BomePNCjBp8/s320/ValueAssistanceAdminPattern.jpg" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;div&gt;Providing non-technical end user ability to modify value assistance list of a custom attribute without giving them access to Application Builder.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Problem&lt;/strong&gt;:&lt;br /&gt;Due to the complex workflows and nature of the broadcast business, Documentum needs to be customized for television broadcast engineering in order to use it as a media management system. Part of the customization includes representing different episodes as objects in the repository, and storing cuesheet information of each episode as an attribute. One of the required attributes is closed captioning information, which can only be one of a pre-defined list of values. These values may require modification from time to time by the broadcast engineering staff. This issue can be resolved using a vanilla Documentum install by adding the predefined list of values as a value assistance list to a custom type’s attribute, and allowing the end users access to Documentum Application Builder (DAB) to update the value assistance list. However, DAB encompasses a lot of functionality that is not desirable in a typical end user’s hands. The only other vanilla approach would be to provide the end users with Documentum Query Language (DQL) queries they can enter via Documentum Administrator (DA). This approach also is risky as it requires system administrators to expose DA to non-technical staff and requires training the end users in DQL. Therefore, the only approach is through customization. So how should this customization be implemented?&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Solution&lt;/strong&gt;:&lt;br /&gt;There are several options that can be taken, but most can be summarized into the 2 below:&lt;br /&gt;1) Providing Lookups for Data Fields (this is a pattern already in development at the EMC developer community)&lt;br /&gt;2) Provide a service that encapsulates either the Documentum Foundation Services (DFS) or Documentum Foundation Classes (DFC). The service must also provide a way for clients to execute Documentum Query Language (DQL) required to update the value assistance data for a custom attribute. Then provide a client to consume the service, which can be anything from a thick client to a mobile client.&lt;br /&gt;&lt;br /&gt;Obviously, since Providing Lookups for Data Fields is already a pattern in progress, I will focus on the second option.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Manage Value Assistance Pattern&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Description&lt;/strong&gt;:&lt;br /&gt;The primary business users for this pattern would be operations staff in the television broadcasting industry, though other industry verticals may find use for this pattern as well.&lt;br /&gt;&lt;br /&gt;As a precursor, it is assumed that the current IT environment contains a Documentum repository and a box running either DFS or DFC. Since there is already DFS/DFC in the environment, why does this pattern include a service to encapsulate this functionality? This is to account for the relatively high costs of a Documentum implementation; there are licensing costs associated with every server hosting DFS/DFC.&lt;br /&gt;&lt;br /&gt;Using a design based on services oriented architecture, this pattern will expose the functionality to update the value assistance data for any custom type in a Documentum repository to multiple non-technical users. This will avoid licensing and training costs associated with deploying DAB to multiple desktops and training non-IT staff on how to use DAB.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Functional How-To&lt;/strong&gt;:&lt;br /&gt;The Manage Value Assistance pattern is based on the layered services pattern. First, create a method that accepts the name of a custom type and a custom attribute as parameters, and returns a collection of objects with members for the value, display string and description of each value assistance item. Then, create a method that accepts the name of a custom type, the name of a custom attribute of the custom type, and a collection of value assistance items as described above. This method basically overwrites the existing value assistance data for the custom attribute of the custom type and replaces them with the values contained in the collection passed through as a parameter. Essentially the collection of value assistance data is used to build a string object that will be the DQL query required to run against the repository. Once the DQL query is constructed, the method will run the query against the repository with the PUBLISH keyword to ensure other users have access to the new value assistance data.&lt;br /&gt;&lt;br /&gt;Since this second method will be exposed to multiple users, it must be made re-entrant or thread-safe to prevent multiple users from editing the same custom type and attribute at the same time. As a security precaution, there should be authorization checking implemented here so only users who are supposed to be modifying the value assistance are allowed to. Therefore, this service must never allow anonymous access.&lt;br /&gt;&lt;br /&gt;Next, expose these methods via service consumable by clients hosted on other machines. The favourite choice would be a XML web service but WCF is also a good option. This will enable many users access to this functionality, so it can be shared across the enterprise.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;When to use it&lt;/strong&gt;:&lt;br /&gt;Obviously there are multiple ways to allow non-technical users to gain access to the value assistance data. The database table these values reside in can be updated directly with a stored procedure, saving the trouble of learning the Documentum APIs. Although this is the more efficient way, I do not recommend it as it bypasses a lot of the built in plumbing that Documentum provides. It’s always safer to use the Documentum provided facilities to manipulate it as they naturally invoke required background processes to ensure everything goes smoothly.&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Examples&lt;/strong&gt;:&lt;br /&gt;I implemented an example on &lt;a href="http://www.codeplex.com/documentum"&gt;http://www.codeplex.com/documentum&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The requirements to make the example work are:&lt;br /&gt;Content Server 5.3 SP6.&lt;br /&gt;DFC 5.3 SP6.&lt;br /&gt;Microsoft.NET 3.5 (WCF and WPF).&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1576883349115542732-616478821004798539?l=derricksweng.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derricksweng.blogspot.com/feeds/616478821004798539/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1576883349115542732&amp;postID=616478821004798539' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1576883349115542732/posts/default/616478821004798539'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1576883349115542732/posts/default/616478821004798539'/><link rel='alternate' type='text/html' href='http://derricksweng.blogspot.com/2009/01/value-assistance-management-without-dab.html' title='Value Assistance Management without DAB Pattern'/><author><name>Derrick</name><uri>http://www.blogger.com/profile/14369897513473171282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_Fj8twxGO7qk/SXqF7-saDtI/AAAAAAAAAAU/BomePNCjBp8/s72-c/ValueAssistanceAdminPattern.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1576883349115542732.post-6345732978519149870</id><published>2009-01-16T19:24:00.000-08:00</published><updated>2009-01-16T19:26:10.763-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='playlist'/><category scheme='http://www.blogger.com/atom/ns#' term='recon key'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server'/><category scheme='http://www.blogger.com/atom/ns#' term='S4M'/><category scheme='http://www.blogger.com/atom/ns#' term='LINQ'/><title type='text'>Improving Performance of LINQ Queries</title><content type='html'>Part of the challenge facing television broadcasters is reconciling asrun logs against the playlists they are supposed to represent.  Every once in a while content is changed on the fly so what is actually broadcasted is different from what the original playlist stated.  Part of this reconciliation involves the generation of recon keys, which involve comparing event information of an asrun log against the playlist it was generated for.  In order to compare the original playlists and the resulting asrun logs, I turned to LINQ to get the information.&lt;br /&gt;&lt;br /&gt;One of the LINQ queries I used for generating the recon keys was:&lt;br /&gt;&lt;br /&gt;                var rOriginalEvent = from orgEvents in db.BIP_Playlist_Events&lt;br /&gt;                                     where orgEvents.eventid.Equals(searchStr)&lt;br /&gt;                                     orderby orgEvents.Last_Updated descending&lt;br /&gt;                                     select orgEvents;&lt;br /&gt;&lt;br /&gt;                if (rOriginalEvent.Count() &gt; 0)&lt;br /&gt;                {&lt;br /&gt;                    hasMatchingEvents = true;&lt;br /&gt;                    dlstartDateTime = rOriginalEvent.First().dlstart;&lt;br /&gt;                    orgDlhnumber = rOriginalEvent.First().dlhnumber;&lt;br /&gt;                    orgEventid = rOriginalEvent.First().eventid;&lt;br /&gt;&lt;br /&gt;The purpose of this query was to search archives of the original playlists from which the D-class asrun log was generated to compare the original event id, start time and dlhnumber against the asrun log’s information.  These playlists were archived in a SQL Server 2005 database table, so this LINQ query was using it as the source.  I timed the query and it ran a total of 0s according to my calculations.  However, when I timed the following rOriginalEvent.First() calls, they took a total of .45 seconds.  This does not seem to be a significant amount of time, but when repeated 1500 times for a single transaction, it meant the transaction would last almost 20 minutes.&lt;br /&gt;&lt;br /&gt;Since LINQ automatically generates the most efficient query in the background, I looked for ways to extract the record I wanted directly from the var query.   I then turned to the following LINQ query:&lt;br /&gt;&lt;br /&gt;                var rOriginalEvent = (from orgEvents in db.BIP_Playlist_Events&lt;br /&gt;                                     where orgEvents.eventid.Equals(searchStr)&lt;br /&gt;                                     orderby orgEvents.Last_Updated descending&lt;br /&gt;                                     select orgEvents).FirstOrDefault();&lt;br /&gt;&lt;br /&gt;This would move all the processing into the LINQ SQL connector and extract either the record I wanted or a null object.  The important thing is that I no longer needed the First() call when getting the information:&lt;br /&gt;&lt;br /&gt;                    dlstartDateTime = rOriginalEvent.dlstart;&lt;br /&gt;                    orgDlhnumber = rOriginalEvent.dlhnumber;&lt;br /&gt;                    orgEventid = rOriginalEvent.eventid;&lt;br /&gt;&lt;br /&gt;By moving the costly First() call into the LINQ query itself I reduced the time of the total operation from .45 seconds to .1 seconds, thus a performance improvement of 4.5 times faster than before.&lt;br /&gt;&lt;br /&gt;In conclusion, I recommend that if you are after a single record for a specific set of information, use the above LINQ expression.  The FirstOrDefault() method returns the first element in the collection or the default value if the collection is empty, so you don’t have to worry about dealing with a null object.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1576883349115542732-6345732978519149870?l=derricksweng.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derricksweng.blogspot.com/feeds/6345732978519149870/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1576883349115542732&amp;postID=6345732978519149870' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1576883349115542732/posts/default/6345732978519149870'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1576883349115542732/posts/default/6345732978519149870'/><link rel='alternate' type='text/html' href='http://derricksweng.blogspot.com/2009/01/improving-performance-of-linq-queries.html' title='Improving Performance of LINQ Queries'/><author><name>Derrick</name><uri>http://www.blogger.com/profile/14369897513473171282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1576883349115542732.post-6295623610984687497</id><published>2009-01-10T19:32:00.000-08:00</published><updated>2009-01-17T07:49:21.658-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='CMMI'/><category scheme='http://www.blogger.com/atom/ns#' term='Capability Maturity Model'/><category scheme='http://www.blogger.com/atom/ns#' term='process'/><category scheme='http://www.blogger.com/atom/ns#' term='Software Engineering'/><category scheme='http://www.blogger.com/atom/ns#' term='engineer'/><category scheme='http://www.blogger.com/atom/ns#' term='Carnagie Melon'/><title type='text'>What is Software Engineering?</title><content type='html'>Software engineering is defined as the application of engineering principles and practices to software development with the end-goal of making software development a predictable and controlled discipline.&lt;br /&gt;&lt;br /&gt;CMMI attempts to bring this discipline to software development projects by offering suggestions for roles, responsibilities and processes for software projects.&lt;br /&gt;&lt;br /&gt;However, the goal of this blog is not to discuss process; I'll leave that for my software engineering management blog. This blog will focus on commonly experienced software development problems, and their respective solutions. The reason for this is to reduce the amount of unknowns in software projects to make them more predictable and controlled. Thus this blog will contribute to the software engineering discipline.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1576883349115542732-6295623610984687497?l=derricksweng.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derricksweng.blogspot.com/feeds/6295623610984687497/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1576883349115542732&amp;postID=6295623610984687497' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1576883349115542732/posts/default/6295623610984687497'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1576883349115542732/posts/default/6295623610984687497'/><link rel='alternate' type='text/html' href='http://derricksweng.blogspot.com/2009/01/what-is-software-engineering.html' title='What is Software Engineering?'/><author><name>Derrick</name><uri>http://www.blogger.com/profile/14369897513473171282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1576883349115542732.post-7505395489114649119</id><published>2008-12-25T08:20:00.000-08:00</published><updated>2009-01-16T19:26:43.517-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='XPath'/><category scheme='http://www.blogger.com/atom/ns#' term='playlist'/><category scheme='http://www.blogger.com/atom/ns#' term='XML'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server'/><category scheme='http://www.blogger.com/atom/ns#' term='S4M'/><title type='text'></title><content type='html'>&lt;strong&gt;Performance issues in querying XML in SQL Server&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Introduction&lt;/em&gt;&lt;br /&gt;One of the challenges in the Canadian broadcasting industry is to prove to the CRTC that what a broadcaster aired on television is actually what they wanted to air, and if a discrepancy exists, have the ability to explain it. To do so, broadcast trafficking systems like S4M and SDS generate playlists and asrun logs, which must be reconciled per CRTC regulations. I was tasked with developing a simple application to reconcile these playlists and asrun logs with each other. Since the playlists were provided as XML files, I stored the playlists in a SQL Server XML column so when provided with an asrun log, I could always go back to the database and find the associated playlist easily with a SQL query. Playlists were linked to asrun log files via an event id, which was an attribute in the XML playlist file but a field on a flat text asrun log. This meant that every time I needed to verify the event record in the asrun log had a matching playlist event, I would have to parse out the event id from a flat text row, then query the XML column for an event XML node with an eventid attribute with a matching value.&lt;br /&gt;&lt;br /&gt;That was the business problem I was attempting to address. To translate it into SQL syntax, I had to execute the following query every time I wanted to reconcile an event record:&lt;br /&gt;&lt;br /&gt;SELECT Playlist.query('for $A in /dlprog/dldate/dlevent&lt;br /&gt;where $A/@eventid[.="79576616"]&lt;br /&gt;return&lt;br /&gt;$A '&lt;br /&gt;)&lt;br /&gt;FROM [Corporate_Applications].[dbo].[BIP_Playlists]&lt;br /&gt;&lt;br /&gt;(Many thanks to Yihong Xu, our trustworthy data architect, in providing this query).&lt;br /&gt;&lt;br /&gt;&lt;em&gt;The Performance Issue&lt;/em&gt;:&lt;br /&gt;If one examines the above SELECT statement query, it will become obvious that the records are not filtered with a WHERE clause. Instead, all the records are subjected to a XPath query in the SELECT clause. So when I innocently applied this query in my code, my reconciliation of the asrun log against the playlist took approximately 14 minutes.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;The Solution&lt;/em&gt;:&lt;br /&gt;Performance issues are normally rooted in architecture, so I revisited my application and in particular, my data architecture. Obviously, the first thing I focused on was reducing the time taken for the query. Since all the querying was done in the XPath expression in the SELECT statement, I had to pull that logic out and put it into a WHERE clause. However, the existing data architecture would not allow me.&lt;br /&gt;&lt;br /&gt;Therefore, the only choice I had was to create another table to hold the eventid information I would be querying against in its own column, as opposed to buried in the XML data in the XML column, as well as the related values I would need to SELECT from the dlevent node. I then populated this table as I read the playlist files, as at that point I was inserting the entire playlist file as a XML field into the SQL Server table anyway. Next, I adjusted the above SELECT statement to query against the eventid column of my new table, and the resulting performance improvement was staggering. The resulting reconciliations now took roughly 1 minute, meaning a performance improvement of about 14 times.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;Lesson learned&lt;/em&gt;&lt;br /&gt;XPath queries embedded in SELECT clauses are very inefficient. If there is an attribute in a piece of XML data you will constantly be querying, create a separate table that holds that attribute value as a column, along with foreign keys to link back to the original table with the XML column.&lt;br /&gt;&lt;br /&gt;Looking back on the problem, I would have done a deeper analysis of the data architecture to ensure that the table structures were optimized for SELECT queries, as opposed to just aiming for simplicity. I would also have used a different content management system to archive the playlists, such as Documentum, since it more naturally supports management and chunking of XML data, as well as indexing the XML data for fast retrieval.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1576883349115542732-7505395489114649119?l=derricksweng.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://derricksweng.blogspot.com/feeds/7505395489114649119/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1576883349115542732&amp;postID=7505395489114649119' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1576883349115542732/posts/default/7505395489114649119'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1576883349115542732/posts/default/7505395489114649119'/><link rel='alternate' type='text/html' href='http://derricksweng.blogspot.com/2008/12/performance-issues-in-querying-xml-in.html' title=''/><author><name>Derrick</name><uri>http://www.blogger.com/profile/14369897513473171282</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
