December 26, 2018

WCF - Increase message size quota for incoming messages.

You may encounter this error message while receving larger resultset from WCF service:

The maximum message size quota for incoming messages (65536) has been exceeded. To increase the quota, use the MaxReceivedMessageSize property on the appropriate binding element.

By default the client application supports the message size 65536. To fix this error you have to increase this default limit.

In App.config or Web.config file of the client application, change/add attributes of the binding tag as below:

 <bindings>
  <basicHttpBinding>
   <binding name="basicHttp" allowCookies="true"
      maxReceivedMessageSize="20000000" 
      maxBufferSize="20000000"
      maxBufferPoolSize="20000000">
    <readerQuotas maxDepth="32" 
      maxArrayLength="200000000"
      maxStringContentLength="200000000"/>
   </binding>
  </basicHttpBinding>
 </bindings>

Here I increased the default value of maxReceivedMessageSize to 20000000.

December 25, 2018

WCF - How to turn on IncludeExceptionDetailInFaults

You may encounter this error message while working with WCF services:

The server was unable to process the request due to an internal error.  For more information about the error, either turn on IncludeExceptionDetailInFaults
 (either from ServiceBehaviorAttribute or from the  configuration behavior) on the server in order to send the exception information back to the client, 
or turn on tracing as per the Microsoft .NET Framework SDK documentation and inspect the server trace logs.

This error message is not showing the real error or exception cause on server side because of security reasons, but instructing to change the configuration in order to receive real exception message, which may be some internal server error.

To fix this error. We have two ways to set this flag's value, either by web.config file or from C# Code.

  1. Open the web.config file of the service. Define a behavior in your web.config file (it may be already defined by default, but with false value. e.g. includeExceptionDetailInFaults="false"). We need to set includeExceptionDetailInFaults="true" in order to include exception details:

    <configuration>
      <system.serviceModel>
        <behaviors>
          <serviceBehaviors>
            <behavior name="debug">
              <serviceDebug includeExceptionDetailInFaults="true" />
            </behavior>
          </serviceBehaviors>
        </behaviors>
        ...
      </system.serviceModel>
    </configuration>
    

    Then apply or attach the behavior to your service:

    <configuration>
      <system.serviceModel>
        ...
        <services>
          <service name="MyServiceName" behaviorConfiguration="debug" />
        </services>
      </system.serviceModel>
    </configuration>
    
  2. You can also set the value of includeExceptionDetailInFaults from C# Code, by using ServiceBehavior attribute on the service's class declaration.

    [ServiceBehavior(IncludeExceptionDetailInFaults = true)]
    public class MyServiceClass:IMyService
    {
     //my service methods...
    }
    

December 20, 2018

The OLE DB provider "OLEDB.12.0" for linked server "(null)"

I was trying to retrieve data from excel in T-SQL using the following statement:

SELECT * FROM OPENROWSET('Microsoft.ACE.OLEDB.12.0', 
    'Excel 12.0;Database=C:\MyPath\MyFile.xlsx;;HDR=YES;IMEX=1', [Sheet1$])

But was receiving the error message.

 Cannot initialize the data source object of OLE DB provider "Microsoft.ACE.OLEDB.12.0" for linked server "(null)".

While searching solution for this error, I have found multiple reasons that leads to differents solutions to fix this issue. Here are the options I have found during my search.

  • Solution 1:

    • Check if the file name is correct/exists?
    • Check if the path/directory name is correct/exists?
  • Solution 2:

    Check if the target file is open, then close it and run your query again.

  • Solution 3:

    Check if you have Microsoft Access Database Engine 2010 installed? If not, you can download it from Microsoft . A system restart may also be helpful.

  • Solution 4:

    Run SSMS as admin.

  • Solution 5:

    Check if the user has rights on SQL Server Service. If not you have to set the proper account/user for this service. Follow these steps:

    • Open the services console by typing Services.msc in the run command.
    • Find the service with name SQL Server(MyInstanceName) (MyInstanceName is the name of your instance), right click on it and click Properties.

      SQL Server Service - Properties
    • In the Properties dialog box, move to Log On tab, against This account: radio option, click the Browse button, a Select User dialog box will appear.

      SQL Server Service - Log On tab
    • In the Select User dialog box, enter the proper user name or you can find from the list by click on Advance button and search for the desired user. Once user is selected, click OK.

      SQL Server Service - Select User tab
    • Then back on the Log On tab, enter the passwords, press Apply, then press OK.

    • Restart the service to make the changes take effect.

      SQL Server Service - Restart

  • Solution 6:

    You have to enable Ad hoc distributed queries:

        Exec sp_configure 'show advanced options', 1;
        RECONFIGURE;
        GO
    
        Exec sp_configure 'Ad Hoc Distributed Queries', 1;
        RECONFIGURE;
        GO
       

  • Solution 7:

    Check the In Process and Dynamic Parameters options for the ACE provider, use following T-SQL command.

        EXEC master.dbo.sp_MSset_oledb_prop N'Microsoft.ACE.OLEDB.12.0'     
         , N'AllowInProcess', 1 
        GO
    
        EXEC master.dbo.sp_MSset_oledb_prop N'Microsoft.ACE.OLEDB.12.0'
         , N'DynamicParameters', 1
        GO
       

  • Solution 8:

    • If you are using select statement with specific columns, make sure the column names are correct. For example in this query:

          SELECT [Col A], [Col B] FROM OPENROWSET('Microsoft.ACE.OLEDB.12.0', 
              'Excel 12.0;Database=C:\MyPath\MyFile.xlsx;;HDR=YES;IMEX=1', [Sheet1$])
        

      Check if [Col A] and [Col B] actually exist in your spreadsheet?

    • Sheet name might be wrong. Do Sheet1(in excel) actually exist in your spreadsheet? Have you appended $ sign with sheet name in your query like [Sheet1$].

  • Solution 9:

    Check if your user have the write access on the Temp folder, e.g.
       C:\Users\MyUser\AppData\Local\Temp (MyUser is your user name)
      
  • Solution 10:

    Check the permissions on the Temp folder located in ServiceProfiles. You have to pick the one based on whether you use a local system account or network domain account.

    For local system account, you have to select:

        C:\Windows\ServiceProfiles\LocalService\AppData\Local\Temp
       

    For network domain accounts, folder is:

        C:\Windows\ServiceProfiles\NetworkService\AppData\Local\Temp
       

    Right click on this folder and give it read write access to the account executing the code.

I tried some of the above solutions to get rid of this error.

If any one has some other solution that helped him solve the issue please share in the comments section below.

References:

December 6, 2018

Directory Junction vs SYMLINK (Symbolic Link)

In the last post Create a Symbolic Link in Windows, we have seen how to create different Symbolic Links, i.e, Hard Link, Directory Symbolic Link and Directory Junction Link. If you have not read the last post I recommend to have a look, it will help you understand better.

In this post we will see how Symbolic Link and Directory Junction fits in different scenarios. At some extent Symbolic Link and Directory Junctions look similar, but they serve different purpose. Here is a list of some of the differences between a junction and a symbolic link.

  • A junction point is designed to target local directories, but a symbolic link can be used for directories and files.
  • A junction can only be linked to a local volume path. Symbolic links can be used to link both local and remote/network path. For example, a symbolic link can link to the network share e.g. \\officenetwork\shared\some-folder.

In windows explorer, the icons displayed for both Symbolic Link and Directory Junction seems identical and you can't differentiate if the created link is a Symbolic Link or a Directory Junction by seeing it. But from the command prompt, you can easily identify the correct type by using DIR command. If you have seen my last post (mentioned above), you may notice that the Directory Junction item will be displayed with <JUNCTION> type in console.

If you try to create a Directory Junction Link to some remote path, it will give you an error message. You have to use Directory Symbolic Link when you need to create a link for remote path. Lets understand this with an example:

Create a Directory Junction Link

In this example, I will create a Directory Symbolic Link to Target remote folder named TestFolder. The directory linked will be created at path C:\SymbolicLink\Network\J_Link.

  • Open the command prompt and go to the path C:\SymbolicLink\Network
  • Run the following command:

       mklink /J "C:\SymbolicLink\Network\J_Link" "\\officenetwork\IT-Developmnet\Idrees\TestFolder"
      

    You will see the error message as follows:

       Local volumes are required to complete the operation.
      
    Directory Junction Link for network path giving error

Create a Directory Symbolic Link

In this example, I will create a Directory Symbolic Link to Target remote folder named TestFolder. The directory linked will be created at path C:\SymbolicLink\Network\J_Link.

  • Open the command prompt and go to the path C:\SymbolicLink\Network
  • Run the following command:

       mklink /D "C:\SymbolicLink\Network\D_Link" "\\officenetwork\IT-Developmnet\Idrees\TestFolder"
      

    You will see the success message as follows:

       symbolic link created for C:\SymbolicLink\Network\D_Link <<===>> \\officenetwork\IT-Developmnet\Idrees\TestFolder
      
    Directory Symbolic Link for network path

    If you run the DIR command at C:\SymbolicLink\Network, you will see that the newly created directory link will be displayed as <SYMLINKD>.

    Directory Symbolic Link for network path - in command prompt

I hope you find this post helpful. I welcome your suggestions or feedback in the comments section below.

December 3, 2018

Create a Symbolic Link in Windows

Symbolic Link, Soft Link or SymLink referred to the same thing, is a file that is linked to another file or directory, which can be on the same computer or any other on the network. You can create Symbolic Link by using mklink command, which is available since Windows Vista.

Syntax of mklink is:

MKLINK [[/D] | [/H] | [/J]] Link_File_Or_Directory_Path Target_File_Or_Directory_Path

As you can see from the above syntax, mklink allows following 3 switches to create different types of Symbolic Links.

  • /D Directory symbolic link.
  • /H Creates a hard link instead of a symbolic link.
  • /J Directory junction.

The default is a file symbolic link, i.e. if you did not specify and switch than the link created will be File Symbolic Link.

Lets start create each type of link with an example:

First we setup he environment for examples to follow. I created a folder named Symbolic Links at C:/. Inside Symbolic Links folder created another folder named Original, which will be act as Target Folder of symbolic links and contains two files file1.txt and file2.txt.

Create a Hard Link

In this example, I will create a Hard Link to file1.txt which we created inside our Target Folder named Original. The linked file will be created at path C:\SymbolicLink\H_Link.

  • Open the command prompt and go to the path C:\SymbolicLink
  • Create new directory H_Link with the following command

       mkdir H_Link
      
  • Run the following command:

       mklink /H "C:\SymbolicLink\H_Link\file1.txt" "C:\SymbolicLink\Original\file1.txt"
      

    If you get the following error message:

       You do not have sufficient privilege to perform this operation.
      

    Then just restart the command prompt with Administrator Privileges. If you are already running command prompt with Administrator Privileges then you will see the success message as follows:

       Hardlink created for C:\SymbolicLink\H_Link\file1.txt <<===>> C:\SymbolicLink\Original\file1.txt
      

    We have successfully create the Symbolic Link to a file (also know as Hard Link).

Create a Directory Symbolic Link

In this exmaple, I will create a Directory Symbolic Link to Target Folder named Original. The directory linked will be created at path C:\SymbolicLink\D_Link.

  • Open the command prompt and go to the path C:\SymbolicLink
  • Run the following command:

       mklink /D "C:\SymbolicLink\D_Link" "C:\SymbolicLink\Original"
      

    If you get the following error message:

       You do not have sufficient privilege to perform this operation.
      

    Then just restart the command prompt with Administrator Privileges. If you are already running command prompt with Administrator Privileges then you will see the success message as follows:

       symbolic link created for C:\SymbolicLink\D_Link <<===>> C:\SymbolicLink\Original
      

    If you run the DIR command at C:\SymbolicLink, you will see that the newly created directory link will be displayed as .

    Create symbolic links

    In windows explorer, the linked directory will be shown with icon similar to the shortcut icon, like this:

    Create symbolic links

Create a Directory junction

In this exmaple, I will create a Directory Symbolic Link to Target Folder named Original. The directory linked will be created at path C:\SymbolicLink\J_Link.

  • Open the command prompt and go to the path C:\SymbolicLink
  • Run the following command:

       mklink /J "C:\SymbolicLink\J_Link" "C:\SymbolicLink\Original"
      

    You will see the success message as follows:

       Junction created for C:\SymbolicLink\J_Link <<===>> C:\SymbolicLink\Original
      

    If you run the DIR command at C:\SymbolicLink, you will see that the newly created directory link will be displayed as .

    Create symbolic links

    In windows explorer, the linked directory will be shown with the icon similar to Directory Symbolic Link:

    Create symbolic links

It seems like Directory Symbolic Link and Directory junction works in the same way. But there is a difference. I will explain the difference in next post soon.

November 12, 2018

IIS 7.0 or greater is required to install IIS SEO Toolkit 1.0

I downloaded the Search Engine Optimization Toolkit 64 bit installer from www.iis.net, and while trying to install at my PC with Windows 10, it was showing me this error:

 IIS Version 7.0 or greater is required to install IIS Search Engine Optimization Toolkit 1.0

After searcing, I found the following solution worked for me:

  • Find the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\InetStp from RegEdit.
  • Right click on MajorVersion name and then click Modify...

    Edit regedit SEO
  • In decimal format, it is showing value 10. Change this value to 9 and click OK.

    Edit regedit SEO
  • Now try to install Search Engine Optimization Toolkit, it should be installed successfully.
  • After installing SEO Toolkit, revert the value of MajorVersion back to 10 and click OK.

November 8, 2018

Find File-Type by Magic Number of File

In this post, we will find the file type for a file using Magic Number.

What is a Magic Number?

From Wikipedia Magic Number

Magic Number is a constant numerical or text value used to identify a file format or protocol.

Magic number is a hex number occupying a few bytes at the beginning of the file and indicates the type of content, but is not visible to users.

Some users may be thinking why not simply check the file extension to find the file type. Yes, We made the same mistake!

In common scenarios this would be enough to check by file extensions, but we faced this scenario where we can not trust on file extension. The problem we faced during our website's Penetration Testing. There is a form which let user to upload files and it is required that user can only upload PDF files. We added a check based on file extension and made it vulnerable.

During the Penetration Test, our QA team has found this vulnerability, hackers can upload even exe files by renaming the target file to PDF, like some-dangerous-file.exe.pdf. If you are checking by file extension then this file will get successfully uploaded on server.

In order to correctly find the content type of a file, we have to check Magic Number of target file. Since Magic Number can vary in length for different file types, for example, 5 bytes (4D-5A) for exe file and 14 digits (25-50-44-46-2d) for pdf file. In this example, I am reading first 20 bytes to find magic number, you may need to read more bytes for magic number if the file format you are targetting has the magic number with length greater than 20.

Lets move to the code segment, in this sample I have written two functions. IsMagicNumberMatched() is the method doing the real work to check magic number for the file-path passed as parameter. GetAuditorOpinionFromFile() is the wrapper method to test magic numbers for different files. I am writing here 4 common file types exe, pdf, xml and rar. In the end, this method will return status message as string, to display the Magic Number status if it is matched with the parameter we passed.

 public static string GetAuditorOpinionFromFile()
 {
  string filePath_EXE = @"C:\SOME_PATH_TO\MyFile.exe";
  string filePath_PDF = @"C:\SOME_PATH_TO\MyFile.pdf";
  string filePath_XML = @"C:\SOME_PATH_TO\MyFile.xml";
  string filePath_RAR = @"C:\SOME_PATH_TO\MyFile.rar";

  Dictionary numberList = new Dictionary();
  numberList.Add("exe", "4D-5A");
  numberList.Add("pdf", "25-50-44-46-2d");
  numberList.Add("xml", "3c-3f-78-6d-6c-20");
  numberList.Add("rar", "52-61-72-21-1A-07-00");

  StringBuilder sb = new StringBuilder();            
  sb.AppendFormat("File Path: {0}, File Magic No: {1}, IsMatched: {2}", filePath_EXE, numberList["exe"], IsMagicNumberMatched(filePath_EXE, numberList["exe"])).AppendLine();
  sb.AppendFormat("File Path: {0}, File Magic No: {1}, IsMatched: {2}", filePath_PDF, numberList["pdf"], IsMagicNumberMatched(filePath_PDF, numberList["pdf"])).AppendLine();
  sb.AppendFormat("File Path: {0}, File Magic No: {1}, IsMatched: {2}", filePath_XML, numberList["xml"], IsMagicNumberMatched(filePath_XML, numberList["xml"])).AppendLine();
  sb.AppendFormat("File Path: {0}, File Magic No: {1}, IsMatched: {2}", filePath_RAR, numberList["rar"], IsMagicNumberMatched(filePath_RAR, numberList["rar"])).AppendLine();

  return sb.ToString();
 }

 private static bool IsMagicNumberMatched(string filePath, string candidateMagicNo)
 {
  BinaryReader reader = new BinaryReader(new FileStream(Convert.ToString(filePath), FileMode.Open, FileAccess.Read, FileShare.None));

  ////set start position = 0, and read first 20 bytes. for some other with magic number length greater than 20, you may need to read more bytes.
  reader.BaseStream.Position = 0x0;
  byte[] data = reader.ReadBytes(20);

  //close the reader
  reader.Close();

  //convert bytes data to string in hex format
  string string_data_as_hex = BitConverter.ToString(data);

  // substring to select first (n) characters from hexadecimal array
  string currentMagicNo = string_data_as_hex.Substring(0, candidateMagicNo.Length);
  
  return currentMagicNo.ToLower() == candidateMagicNo.ToLower();
 }

I hope you find this post helpful, I welcome your comments or suggestions to help improve this post.

Resources:

November 6, 2018

Unhandled exception of type ‘StackOverflowException’

While working on website project using Visual Studio 2015, I encountered this strange error message:

 An unhandled exception of type ‘System.StackOverflowException’ 
 occurred in System.Runtime.Serialization.dll
StackOverFlow exception

You may notice that if you click on View Detail... link of the exception message, there is no more information is available like stack trace etc.

StackOverFlow exception

I know there is no any complex logic defined in my code-base that could fall in infinite loop and cause StackOverflowException. After searching, I found the real cause of this error, and is not related to my code-base but a feature by Visual Studio know as Browser Link.

From MSDN blog:

Browser Link is just a channel between your Visual Studio IDE and any open browser. This will allow dynamic data exchange between your web application and Visual Studio.

Visual Studio uses this channel to exchange data between web application and Visual Studio, since it will serialze data before exchange, that was leading to StackOverflowException. The solution is just disable this feature.

There are two ways to disable this feature:

  1. From Visual Studio, click on the small down arrow near Refresh Linked Browsers button, from the drop-down options listed, just un-check Enable browser link.

    Enable browser link
  2. Add the following key in web.config appSettings tag.

     <add key=”vs:EnableBrowserLink” value=”false” />
    

Resources:

October 11, 2018

ASP.NET Health Monitoring with Custom Events

In the last post we have created a custom provider to send log data to WCF service client using ASP.NET Health Monitoring feature. We have seen that there are multiple events which we can map to the provider in order to log information about that events. Just to recap here is the list of default events available by ASP.Net Health Monitoring feature.

  • All Events
  • Heartbeats
  • Application Lifetime Events
  • Request Processing Events
  • Infrastructure Errors
  • Request Processing Errors
  • All Audits
  • Failure Audits
  • Success Audits

These events could provide plenty of useful information which can help us to analyze application state if there comes any problem while running in production environment. But there may be the case where you might want to log your own custom event. For example, I want to log a hit event for specific page using this feature rather than use some other logging library or write my own, which further may require extra configuration steps or wrapper classes. Writing your own custom event helps you to log event information with ASP.Net Health Monitoring feature and saves you from that extra effort.

Lets start writing the custom event.

First we have to inherit base class WebRequestEvent found in the namespace System.Web.Management. While calling the base constructor we have to provide eventCode. Note that for custom events we have available event codes starting from 100000. You may find full list of event codes defined as constant fields in WebEventCodes sealed class. The Last EventCode constant defined is:

public const int WebExtendedBase = 100000;

For custom events we have to use codes starting from this number. In this exmaple I am using constant event code variable by adding to 10 to the WebExtendedBase event code value, as:

private const int EVENT_CODE = System.Web.Management.WebEventCodes.WebExtendedBase + 10;

10 is just an arbitrary value, you can pick any number.

If you want to add extra information then you have to write an override of Raise() function, which is not necessary, but in this example I am writing this override to add my custom message, in a class level private variable I am using this variable to add information about current UserId from session variable if present:

private string defaultLocalMessage = "";

Which I want to log alongwith other fields. But this variable could not be directly accessible from WebBaseEvent object's properties which we see in last example, in ProcessEvent() method of custom provider. If we can not directly access additional variables then what is the benefit of using this variable? Well, There is a workaround!

Any additional information holding by custom variables, can be used in another overriden function FormatCustomEventDetails. Although this function returns void, it will not directly return any value but this function is interally called when the provider invokes one of the ToString() methods. So, finally when you call ToString() function for WebBaseEvent object in the function ProcessEvent(), you will get that additional information that you holded in local variables and added to the WebEventFormatter object in another overriden method FormatCustomEventDetails(). When you see the code you will get it more clear.

Here is the complete code listing for custom event class PageHitRequestEvent.

public class PageHitRequestEvent : System.Web.Management.WebRequestEvent
{
    private string defaultLocalMessage = "";
    private const int EVENT_CODE = System.Web.Management.WebEventCodes.WebExtendedBase + 10;

    public PageHitRequestEvent(string eventMessage, object eventSource)
        :
        base(eventMessage, eventSource, EVENT_CODE)
    {
        
    }

    // Raises the PageHitRequestEvent.
    public override void Raise()
    {
        // prepare custom message.
        defaultLocalMessage = "";
        if(HttpContext.Current == null)
        {
            defaultLocalMessage = ", LocalMessage: {Request Context is null";
        }
        else
        {
            defaultLocalMessage += "SessionID: " + HttpContext.Current.Session.SessionID;

            string userId = HttpContext.Current.Session["UserId"] as string;
            if (string.IsNullOrEmpty(userId))
            {
                defaultLocalMessage += ", UserId: (Anonymous)";
            }
            else
            {
                defaultLocalMessage += ", UserId: " + HttpContext.Current.Session["UserId"];
            }
            defaultLocalMessage += "}";
        }
        
        // raise the event. 
        base.Raise();
    }

    public override void FormatCustomEventDetails(WebEventFormatter formatter)
    {
        base.FormatCustomEventDetails(formatter);

        // Add custom data.
        formatter.AppendLine("");

        formatter.IndentationLevel += 1;

        formatter.TabSize = 4;

        formatter.AppendLine("* PageHitRequestEvent Start *");

        // Display custom event information.
        formatter.AppendLine(defaultLocalMessage);
              
        formatter.AppendLine("* PageHitRequestEvent End *");

        formatter.IndentationLevel -= 1;
    }   
    
}

The second step is to raise this event from the source we want to log information about. Lets say we have some critical page in WebForms application or Contoller/Action in MVC Application, and we want to log data every time user hit that URL. Only we have to create the object of custom event class and simply call the Raise() method, which will just trigger the event and any related provider will handle this event as usual.

PageHitRequestEvent myEventObject = new PageHitRequestEvent("some logging message", this);
// raise the event.
myEventObject.Raise();

If you are using the custom provider from my previous post, where I logged detailed information from ToString() method, you will also get the information which you have written to the WebEventFormatter object in FormatCustomEventDetails method.

Finally comes the configuration part. Although in custom provider example, we have mapped eventName="All Events" in rules section, which will allow the corresponding provider to handle all events, so also our custom event we have created in this example.

But also if you want you can separately add custom event and its mapping with the desired provider.

First add the following line in eventMappings tag to define custom event name.

 <add name="My Event" type="PageHitRequestEvent" />

Second add following line in rules tag to map this event to the desired provider.

 <add name="My Event Rule" eventName="My Event" provider="FailedAuthenticationProvider2"
            minInstances="1" maxLimit="Infinite" minInterval="00:00:00" custom="" />
 

Now the given provider should be able to handle the custom event when triggered.

Resources:

October 4, 2018

ASP.NET Health Monitoring using Custom Provider

ASP.NET Health Monitoring is a useful tool for logging error information that could ease diagnosing problems in a deployed application.

I have found this great article at Microsoft for configuring health monitoring feature in web projects.
Logging Error Details with ASP.NET Health Monitoring (C#)

Although builtin providers are more than enough to provide a rich information about the state of application. But there could be a chance you need to create custom provider to log additional information or additional storage option. Like in my case we need to log data in separate database which our application do not have direct access, but we have to call WCF service which would store data in target database. For this requirement I have written my custom provider.

Before proceeding to write code for custom provider, I recommend you to review the above mentioned article for health monitoring which will give you a strong background information.

Lets start writing custom provider class.

public class WCFCustomWebEventProvider : WebEventProvider
{
 //WCF service's client object.
    MiddleWare.ServiceClient _client = new MiddleWare.ServiceClient();
 
    public override void Initialize(String name, System.Collections.Specialized.NameValueCollection config)
    {
        base.Initialize(name, config);
    }

    public override void Flush()
    {

    }

    public override void ProcessEvent(WebBaseEvent raisedEvent)
    {
  //prepare StringBuilder with required information from WebBaseEvent object
        StringBuilder sb = new StringBuilder();
        sb.Append("EventId: " + raisedEvent.EventID + ", ");
        sb.Append("EventTime: " + raisedEvent.EventTime + ", ");
        sb.Append("EventTimeUtc: " + raisedEvent.EventTimeUtc + ", ");
        sb.Append("EventType: " + raisedEvent.GetType().Name + ", ");
        sb.Append("EventSource: " + raisedEvent.EventSource + ", ");
        sb.Append("EventSequence: " + raisedEvent.EventSequence + ", ");
        sb.Append("EventOccurrence: " + raisedEvent.EventOccurrence + ", ");
        sb.Append("EventCode: " + raisedEvent.EventCode + ", ");
        sb.Append("EventDetailCode: " + raisedEvent.EventDetailCode + ", ");
        sb.Append("Message: " + raisedEvent.Message + ", ");
        sb.Append("ApplicationPath: " + WebBaseEvent.ApplicationInformation.ApplicationPath + ", ");
        sb.Append("ApplicationVirtualPath: " + WebBaseEvent.ApplicationInformation.ApplicationVirtualPath + ", ");
        sb.Append("MachineName: " + WebBaseEvent.ApplicationInformation.MachineName + ", ");
        sb.Append("RequestUrl: " + HttpContext.Current.Request.Url.AbsoluteUri + ", ");
        sb.Append("Details: " + raisedEvent.ToString());

  //pass the string to WCF client object.
        _client.CreateLog(sb.ToString());
    }

    public override void Shutdown()
    {

    }
}

I have tried to keep it simpler to meet basic requirements, and avoid additional changes which you may need for performance requirements like make use of buffering.

Here, in the ProcessEvent override method, I prepared a string variable build up the required information from argument WebBaseEvent raisedEvent. Then the final string message containing all required logging details is then passed to the WCF client. Putting all the logging information in a single string variable is obviously not a good approach, I did here only to keep things simple and try to focus on the logic to add custom provider. A better approach could be to split data of our interest in separate variables and update the WCF method to accept multiple parameters.

Here we are done with custom provider's C# code.

I assumed that you have read the above mentioned article, so now I will move to the web.config part.

Add the following healthMonitoring tag inside system.web.

<healthMonitoring enabled="true">
   <providers>
  <add name="WCFCustomWebEventProvider_1" type="WCFCustomWebEventProvider" />
   </providers>
   <rules>
   <add name="All Events Rule" eventName="All Events" provider="WCFCustomWebEventProvider_1"
    minInstances="1" maxLimit="Infinite" minInterval="00:00:00" custom="" />
   </rules>
   <eventMappings>
  <add name="All Events" type="System.Web.Management.WebBaseEvent,System.Web,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a"
   startEventCode="0" endEventCode="2147483647" />
   </eventMappings>
</healthMonitoring>

For simplicity, I am logging All Events, and also removed the configuration sections for bufferModes and profiles which you can add if you need more control over logging. Note that I have mentioned the custom provider WCFCustomWebEventProvider in the type attribute while adding provider.

At-minimum, health monitoring needs you to configure three basic elements.

  1. Providers: You have to specify a provider which will decide the storage target and do the work to store logging information. You can also define multiple providers like for some critical errors, you need to log information in the database and also want to generate an email to the system administrator. In our exmaple, we have used only one custom provider which will store information by calling WCF client method.

  2. EventMappings: It contains all the events names we are interested to log information about. In our example I placed a single event name All Events, which enables the application to log data about all events. If you dont need to log data about all events, you can define the desired events according to the requirements.

    Some other available events are:

    • All Events
    • Heartbeats
    • Application Lifetime Events
    • Request Processing Events
    • Infrastructure Errors
    • Request Processing Errors
    • All Audits
    • Failure Audits
    • Success Audits
  3. Rules: Rule is basically a mapping you have to define, to decide which event should mapped to which provider. In our example, I have defined that eventName="All Events" should be handled by provider="WCFCustomWebEventProvider_1".

September 17, 2018

jQuery AJAX failed calling ASP.NET WebMethods

Recently I migrated all code files from ASP.NET Web Application to ASP.NET WebSite (with Framework 4.5.2), because of some requirements we need to move towards website template. At the time I faced this issue, all the ajax calls stopped working. When I placed breakpoint on WebMethod, It was not getting fired.

Here is the WebMethod code:


[WebMethod(EnableSession = true)]
public static string GetPageSummary(string pageid)
{
 string html = "";

 if (!string.IsNullOrEmpty(pageid))
 {
  html = PageHelper.GetPageSummaryHtml(pageid);
 }

 return html;
}

And here is the jquery Ajax call for that method:

$.ajax({

  type: "POST",
  url: "MyPage.aspx/GetPageSummary",
  data: "{ 'pageid':'" + id + "'}",
  contentType: "application/json; charset=utf-8",
  dataType: "json",
  success: function (response) {
   showModelPopupForSummary(response.d)
  },
  error: function (response) {   
   //error-handling code...
  }
 });

It all looks fine and working in previous project, but not with new WebSite project.

After spending way too much time on this, I found out the solution for this.

You have to change enum value for AutoRedirectMode. Go to App_Code/RouteConfig.cs, inside RegisterRoutes() method, you will see this line:

 settings.AutoRedirectMode = RedirectMode.Permanent;

Change the enum value to RedirectMode.Off, final statement would be:

 settings.AutoRedirectMode = RedirectMode.Off;

After changing this all my ajax calls started working successfully.

August 27, 2018

How to use table variable in dynamic sql?

I came across a scenario while working with user-defined-table-types where I was need to query from table variable using dynamic SQL. I feel it worth sharing here in case someone might need in future.

I have created a user-defined table-type:

CREATE TYPE [dbo].[UDTT_Items] AS TABLE(    
    [ItemId] int identity(1, 1),
    [ItemCode] [varchar](10) NULL,
    [ItemName] [varchar](255) NULL, 
    [StockQty] decimal(18,3 ) NULL, 
    PRIMARY KEY CLUSTERED 
(
    [ItemId] ASC
)WITH (IGNORE_DUP_KEY = OFF)
)
GO

In my stored procedure I declared a table variable for this UDTT:

declare @tblItems UDTT_Items

Here is how we normally use select statement on table variable.

select * from @tblItems

The problem comes when I tried to put this table variable in dynamic SQL. For example, if I try to run the above select statement from execute clause:

EXECUTE SP_EXECUTESQL N'select * from @tblItems'

It gives me the error message:

Must declare the table variable "@tblItems".

Fortuntely there is work around available by EXECUTE SP_EXECUTESQL. You can pass the table variable as a read-only parameter to dynamic SQL. So the following dynamic sql will work and run the given query as exptected.

DECLARE @SQL NVARCHAR(MAX);
SET @SQL ='SELECT * FROM @tblItems;';
EXECUTE SP_EXECUTESQL @SQL,N'@tblItems UDTT_Items READONLY',@tblItems;

Here I put my query in @SQL variable, and when calling EXECUTE SP_EXECUTESQL, we have to first define our parameters that need to be passed as second argument, and the actual table variable(i.e. parameter value) in third argument.

August 26, 2018

MS SQL Server - Show Query Execution Time

If you are using MS SQL Server 2008 or higher version, you can use the setting STATISTICS TIME. It will display the execution or run time in separate messages tab.

Setting this option ON will display the number of milliseconds required for complete statement cycle, i.e. parse, compile, and execute.

For example, following query will display order count for each customer who get registered in the month of August 2018.

select c.FirstName, c.LastName, c.RegistrationDate, Count(1) as OrderCount
from dbo.Customers c
 inner join dbo.Orders o on o.OrderId = c.OrderId
where 
 c.RegistrationDate between '1 August 2018' and '31 August 2018'
group by c.FirstName, c.LastName, c.RegistrationDate

Here is the sample output from messages tab, showing the execution time for above statement.

(200 rows affected)

 SQL Server Execution Times:
   CPU time = 1640 ms,  elapsed time = 2259 ms.
   

References:

July 21, 2018

Generating random strings with T-SQL

For testing scenarios you may need to generate random strings. You can use one of the following ways for this purpose.


declare @alphabet varchar(36) = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
select
substring(@alphabet, convert(int, rand()*36), 1) +
substring(@alphabet, convert(int, rand()*36), 1) +
substring(@alphabet, convert(int, rand()*36), 1) +
substring(@alphabet, convert(int, rand()*36), 1) +
substring(@alphabet, convert(int, rand()*36), 1);

This script will generate random string of length 5. If you need to increase or decrease number of characters in the generated string, you can add/remove substring statements in the above script.

A more easier approach can be to use NEWID() function, convert the result to varchar and then you can use LEFT(), RIGHT(), SUBSTRING() or any combination of string functions to generate string of desired length.

SELECT CONVERT(varchar(255), NEWID())

This will convert NEWID() output to varchar(255)

SELECT LEFT(CONVERT(varchar(255), NEWID()), 10)

This will display left 10 characters of the NEWID().

SELECT RIGHT(CONVERT(varchar(35), NEWID()), 10)

This will display 10 characters of the NEWID() from right.

SELECT SUBSTRING(CONVERT(varchar(35), NEWID()), 10, 25)

This will display substring of 15 characters starting from index 10 and ends at index 25.

June 11, 2018

Understanding DEFAULT constraints in MS SQL Server

If you have worked with multiple instances of a database, there is a high chance that you have faced issues with constraints default names. You generated the script for tables/schema changes and try to run on another instance of that database, where some constraint is being dropped from a table, and you get error message.

For example, you have an Items table, have a constraint defined on column Item_Code with default name, and a drop constraint statement like this:

 ALTER table [Items] drop DF__Items__Item_Code__07F6335B

It may worked on your current database instance, but on some other instances it may give you an error message like this:

 Msg 3728, Level 16, State 1, Line 1
 'DF__Items__Item_Code__07F6335B' is not a constraint.
 Msg 3727, Level 16, State 0, Line 1
 Could not drop constraint. See previous errors.

Why you get this error?

Because at the time you created this constraint, you have not provided a custom name, so SQL Server generated a default name. Since default name is auto-generated, there are no chances that this same name will be generated on each instance, no all other instances are having constraint with same name. When you try to run this drop constraint statement on other instances, you are getting this error.

Lets try to understand this with an example.

Create a new database.

Create a new table, lets say Items with this statement.

CREATE TABLE [dbo].[Items](
 [Id] [int] IDENTITY(1,1) NOT NULL,
 [Item_Code] [nvarchar](255) NOT NULL,
 [Item_Name] [nvarchar](255) NOT NULL,
 CONSTRAINT [PK_Items] PRIMARY KEY CLUSTERED 
(
 [Id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

At this point we have no constraints on any column of Items table. Lets defined a new one.

 ALTER TABLE [dbo].[Items] ADD  DEFAULT ('') FOR [Item_Code]
 GO

In this constraint, we are setting empty string as default value for column Item_Code. I did not provide any name, SQL Server has defined an auto-generated name, in my case it is DF__Items__Item_Code__07F6335A, and this is not a good approach.

Better is to define a custom name for constraints, which will remain static across all instances of this database.

Lets define a new constraint on Item_Name column, but this time with custom name:

 ALTER TABLE [dbo].[Items] ADD  CONSTRAINT [DF_Items_Item_Name]  DEFAULT ('') FOR [Item_Name]
 GO

Here constraint name is DF_Items_Item_Name, and will remain common on all nodes.

So far, we have created a new table and then defined constraints on existing columns.

The same problem will happen when you add new columns to this table. Lets add a new column CreatedBy with default name 'admin':

 ALTER TABLE [dbo].[Items] ADD [CreatedBy] [nvarchar](20) NOT NULL DEFAULT 'admin' 
 GO    

Here again, we did not provide a custom name, so SQL Server will place auto-generated. In my case it is DF__Items__CreatedBy__0DAF0CB0.

Good technique will be to provide custom name, lets create a new column CreatedOn, but this time with our specified name:

 ALTER TABLE [dbo].[Items] ADD [CreatedOn] [datetime] 
  CONSTRAINT [DF_Items_CreatedOn] DEFAULT (getdate()) NOT NULL 
 GO

Here constraint name is DF_Items_CreatedOn.

One thing to be aware that you can not change constraint names, you have to drop old one and create new constraint if you need to modify or rename.

I hope you found this post helpful. Share your thoughts in the comments below!

May 18, 2018

Crystal Report: could not load assembly

I moved crystal report files from ASP.Net website to Windows Forms Application, and run the application, then I got this error message on loading reports.

Could not load file or assembly 'file:///C:\Program Files\SAP BusinessObjects\Crystal Reports for .NET Framework 4.0\Common\SAP BusinessObjects Enterprise XI 4.0\win32_x86\dotnet1\crdb_adoplus.dll' or one of its dependencies. The system cannot find the file specified.

By adding following settings in app.config file solved this issue for me.

 <startup useLegacyV2RuntimeActivationPolicy="true">
  <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
 </startup>

April 16, 2018

Read web.config file outside the application folder in C#

I have a web application running on my server. And I needed to develop a small utility application which has to do some work in database, and I don't want to write same database credentials or connection strings(and some other additional app-settings) in this new console application, I want it to read settings from same web.config file. But the problem I was facing when I tried this code:

 string filePath = @"D:\MyProject\Web.config";
 Configuration c1 = ConfigurationManager.OpenExeConfiguration(filePath);
 var value1 = c1.AppSettings.Settings["Key1"].Value;

It was not giving any error if it was failed to load the given file path, but also was not working as per my expectations. The Configuration object c1 was loaded fine but when I tried to read ApSettings, it was empty. There was no key/value pairs loaded in this collection object. So trying to read value for Key1 was giving me error as:

 Object reference not set to an instance of an object. 

After wasting much time thinking and searcing on this issue, I found this solution. It worked fine and allowed me read config file from another application.

 string filePath = @"D:\MyProject\Web.config";
 var fileMap = new ExeConfigurationFileMap { ExeConfigFilename = filePath };
 var configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None);
 var value = configuration.AppSettings.Settings["Key1"].Value;

Here I get proper value of Key1, in variable value.

March 31, 2018

Running PHP script from the command line

I was need to call my php script from command prompt in Windows. This is what I found:

Let's say, we have PHP file myscript.php in Test folder inside htdocs folder of XAMPP installation (this is not mandatory, you can place your php file anywhere on your local hard drive), and I want to call it from command prompt.

We need to write it this way:

 php "D:\xampp\htdocs\Test\myscript.php"

It is assumed that your php.exe file path is set in environment PATH variable. If it is not included in environment PATH variable then you may need to write full path for php.exe. In my case, it is like this:

 "D:\xampp\php\php.exe" "D:\xampp\htdocs\Test\myscript.php"

March 17, 2018

XAMPP - PHP - Connect with MS SQL Server 2008 R2 in Laravel

Few days back I got stuck in this situation, I was developing PHP web application with Laravel 5.6 framework, the problems comes when I tried to connect with MS SQL Server rather than MySQL or MariaDB (that comes as default in XAMPP package), It started giving me error like driver not found. Here is how I solved this problem.

First you need to download Microsoft Drivers 4.3 for PHP for SQL Server, then extract the files to some folder. Copy the file php_pdo_sqlsrv_71_ts_x86.dll to php/ext folder (from XAMPP installation's root folder). Now we have to instruct PHP to use this extension.

Open php/php.ini file and move the cursor to extensions section. Add following new line of extension which directs PHP to use SQL Server driver's dll.

extension=php_pdo_sqlsrv_71_ts_x86.dll

We have successfully set-up our PHP environment with MS SQL Server Driver.

Don't forget to restart your server afterwards.

Now you should be able to successfully connect with SQL Server from PHP.

Here comes the Laravel part. You have to specify connection string parameters in .env file of Laravel root folder.

DB_HOST=MY-PC\MYSQL2008R2
DB_PORT=1433
DB_DATABASE=ITEMMASTER
DB_USERNAME=myuser
DB_PASSWORD=mypassword

Or alternative way is to define connection parameters in config/database.php file.

'sqlsrv' => [
            'driver' => 'sqlsrv',
            'host' => 'MY-PC\MYSQL2008R2',
            'port' => '1433',
            'database' => 'ITEMMASTER',
            'username' => 'myuser',
            'password' => 'mypassword',
            'charset' => 'utf8',
            'prefix' => '',
        ],

Now Laravel website should be able to connect with SQL Server database.

I hope you found this post helpful. Do you agree? Share your thoughts in the comments below!

February 24, 2018

ASP.NET MVC - DateTime format for each request

I was being asked this question by one of my colleague, if we can set a DateTime format at some global level so that if we need to display/process date at multiple controllers or views, we should not be worried about a specific format. I came up with this solution, I am sharing here, and welcome your suggestion or comments for any improvements or alternative solutions.

Lets say we need to display and save date in yyyy/MM/dd format. Since our server default format is set to dd/MM/yyyy, and we could not change it because some other applications might get affected, so we decided to set the required format at request level in our ASP.Net MVC application. For this we have to set the date format for current culture for each request, i.e. inside Global.asax.cs, in Application_BeginRequest() event.

Here is the real code which will set the format for current culture with the required one.

protected void Application_BeginRequest(Object sender, EventArgs e) 
{
 CultureInfo newCultureInfo = (CultureInfo)System.Threading.Thread.CurrentThread.CurrentCulture.Clone();
        newCultureInfo.DateTimeFormat.ShortDatePattern = "yyyy/MM/dd";
        //newCultureInfo.DateTimeFormat.LongDatePattern = "yyyy/MM/dd";
        //newCultureInfo.DateTimeFormat.FullDateTimePattern = "yyyy/MM/dd";
        newCultureInfo.DateTimeFormat.DateSeparator = "/";
        Thread.CurrentThread.CurrentCulture = newCultureInfo;
}

Here I am setting the DateTimeFormatInfo object's ShortDatePattern property to "yyyy/MM/dd", in CurrentCulture. Similarly you can set other required properties like LongDatePattern, FullDateTimePattern, ShortTimePattern or LongTimePattern etc. In this way you don't have to set the required date pattern in multiple places.

January 17, 2018

How to validate only numbers in string variable in C#?

There are multiple ways to validate user input string in order to allow only numbers. Lets say we have a string in variable myInput, following are some tips you can use to check if the input string is only contains numbers.

Check for numbers while entering data

If you are using Winforms and want to validate user input, then you can use TextBox's KeyPress event.

private void txtNumber_KeyPress(object sender, KeyPressEventArgs e)
{
  if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar) && e.KeyChar != '.')
  {
    e.Handled = true;
  }
}

You can set e.Handled property to true if the character entered is not the desired one, it will suppress that key. You can use similar technique in JavaScript if you are working with ASP.NET.

Check for numbers if you have a string variable

In many cases, if you are not using Winforms application or you may need to inspect the string variable other than the KeyPress event, then you can use one of following methods.

If the string variable contains a value within valid integer range, then you can use int.TryParse() function to check if it has valid integer value.

private bool ValidateNumber(string myInput)
{
    int val;
    if (int.TryParse(myInput, out val))
    {
       return true;
    }
    else
    {
        return false;
    }
}

Note: This method has limitation that the input is being checked only for valid integers, which can have a maximum value of 2,147,483,647. For example, if you need to verify a string of 15 digits, then this method may not give you the desired result.

In order to validate a string with numbers having more digits, we can use the following options:

Char.IsDigit can be used to check if the character at any specified position in string is a digit, and here we are using Linq function All to our string, which will call Char.IsDigit function for each character in the string, and hence can get the required output.

 myInput.All(Char.IsDigit)

A more formal method can be used, i.e. Regular Expressions, which can give you more control in different scenarios. Here we are using Regular Expression "^[0-9]*$" to match for numbers in input string.

 System.Text.RegularExpressions.Regex.IsMatch(myInput, "^[0-9]*$")

I hope you find this post helpful, I welcome your comments or suggestions if you may find any more alternative(s) to validate numeric figures in a string.