Friday, November 21, 2014

c# - Using Custom Partition in Task Parallel Library TPL

For one of my project I wanted to take all the files in a folder and merge 100 files together into 1 file resulting in a smaller set of files more or else of similar sizes.I had around 30 thousand files which are merged to 300 files.I used custom partitioner in TPL to do this.Below is the sample code that I started with.

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace CustomPartitioner

{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                int iFileNumber = 0;
                string sInputFolderPath =ConfigurationManager.AppSettings["INPUT_FOLDER_PATH"];
                string[] sFileList = Directory.GetFiles(sInputFolderPath, "*.gz");
               
                var rangePartitioner = Partitioner.Create(0, sFileList.Length, 100);//100 is the merge size
               
                Parallel.ForEach(rangePartitioner, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }, (range, loopState) =>
               {
                        try
                       {
                                using (StreamWriter oWriter = new StreamWriter(@"c:\Index_"+ Interlocked.Increment(ref iFileNumber)+".txt"))
                               {
                                    for (int iIndex = range.Item1; iIndex < range.Item2; iIndex++)
                                   {
                                        try
                                       {
                                               oWriter.WriteLine(string.Format("{0}\t{1}", sFileList[iIndex], iIndex));
                                        }
                                        catch (Exception ex)
                                       {
                                            Console.WriteLine(ex.Message);
                                        }
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                Console.WriteLine(ex.Message);
                                Console.WriteLine(ex.InnerException);
                                Console.WriteLine(ex.StackTrace);
                            }
                 });           
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.InnerException);
                Console.WriteLine(ex.StackTrace);
            }
            Console.WriteLine("Done !!!");
            Console.Read();
        }
    }
}

c# - Load large text files to database

Two ways to load a large text files to a table in the database are using
  • bcp command with xp_cmdShell enabled on the database server
  • bulk insert
The files I load are in the range of 10-15 GB tab delimited text files.These files are created by daily processes and are stored on file servers running Windows Server 2008.After the files are created they are loaded to SQL Server Database.

bcp command with xp_cmdShell


using System;
using System.Text;
using System.Configuration;
using System.IO;
using System.Data.SqlClient;

public class LoadLargeFileToDB


          static void Main()
          {
                 string sConnectionString = "server=MyServer;database=MyDatabase;uid='username';pwd='password'";
                 string sServer = "MyServer";
                 string sTableName = "MyTable";
                 string sImportFilePath = @"\\Servername\DriveName\SharedFolder\FileName.txt";


                 try
                 {
                      if (!File.Exists(sImportFilePath))
                              throw new Exception("Error importing BCP file = " + sImportFilePath + ". File does not exists.");
                                     
                      using (SqlConnection oSqlConnection = new SqlConnection(sConnectionString))
                      {
                             oSqlConnection.Open();
                             SqlCommand oSqlCommand = new SqlCommand("exec xp_cmdShell 'bcp " + sTableName + " in " + sImportFilePath + " -c -T -S " + sServer + "'", oSqlConnection);
                             oSqlCommand.CommandTimeout = 120000;
                             oSqlCommand.ExecuteNonQuery();
                      }
                }
                catch (Exception ex)
                {
                      Console.Error(ex.Message);
                      Console.Error(ex.InnerException);
                      Console.Error(ex.StackTrace);
                }
         }
}


Bulk insert

using System;
using System.Text;
using System.Configuration;
using System.IO;
using System.Data.SqlClient;

public class LoadLargeFileToDB


          static void Main()
          {
                 string sConnectionString = "server=MyServer;database=MyDatabase;uid='username';pwd='password'";
                 string sServer = "MyServer";
                 string sTableName = "MyTable";
                 string sImportFilePath = @"\\Servername\DriveName\SharedFolder\FileName.txt";


                 try
                 {
                      if (!File.Exists(sImportFilePath))
                              throw new Exception("Error importing BCP file = " + sImportFilePath + ". File does not exists.");
                                     
                      using (SqlConnection oSqlConnection = new SqlConnection(sConnectionString))
                      {
                             oSqlConnection.Open();
                             SqlCommand oSqlCommand = new SqlCommand("bulk insert "+ sTableName +" from '"+ sImportFilePath +"'";, oSqlConnection);
                             oSqlCommand.CommandTimeout = 120000;
                             oSqlCommand.ExecuteNonQuery();
                      }
                }
                catch (Exception ex)
                {
                      Console.Error(ex.Message);
                      Console.Error(ex.InnerException);
                      Console.Error(ex.StackTrace);
                }
         }
}

Thursday, November 6, 2014

c# - Send alerts when database table row value is changed

Requirement:
Send an alert when any column value in a given list of rows in a table changes.

There are different ways to build an alert system when a row's column value changes but in this case I am using C# to write a program that tracks these changes and send an email report with the changes.

The C# program is scheduled to run every minute to check whether the row value changed.

Few things to note.The table has primary key so all rows are unique.The table rows do not change often  and the size of the table is small with only a few thousand rows.Also we want to only track a subset of rows in the table.


Step 1: Create a file or table that holds the rows from the original table that will be tracked.The primary key list that will be tracked is stored in the configuration file.In this case I will be using a file to store these rows.


Step 2: Extract the rows based on the primary key list in configuration file from the original table and compare with the rows in the file or table created in the Step 1.


Step 3: If the column values are different add the primary key,column name,old value,new value to the report.


Step 4: Send an email with the report created in Step 3


Step 5: If the primary key count in the configuration file has changed or does not match with the rows in the file created in Step 1,then overwrite the file with the new list of rows that will be tracked.


Below is the entire code


using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Net.Mail;
using System.Text;
using NLog;

namespace MyCompany.MyDept.MyProject.TableRowEditAlert

{
    class Program
    {
        static void Main(string[] args)
        {
            Logger oLog = LogManager.GetCurrentClassLogger();
            try
            {
                string sConnectionString = ConfigurationManager.AppSettings["MY_DB"];
                string sIdList = ConfigurationManager.AppSettings["ID_LIST"];
                string sFilePath = ConfigurationManager.AppSettings["FILE_PATH"];

                DataSet ds = GetIdDetails(sConnectionString, sIdList);

                if (ds != null)
                {
                    if (!File.Exists(sFilePath))
                    {
                        CreateIdDetailsFile(sFilePath, ds,true);
                    }
                    else
                    {
                        string [] sFileLines = File.ReadAllLines(sFilePath);
                        if ((sFileLines.Length - 1) != ds.Tables[0].Rows.Count)
                        {
                            CreateIdDetailsFile(sFilePath, ds, false);
                        }
                        else
                        {
                            StringBuilder sDiff = new StringBuilder();
                            string [] sCols = sFileLines[0].Split('\t');
                           
                            for (int iIndex = 1; iIndex < sFileLines.Length; iIndex++)
                            {
                                string[] sFileLineItems = sFileLines[iIndex].Split('\t');
                                for (int jIndex = 0; jIndex < sFileLineItems.Length; jIndex++)
                                {
                                    if (ds.Tables[0].Rows[iIndex -1][jIndex] != null && ds.Tables[0].Rows[iIndex-1][jIndex].ToString() != sFileLineItems[jIndex])
                                    {
                                        sDiff.Append(sFileLineItems[0]);
                                        sDiff.Append('\t');
                                        sDiff.Append(sFileLineItems[6]);
                                        sDiff.Append('\t');
                                        sDiff.Append(sFileLineItems[1]);
                                        sDiff.Append('\t');
                                        sDiff.Append(sCols[jIndex]);
                                        sDiff.Append('\t');
                                        sDiff.Append(sFileLineItems[jIndex]);
                                        sDiff.Append('\t');
                                        sDiff.Append(ds.Tables[0].Rows[iIndex-1][jIndex].ToString());
                                        sDiff.Append('\n');
                                    }
                                }
                            }
                             if (!string.IsNullOrEmpty(sDiff.ToString()))
                            {
                                SendMail(GenerateReport(sDiff.ToString()).ToString(), ConfigurationManager.AppSettings["EMAIL_SUBJECT"], true, ConfigurationManager.AppSettings["NOTIFICATION_LIST"].Split(','));

                                CreateIdDetailsFile(sFilePath, ds, false);

                            }
                        }
                    }
                }
                
            }
            catch (Exception ex)
            {
                oLog.Error(ex.Message);
                oLog.Error(ex.StackTrace);
            }
        }

        private static void CreateIdDetailsFile(string sFilePath, DataSet ds,bool bAppend)

        {
            using (StreamWriter oWriter = new StreamWriter(sFilePath,bAppend))
            {
                StringBuilder sb = new StringBuilder();
                foreach (DataColumn col in ds.Tables[0].Columns)
                {
                    sb.Append(col.ColumnName);
                    if (col.Ordinal != ds.Tables[0].Columns.Count-1)
                        sb.Append('\t');
                }
                sb.Append(Environment.NewLine);

                foreach (DataRow row in ds.Tables[0].Rows)

                {
                    foreach (DataColumn col in ds.Tables[0].Columns)
                    {
                        sb.Append(row[col] != null ? row[col].ToString() : "");
                        if(col.Ordinal != ds.Tables[0].Columns.Count - 1)
                            sb.Append('\t');
                    }
                    sb.Append(Environment.NewLine);
                }
                oWriter.Write(sb.ToString());
            }
        }

        private static DataSet GetIdDetails(string sConnectionString, string sIdList)

        {
            using (SqlConnection oConn = new SqlConnection(sConnectionString))
            {
                oConn.Open();
                SqlCommand oCmd = new SqlCommand("select * from dbo.my_table where id in (" + sIdList + ") order by id", oConn);
                oCmd.CommandType = CommandType.Text;
                SqlDataAdapter oDataAdapter = new SqlDataAdapter(oCmd);
                DataSet ds = new DataSet();
                oDataAdapter.Fill(ds);
                return ds;
            }
        }

        public static void SendMail(String message, String subject, Boolean p_bIsHtml, String[] notificationList)

        {
            try
            {
                MailMessage MyMessage = new MailMessage();
                MyMessage.From = new MailAddress(ConfigurationManager.AppSettings["EMAIL_FROM"]);
                foreach (String email in notificationList)
                    MyMessage.To.Add(new MailAddress(email));
                MyMessage.IsBodyHtml = p_bIsHtml;
                MyMessage.Subject = subject;
                MyMessage.Body = message;

                SmtpClient emailClient = new SmtpClient("smtp.office.mycompany.com");

                emailClient.Send(MyMessage);
            }
            catch
            {
            }
        }

        private static StringBuilder GenerateReport(string sDiff)

        {
            string [] sLines = sDiff.Split('\n');
            StringBuilder sbHtml = new StringBuilder();
            sbHtml.AppendLine(@"<style type='text/css'>td{font-size: 9px;font-family: verdana} </style>");
            sbHtml.AppendLine("<table><border=1><tr bgcolor=Orange align=center><td colspan=6><b>Table Row Edit Alert</b></td></tr><tr bgcolor=LightBlue align=center><td><b>ID</b></td><td><b> Name</b></td><td><b> URL</b></td><td><b>Column</b></td><td><b>Old Value</b></td><td><b>New Value</b></td></tr>");
            foreach (string  s in sLines)
            {
                if(!string.IsNullOrEmpty(s))
                {
                    string[] sLineItems = s.Split('\t');
                    sbHtml.AppendLine("<tr bgcolor=LightGreen align=center><td><b>" + sLineItems[0] + "</b></td><td><b>" + sLineItems[1] + "</b></td><td><b>" + sLineItems[2] + "</b></td><td><b>" + sLineItems[3] + "</b></td><td><b>" + sLineItems[4] + "</b></td><td><b>" + sLineItems[5] + "</b></td></tr>");
                }
            }
            sbHtml.AppendLine("</table");
            sbHtml.AppendLine("<br/>");
            sbHtml.AppendLine("<br/>");
            return sbHtml;
        }
    }
}

Configuration File

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="MY_DB" value="server=server_name;database=database_name;uid='my_id';pwd='my_password';"/>
    <add key="ID_LIST" value="100,101,102,103,500,501,501,701,703,704" />
    <add key="FILE_PATH" value="C:\Data\Id.txt"/>
    <add key="EMAIL_FROM" value="admin@mycompany.com"/>
    <add key="EMAIL_SUBJECT" value="Table Row Edit Alert"/>
    <add key="NOTIFICATION_LIST" value="my_email@mycompany.com"/>
  </appSettings>
</configuration>