Art of Software, Craft of Hardware

“Software is a great combination between artistry and engineering. When you finally get done and get to appreciate what you have done it is like a part of yourself that you've put together. I think a lot of the people here feel that way.” -- Bill Gates

Friday, May 7, 2010

Special Folder Paths in dotNet

We have a lot of special folders in windows MyDocuments etc, which change from user to user. Now if I plan to store my applications configuration settings in any of these folders, or my application wants to access these folders to perform an operation, how do I get it?

.Net provides an easy way out. We have Environment.SpecialFolder which can be used for these purposes.


Read more!

Thursday, April 22, 2010

Configuring log4net in your project

I found this extremely useful post by Mitch on Log4Net, .Net Logging Tool. A must read post for someone trying to use log4net in their projects. Since this blog acts as my repository of my learnings. I am reproducing a significant amount of content from Mitch's post here. I would recommend you go there for details.

The main part I am reproducing here deals with configuring log4net configuration in a separate file from app.config or web.config.


Add the log4net assembly to your project

It’s good practice to include third party assemblies under source control as part of a project. That way, performing a get latest on a new PC includes everything.

If your project does not already contain a source-controlled external libs folder for third party assemblies:

  1. Right click on project
  2. Select “Add -> New Folder”, call it “ThirdPartyDlls” or “libs” or whatever your in-house standards specify.
OK, now add log4net:
  1. Right click on your external libs folder, select “Add Existing Item…” and browse to where you unzipped log4net and choose the release version of log4net.dll (\bin\net\2.0\release\log4net.dll)
  2. Right click on References, select “Add Reference …” and browse to your libs folder and pick the log4net.dll you just added.

Configure log4net: Creating and Specifying the Log4Net Config File
Although it is possible to add your log4net configuration settings to your project’s app.config or web.config file, it is preferable to place them in a separate configuration file. Aside from the obvious benefit of maintainability, it has the added benefit that log4net can place a FileSystemWatcher object on your config file to monitor when it changes and update its settings dynamically.
To use a separate config file, add a file named Log4Net.config to your project and add the following attribute to yourAssemblyInfo.cs file:
[assembly: log4net.Config.XmlConfigurator(ConfigFile = "Log4Net.config", Watch = true)]
Note: for web applications, this assumes Log4Net.config resides in the web root. Ensure the log4net.config file is marked as “Copy To Output” -> “Copy Always” in Properties.
Here is an sample log4net config file with several appenders defined:



xml version="1.0" encoding="utf-8" ?>
<log4net>
  <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
    <file value="..\\Logs\\CurrentLog" />
    <appendToFile value="true" />
    <rollingStyle value="Size" />
    <maxSizeRollBackups value="10" />
    <maximumFileSize value="10000" />
    <staticLogFileName value="true" />
    
    
    
    <filter type="log4net.Filter.LevelRangeFilter">
      <acceptOnMatch value="true" />
      <levelMin value="INFO" />
      <levelMax value="FATAL" />
    filter>
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%-5level %date [%thread] %-22.22c{1} - %m%n" />
    layout>
  appender>
  <appender name="LogFileAppender" type="log4net.Appender.FileAppender">
    <file value="log-file.txt" />
    
    
    <appendToFile value="true" />
    <layout type="log4net.Layout.PatternLayout">
      <header value="[Your Header text here]" />
      <footer value="[Your Footer text here]" />
      <conversionPattern value="%date [%thread] %-5level %logger [%ndc] 
                 <%property{auth}> - %message%newline" />
    layout>
    
  appender>
  <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
    layout>
  appender>
  <appender name="EventLogAppender" type="log4net.Appender.EventLogAppender" >
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%date [%thread] %-5level %logger
                         [%property{NDC}] - %message%newline" />
    layout>
  appender>
  
  <root>
    <level value="DEBUG" />
    <appender-ref ref="LogFileAppender" />
    <appender-ref ref="ConsoleAppender" />
    <appender-ref ref="RollingFileAppender" />
  root>
  
  <logger name="ConsoleApp.LoggingExample">
    <level value="ERROR" />
    <appender-ref ref="EventLogAppender" />
  logger>
log4net> 
In the “RollingFileAppender“ defined above, as Phil Haack points out: “Note that the file value (with backslashes escaped) points to ..\Logs\CurrentLog. [In Web Applications] this specifies that log4net will log to a file in a directory named Logs parallel to the webroot. You need to give the ASPNET user write permission to this directory, which is why it is generally a good idea to leave it out of the webroot. Not to mention the potential for an IIS misconfiguration that allows the average Joe to snoop through your logs.”
Using the Logger
At the start of each class declare a logger instance as follows:
public class ClassWithLoggingExample
{
    private static readonly ILog log = 
LogManager.GetLogger( System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
 
    ...
}

You will need to add a “using log4net;" statement to each class file. By defining a logger in each class you have the ability to control the logging level on a class by class basis, simply by using the config file.
Once you have instantiated a logger, you can call its logging methods. Each method is named for the logging level. For example:

// In ascending order of severity (descending level of verbosity) 
log.Debug("This is a DEBUG level message. The most VERBOSE level.");
log.Info("Extended information, with higher importance than the Debug call");
log.Warn("An unexpected but recoverable situation occurred");
log.Error("An unexpected error occurred, an exception was thrown, or is about to be thrown", ex);
log.Fatal("Meltdown!", ex);


Whether or not a message shows up in your logs depends on how you have configured your appenders and the logging level you have set. If you want to avoid the overhead of string construction and a call to a logging method, you can first test to see if the appropriate level is active:
// If you are concerned about performance, test the appropriate
// log level enabled property (one for each of the log level methods)
if (log.IsInfoEnabled) 
log.Info("Performance sensitive Info message");

The format of the logged output can be adjusted in the config file. See the log4net documentation that came in the zipped download (\doc\release\sdk\log4net.Layout.PatternLayout.html).
For web applications, add the following line to the Application_Error method in the Global.asax.cs file:
protected void Application_Error(object sender, EventArgs e)
{
log.Fatal("An uncaught exception occurred", this.Server.GetLastError());
}


Read more!

Wednesday, February 24, 2010

Inserting character data inside XSLT



]]>

OutPut:


Read more!

Friday, February 5, 2010

Microsoft Dynamics CRM 4.0 JavaScript SDK

Ascentium released a Javascript SDK for Microsoft Dyanmics CRM 4.0 in the year 2008. This was the best JS SDK for CRM 4.0 that I came across. It abstracts all the rote code that one has to write for preparing the soap request, sending the request, handling the response in both synchronous and asynchronous ways. You can find the complete details of what this library in this blog post. You can find the download the code from Ascentium here.


Though the SDK from Ascentium covers all the functionality it has a few shortcomings:
It works only for CRM On Premise installation.
It does not provide a way to login as a different user.

Though it looks like a small issue, the above two limits the usage of this SDK in a lot of scenarios.

While working one of the projects at Proteans, I came across this library. Going through the code, I realized I just need to change few functions to get it working for all kinds of deployment and also provide an ability to login as different users.

In summary, I modified three functions and added one new function. I am posting the changes here. I will also release the sdk with complete changes at a later date. Thanks to Proteans for letting me work on this.

CRMService: This function now takes additional optional parameters in User Name, Passsword, AuthType. (These parameters are optional only for on premise deployment).
function Ascentium_CrmService(sOrg, sServer, sUserName, sPassword, iAuthType) {
/// CrmService object constructor. allows for calling crm webservice CRUD operations directly from client script.
/// CRM Organization Name. Not required if used on a CRM form.
/// (optional) URL of CRM Server. Url of current window is used if this is null.
/// (optional) UserName of CRM User. User Name is optional if OnPremise installation.
/// (optional) Password of CRM User. Password is optional if OnPremise installation.
/// (optional) Authentication Type to the CRM Server. Defaulted to OnPremise(AuthType = 0) installation this is null.

this.org = sOrg;
this.server = sServer;
this.username = sUserName;
this.password = sPassword;
this.authtype = iAuthType;

if (sOrg == null) {
if (typeof (ORG_UNIQUE_NAME) != "undefined") {
this.org = ORG_UNIQUE_NAME;
}
else {
//Error Handler
alert("Error: Org Name must be defined.");
}
}

// URL was not provided, assume the JS file is running from the CRM Server
if (sServer == null) {
this.server = window.location.protocol + "//" + window.location.host;
}

// Defaulting to OnPremise installation if AuthType is not present
if (iAuthType) {
this.authtype = iAuthType;
} else {
this.authtype = 0;
}

// If either UserName or Password is not present, default to OnPremise CRM installation.
if (sUserName == null || sPassword == null) {
this.username = null;
this.password = null;
this.authtype = 0;
}
}

ExecuteRequest: This function now is modified to take care of both On Premise and IFD deployments.
Ascentium_CrmService.prototype._ExecuteRequest = function(sXml, sMessage, fInternalCallback, fUserCallback, sUrl) {
// Create the XMLHTTP object for the Update method.
var oXmlHttp = this.CreateXmlHttp();

try {
//We need to handle the cases when username and password are present for each of the deployment scenarios
if (this.authtype == 0) {
if (this.username) {
oXmlHttp.open("POST", this.server + "/mscrmservices/2007/crmservice.asmx", (fUserCallback != null), this.username, this.password);
}
else {
oXmlHttp.open("POST", this.server + "/mscrmservices/2007/crmservice.asmx", (fUserCallback != null));
}
}
else {
// We also need to execute the request to get the CRM Ticket as part of header. In this scenario we use sURL to pass the discovery service URL
if (sUrl) {
oXmlHttp.open("POST", sUrl, (fUserCallback != null), this.username, this.password);
} else {
oXmlHttp.open("POST", this.server + "/mscrmservices/2007/crmservice.asmx", (fUserCallback != null), this.username, this.password);
}
}
oXmlHttp.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
oXmlHttp.setRequestHeader("Content-Length", sXml.length);

// In Case of AuthType 2, when getting the CRM Ticket sMessage is ""
if (sMessage) {
oXmlHttp.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/crm/2007/WebServices/" + sMessage);
}

if (fUserCallback != null) {
//asynchronous: register callback function, then send the request.
var crmServiceObject = this;
oXmlHttp.onreadystatechange = function() { fInternalCallback.call(crmServiceObject, oXmlHttp, fUserCallback) };
oXmlHttp.send(sXml);
}
else {
//synchronous: send request, then call the callback function directly
oXmlHttp.send(sXml);
return fInternalCallback.call(this, oXmlHttp, null);
}
}
catch (er) {
}
}

GetHeader: Modified to work for both On Premise and IFD deployments. In case we are going to extend this for Microsoft Online CRM deployment and integrate with Passport, this is the function to change.
Ascentium_CrmService.prototype._GetHeader = function() {

var sEnv = ''
sEnv += '';

var sxml1 = '';
sxml1 += '' + this.authtype + '';

var sxml2 = ('' + this._crmXmlEncode(this.org) + '');
sxml2 += '00000000-0000-0000-0000-000000000000';
sxml2 += '';

// Use CRM's GenerateAuthenticationHeader function if it is available.
if (typeof (GenerateAuthenticationHeader) != "undefined") {
return sEnv + GenerateAuthenticationHeader();
}
else {
// For an on Premise Deployment
if (this.authtype === 0) {
return sEnv + sxml1 + sxml2;
}
else {
// For an IFD Deployment
var sxml = sEnv + '';
sxml += ('' + this._crmXmlEncode(this.org) + '');
sxml += ('' + this._crmXmlEncode(this.username) + '');
sxml += ('' + this._crmXmlEncode(this.password) + '');
sxml += '';
var url = this.server + "/MSCRMServices/2007/SPLA/CrmDiscoveryService.asmx";
//Make a call to crm discvoery service to get CRM Ticket
var crmTicket = ('' + this._ExecuteRequest(sxml, "", this._GetHeaderCallBack, null, url) + '');
return sEnv + sxml1 + crmTicket + sxml2;
}
}
}
GeHeaderCallBack: This function is added to handle the response in case of IFD deployment and return the CRM ticket.
Ascentium_CrmService.prototype._GetHeaderCallBack = function(oXmlHttp, callback) {
///(private) Get Header Call Back method.Returns the CRM Ticket in case of IFD deployment

//oXmlHttp must be completed
if (oXmlHttp.readyState != XMLHTTPREADY) {
return;
}

//check for server errors
if (this._HandleErrors(oXmlHttp)) {
return;
}

//Get the CRM Ticket from the returned xml
var oResult = oXmlHttp.responseXML.selectSingleNode("//CrmTicket");

//return CRM Ticket if sync, or call user callback func if async
if (callback != null) {
callback(oResult.text);
}
else {
return oResult.text;
}
}


I will update you as and when I get to publish the complete script. Until then you can download the code from Ascentium and make the above modifications.


Read more!

Wednesday, February 3, 2010

SQL SERVER - Fix Error 945

SQL SERVER – FIX : Error 945 Database cannot be opened due to inaccessible files or insufficient memory or disk space. See the SQL Server error log for details


I faced this issue twice in the last couple of weeks. If any of you face this, try the following first:

select databaseproperty('','isShutdown')

alter database set offline

alter database set online

If the above does not work. Please go ahead with your Googling.


Read more!

Wednesday, May 20, 2009

Inserting Elements into a Page

The following code snippet is to help insert a code snippet into a Page without using document.write.

function insert()
{
var insertElement = getNewElement('div','ElementID', 'ElementName');
var targetElement = document.getElementById('targetId');
insertAfter(insertElement , targetElement);
}

/* insert a new element after the targetelement*/
function insertAfter(newElement, targetElement) {
var parent = targetElement.parentNode;
if (parent.lastChild == targetElement) {
parent.appendChild(newElement);
}
else {
parent.insertBefore(newElement, targetElement.nextSibling);
}
}
/* Get new Iframe element of 1/1 pixel size*/
function getNewElement(elementTag,elementID,elementName) {
var newElement = document.createElement(elementTag);
newElement.id = elementID;
newElement.name = elementName;
newElement.width = '1';
newElement.height = '1';
newElement.frameborder = '1';
newElement.marginwidth = '1';
newElement.marginheight = '1';
newElement.vspace = '1';
newElement.hspace = '1';
newElement.alltransparency = 'true';

return newElement;
}

As a side we can also get the insert scripts by figuring out the script source:

function getLastScriptElement(url) {
var scripts = document.getElementsByTagName('script');
var matchUrl = 'js/sample.js'.toLowerCase();
for (var i = 0; i < scripts.length; i++) {
var srcUrl = scripts[i].src.toLowerCase();
var match = srcUrl.match(matchUrl);
//if (scripts[i].src.toLowerCase() == url.toLowerCase() + 'js/sample.js'.toLowerCase())
if (null != match) {
break;
}
}
if (undefined == scripts[i + 1]) {
return scripts[i];
} else {
return scripts[i +1];
}
}


Read more!

Sunday, January 28, 2007

Time Trex

We will assume that Apache, PHP, MySQL are installed by now. Once this is done, extract Timetrex to "C:\Apache2.0\htdocs" folder. If you are using Xampp it will be in "**\xampp\htdocs".

In your web browser goto http://localhost/phpMyAdmin. if you installed the components one by one, or goto http://localhost, and then click on phpMyAdmin if you used xampp.

Now create a database 'timetrex'. Then create a user 'timetrex' with complete privileges to the user for the database 'timetrex'.

In the extracted TimeTrax folder rename the file "timetrex.ini.php-example_windows" to "timetrex.ini.php". Now Open this file in a text editor. Search for [database], make sure type = mysqlt is uncommented (';' before a line means its a comment). Enter the database information. Search for "installer_enabled" and set its value to true.

Check for log, storage, dir paths and ensure that those paths exist. Or set these paths according to your preference.

Save the file. Now open the following page in your browser http://localhost/TimeTrex/interface/install.php. Follow the installation procedure.


Read more!