Showing posts with label SQL Server. Show all posts
Showing posts with label SQL Server. Show all posts

Wednesday, October 13, 2010

How to validate a CSV file while importing to a SQL table

This post is based on How to import CSV or Text file to SQL table using SqlBulkCopy in C#, which contain all the explanation about the data import validating excising records and bulk import.

Validating file schema is the next hurdle. For this I have imported the Importer’s column mapping class to handle column type and column width. So that invalid file will be thrown out with an exception.
Based on the table, 
I add column mappings with column types and widths of text fields
///
/// Set column mappings
///
importer.DateTimeFormat = "dd-MMM-yyyy";
importer.ColumnMappings.Add(new ColumnMapping("Id""Id"DataType.Short));
importer.ColumnMappings.Add(new ColumnMapping("Name""Name"DataType.Text, 50));
importer.ColumnMappings.Add(new ColumnMapping("Grade""Grade"DataType.Text, 3));
 Will create a schema file like this,
When we input a CSV file in correct format like this
Importer will import the file. But an Invalid file like this will be thrown with an exception.

Full example
Mark-up:
<%@ Page Language="C#" %>
<html>
<head id="Head1" runat="server">
</head>
<body>
    <form id="form1" runat="server">
        <asp:FileUpload runat="server" ID="fuFile" />
        <asp:Button runat="server" ID="btnImport" OnClick="Import" Text="Import" />
        <asp:DropDownList runat="server" ID="ddlFileType">
            <asp:ListItem Value="CSV" Selected="True">CSV Delimited</asp:ListItem>
            <asp:ListItem Value="TAB">Tab Delimited</asp:ListItem>
        </asp:DropDownList>
    </form>
</body>
</html>
Code:
namespace ActiveTest
{
    public partial class Test : Page
    {
        protected void Import(object sender, EventArgs e)
        {
            String path = Server.MapPath("~/Data/");
            try
            {
                if (this.fuFile.HasFile)
                {
                    /// 
                    /// Upload file
                    ///
                    string fileName = this.fuFile.FileName;
                    string fullName = Path.Combine(path, fileName);
                    if (File.Exists(fullName)) File.Delete(fullName);
                    this.fuFile.PostedFile.SaveAs(fullName);
                    ///
                    /// Instantiate DelimitedDataImporter and set connection string
                    ///
                    DelimiterType type = DelimiterType.CsvDelimited;
                    if (this.ddlFileType.SelectedValue.Equals("TAB")) type = DelimiterType.TabDelimited;
                    DelimitedFileImporter importer = new DelimitedFileImporter(type);
                    importer.ConnectionString =
                        ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
                    ///
                    /// Find existing records and set filter statement [OPTIONAL]
                    ///
                    StringBuilder sb = new StringBuilder(string.Empty);
                    using (SqlConnection con = new SqlConnection(
                        ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString))
                    {
                        SqlCommand cmd = new SqlCommand("SELECT Id FROM Student", con);
                        con.Open();
                        SqlDataReader reader = cmd.ExecuteReader();
                        while (reader.Read())
                            sb.AppendFormat("{0},", reader.GetInt32(0).ToString());
                        reader.Close();
                        con.Close();
                    }
                    string ids = string.Empty;
                    if (!string.IsNullOrEmpty(sb.ToString())) ids = sb.ToString().Remove(sb.Length - 1, 1);
                    if (!string.IsNullOrEmpty(ids)) importer.Filter =
                        string.Format("Id NOT IN ({0})", ids);
                    ///
                    /// Set column mappings
                    ///
                    importer.DateTimeFormat = "dd-MMM-yyyy";
                    importer.ColumnMappings.Add(new ColumnMapping("Id""Id"DataType.Short));
                    importer.ColumnMappings.Add(new ColumnMapping("Name""Name"DataType.Text, 50));
                    importer.ColumnMappings.Add(new ColumnMapping("Grade""Grade"DataType.Text, 3));
                    ///
                    /// Import data to sql table
                    ///
                    importer.ImportToDatabase(fullName, "Student");
                }
            }
            catch
            {
                throw new Exception("Import Faild");
            }
        }      
    }
 
    #region EventArgs Specification
 
    public enum DelimiterType
    {
        TabDelimited,
        CsvDelimited,
        CustomDelimited
    }
    public class ImportDelimitedEventArgs : EventArgs
    {
        private ReadOnlyCollection<object> content;
        private int lineNumber;
 
        public ImportDelimitedEventArgs(OleDbDataReader reader, int number)
        {
            object[] columns = new object[reader.FieldCount];
            reader.GetValues(columns);
            content = new ReadOnlyCollection<object>(columns);
            lineNumber = number;
        }
        public bool BreakImport { getset; }
        public int LineNumber
        {
            get { return lineNumber; }
        }
        public ReadOnlyCollection<object> Content
        {
            get { return content; }
        }
    }
 
    #endregion
 
    #region Mappings
 
    public class ColumnMapping
    {
        public string SourceColumn { getset; }
        public string DestinationColumn { getset; }
        public DataType Type { getset; }
        public int Width { getset; }
        public bool IsValidatble { getset; }
        public ColumnMapping(string sourceColumn, string destinationColumn)
        {
            this.SourceColumn = sourceColumn;
            this.DestinationColumn = destinationColumn;
        }
        public ColumnMapping(string sourceColumn, string destinationColumn, DataType type)
        {
            this.SourceColumn = sourceColumn;
            this.DestinationColumn = destinationColumn;
            this.Type = type;
            if (this.Type == DataType.Text)
                throw new ArgumentNullException("Width should be specified when the data type is Text");
            this.IsValidatble = true;
        }
        public ColumnMapping(string sourceColumn, string destinationColumn, DataType type, int width)
        {
            this.SourceColumn = sourceColumn;
            this.DestinationColumn = destinationColumn;
            this.Type = type;
            this.Width = width;
            this.IsValidatble = true;
        }
    }
    public class ColumnMappingCollection : List<ColumnMapping>
    {
        public bool IsValidatable
        {
            get
            {
                foreach (ColumnMapping m in this) 
                    if (!m.IsValidatble) return false;
                return true;
            }
        }
        public bool HasDateFields
        {
            get
            {
                foreach (ColumnMapping m in thisif (m.Type == DataType.DateTime) return true;
                return false;
            }
        }
    }
 
    #endregion
 
    #region Types
 
    public enum DataType
    {
        Bit,
        Byte,
        Short,
        Long,
        Currency,
        Single,
        Double,
        DateTime,
        Text,
        Memo
    }
 
    #endregion
 
    #region Importer
 
    public class DelimitedFileImporter
    {
        #region Event handler
 
        public event EventHandler<ImportDelimitedEventArgs> ProcessLine;
        private bool HandleLine(OleDbDataReader reader, int number)
        {
            bool result = true;
            EventHandler<ImportDelimitedEventArgs> temp = ProcessLine;
            if (temp != null)
            {
                ImportDelimitedEventArgs args = new ImportDelimitedEventArgs(reader, number);
                temp(this, args);
                result = !args.BreakImport;
            }
            return result;
        }
 
        #endregion
 
        #region Constructors
 
        public DelimitedFileImporter()
            : this(DelimiterType.TabDelimited, null)
        {
            this.Initialize();
        }
        public DelimitedFileImporter(DelimiterType delimiter)
            : this(delimiter, null)
        {
            this.Initialize();
        }
        public DelimitedFileImporter(DelimiterType delimiterType, string delimiter)
        {
            this.Delimiter = delimiterType;
            this.CustomDelimiter = delimiter;
            this.Initialize();
        }
        private void Initialize()
        {
            this.ColumnMappings = new ColumnMappingCollection();
        }
 
        #endregion
 
        #region Properties
 
        public DelimiterType Delimiter { getset; }
        public string CustomDelimiter { get;set;}
        public string Filter { getset; }
        public string ConnectionString { getset; }
        public string DateTimeFormat { getset; }
        public ColumnMappingCollection ColumnMappings { getset; }
 
        #endregion
 
        #region Methods
 
        public void ImportToDatabase(string fileName, string tableName)
        {
            if (string.IsNullOrEmpty(this.ConnectionString))
                throw new ArgumentNullException("Connection String");
            FileInfo file = new FileInfo(fileName);
            WriteSchemaIniFile(file);
            using (OleDbConnection con = JetConnection(file))
            {
                using (OleDbCommand cmd = JetCommand(file, con))
                {
                    con.Open();
                    using (OleDbDataReader reader = cmd.ExecuteReader())
                    {
                        SqlBulkCopy sqlBulk = new SqlBulkCopy(this.ConnectionString);
                        sqlBulk.DestinationTableName = tableName;
                        foreach (ColumnMapping map in this.ColumnMappings)
                            sqlBulk.ColumnMappings.Add(map.DestinationColumn, map.SourceColumn);
                        sqlBulk.WriteToServer(reader);
                    }
                }
            }
        }
        private OleDbConnection JetConnection(FileInfo file)
        {
            StringBuilder connection = new StringBuilder("Provider=Microsoft.Jet.OLEDB.4.0");
            connection.AppendFormat(";Data Source=\"{0}\"", file.DirectoryName);
            connection.Append(";Extended Properties='text;HDR=Yes");
            if (this.Delimiter == DelimiterType.CustomDelimited)
            {
                if (this.CustomDelimiter == null)
                    throw new InvalidOperationException("Custom delimiter is not specified");
                connection.AppendFormat(";FMT=Delimited({1})"this.CustomDelimiter);
            }
            connection.Append("';");
            return new OleDbConnection(connection.ToString());
        }
        private OleDbCommand JetCommand(FileInfo file, OleDbConnection con)
        {
            StringBuilder commandText = new StringBuilder("SELECT * FROM ");
            commandText.AppendFormat("[{0}]", file.Name);
            if (this.Filter != null)
            {
                commandText.Append(" WHERE ");
                commandText.Append(this.Filter);
            }
            OleDbCommand cmd = new OleDbCommand(commandText.ToString(), con);
            cmd.CommandTimeout = 60000;
            return cmd;
        }
        private void WriteSchemaIniFile(FileInfo file)
        {
            string schema = Path.Combine(file.DirectoryName, "Schema.ini");
            if (!File.Exists(schema)) File.Delete(schema);
            using (StreamWriter writer = new StreamWriter(schema))
            {
                writer.WriteLine(string.Format(CultureInfo.InvariantCulture, "[{0}]", file.Name));
                if (this.ColumnMappings.IsValidatable) writer.WriteLine("ColNameHeader=True");             
                switch (this.Delimiter)
                {
                    case DelimiterType.CustomDelimited:
                        writer.WriteLine(string.Format(CultureInfo.InvariantCulture,
                            "Format=Delimited({0})"this.CustomDelimiter));
                        break;
                    case DelimiterType.CsvDelimited:
                    case DelimiterType.TabDelimited:
                    default:
                        writer.WriteLine(string.Format(CultureInfo.InvariantCulture,
                            "Format={0}"this.Delimiter));
                        break;
                }
                if (this.ColumnMappings.HasDateFields)
                {
                    if (string.IsNullOrEmpty(this.DateTimeFormat))
                        throw new ArgumentNullException("DateTimeFormat missing");
                    else
                        writer.WriteLine(string.Format(CultureInfo.InvariantCulture, 
                            "DateTimeFormat={0}"this.DateTimeFormat));
                }
                if (this.ColumnMappings.IsValidatable)
                {
                    foreach (ColumnMapping m in this.ColumnMappings)
                    {
                        writer.WriteLine("Col{0}={1} {2}{3}", 
                            this.ColumnMappings.IndexOf(m) + 1, m.SourceColumn, 
                                m.Type, m.Type == DataType.Text ? " Width " + m.Width.ToString() : string.Empty);
                    }
                }
            }
        }
 
        #endregion
    }
 
    #endregion
}

Wednesday, September 15, 2010

How to serialize object to a string as XML and deserialize XML string to an object (Useful when saving multiple selections to database fileds like checkbox selections)

When saving values of fields where we have multiple selections, it is usual way to save as comma separated values in a text field in the database. However, this make extreamly hard to query and edit those values. So as an alternative way we can serialize the object to a string of XML and then save the string of XML to a XML database field where we can perform X-Path Queries. (For SQL Server X-Path - Please see this article). Then when retrieving values from the database, we can read the XML string from database and desrialize XML string in to our respective object.
This example demonstrate how to serialize and deserialize List<string> object to XML string and deserialize XML string to a List<string> object.
protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);
 
    List<string> values = new List<string>();
    for (int i = 0; i < 10; i++)
        values.Add("Value " + i);
 
    string xml = this.Serialize(values);
    ///
    /// Save database as XML so that you can do a XPath Query
    ///
 
 
    ///
    /// Get xml string and convert it in to a List<string>
    ///
    List<string> savedValues = this.Deserialize(xml);
 
}
private string Serialize(List<string> values)
{
    XmlSerializer serializer = new XmlSerializer(typeof(List<string>));
    MemoryStream stream = new MemoryStream();
    XmlTextWriter writer = new XmlTextWriter(stream, Encoding.UTF8);
    serializer.Serialize(writer, values);
    stream = (MemoryStream)writer.BaseStream;
    UTF8Encoding utf8 = new UTF8Encoding();
    string xml = utf8.GetString(stream.ToArray());
    return xml;
}
private List<string> Deserialize(string xml)
{
    XmlSerializer serializer = new XmlSerializer(typeof(List<string>));
    UTF8Encoding utf8 = new UTF8Encoding();
    byte[] bytes = utf8.GetBytes(xml);
    MemoryStream stream = new MemoryStream(bytes);
    XmlTextReader reader = new XmlTextReader(stream);
    List<string> values = (List<string>)serializer.Deserialize(reader);
    return values;
}

Wednesday, August 18, 2010

How to import an Microsoft Excel file to database table using BulkCopy

It is a common scenario that we need to import excel files to database tables. This article demonstrate how to copy a excel file to a database table using BulkCopy. There are two scenarios in this case.
  • Copy the excel file in to server direcotry (upload file) while uploading the file it is vital to validate the file type. For detail demonstration of how to validate a file using regular expressions, please refer this article, which is not included this snippet for the clarity.
  • Process the file.
Download Source Files
    Database table

    Microsoft Excel sheet


    Code
    <%@ Page Language="C#" %>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head id="Head1" runat="server">
        <script runat="server">
            protected void Import(object sender, EventArgs e)
            {
                String path = Server.MapPath("~/Data/");
                try
                {
                    if (this.fuFile.HasFile)
                    {
                        string fileName = this.fuFile.FileName;
                        string fullName = Path.Combine(path, fileName);
                        if (File.Exists(fullName)) File.Delete(fullName);
                        this.fuFile.PostedFile.SaveAs(fullName);
                        StringBuilder sb = new StringBuilder(string.Empty);
                        using (SqlConnection con = new SqlConnection(
                            ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString))
                        {
                            SqlCommand cmd = new SqlCommand("SELECT Id FROM Student", con);
                            con.Open();
                            SqlDataReader reader = cmd.ExecuteReader();
                            while (reader.Read())
                                sb.AppendFormat("{0},", reader.GetInt32(0).ToString());
                            reader.Close();
                            con.Close();
                        }
                        string ids = string.Empty;
                        if (!string.IsNullOrEmpty(sb.ToString())) ids = sb.ToString().Remove(sb.Length - 1, 1);
                        string excelConnectionString = string.Format(
                                @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0}; 
                                Extended Properties='Excel 8.0;HDR=YES;'", fullName);
                        using (OleDbConnection excelConnection = new OleDbConnection(excelConnectionString))
                        {
                            OleDbCommand ocmd = new OleDbCommand(
                                string.Format("SELECT * FROM [Results$]{0}"string.IsNullOrEmpty(ids) ? 
                                string.Empty : string.Format(" WHERE StudentId NOT IN ({0})", ids)), excelConnection);
                            excelConnection.Open();
                            OleDbDataReader dReader;
                            dReader = ocmd.ExecuteReader();
                            SqlBulkCopy sqlBulk = new SqlBulkCopy(
                                ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);
                            sqlBulk.DestinationTableName = "Student";
                            sqlBulk.ColumnMappings.Add("StudentId""Id");
                            sqlBulk.ColumnMappings.Add("Name""Name");
                            sqlBulk.ColumnMappings.Add("Grade""Grade");
                            sqlBulk.WriteToServer(dReader);
                            dReader.Close();
                            excelConnection.Close();
                        }
     
                    }
                }
                catch
                {
                    throw new Exception("Import Faild");
                }
            }        
        </script>
    </head>
    <body>
        <form id="form1" runat="server">
            <asp:FileUpload runat="server" ID="fuFile" />
            <asp:Button runat="server" ID="btnImport" OnClick="Import" Text="Import" />
        </form>
    </body>
    </html>

    Wednesday, April 22, 2009

    Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding – Microsoft ADO.NET


    Symptom: Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.

    Resolution:

    1. SqlConnection: If this happen when tries to connecting to SQL Server, then try increasing the timeout of the connection
      Connection String: server=Server;database=Database;uid=Uid;pwd=Pwd;timeout=600 (ConnectionTimeout is read-only)
    2. SqlCommand: More interestingly if you manage to connect successfully and although you set a fat timeout value to your connection command object does not inherit the timeout from its connection. It uses its own timeout value. More logically ConnectionTimeout and CommandTimeout.   
        So increase the timeout of the command, which is not read-only like SQLConnection

        command.CommandTimeot = 600 (set to 10 minutes)  

    Reference: ASP.NET Forums

    Friday, February 27, 2009

    Login Failed The user is not associated with a trusted SQL Server connection: SQL Server 2005


    This occurs when you try to login using SQL Server user cardinals without configuring SQL Server to use Windows and SQL Server authentication.

    Resolution:

    [1] Open SQL Server management studio – [2] Login to SQL server using windows authentication – [3] Right click on SQL Server instance and go to properties – [4] Expand security tab and select 'SQL Server and Windows authentication mode' under 'Server Authentication'

    Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding - Microsoft SQL Server 2005


    Symptom:

    SQL Server throws an exception 'Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding. (Microsoft SQL Server, Error: -2) when try to connect from a remote computer. Most probable symptom for the error is TCP Port 1433 being blocked by the windows firewall in a computer running windows XP(SP2).

    Solution

    Scenario 1

    Make sure you have enabled remote connections from the SQL Server surface area configuration and allow TCP/IP, Named Pipe protocol or combination as necessary.

    • To Enable Remote connections: [1] SQL Server 2005 – [2] Configuration Tools – [3] SQL Server Surface area configuration – [4] Click on 'Surface area configuration for services and connections'.
    • Expand [1] Database Engine – [2] Remote Connections – [3] Select Local and remote connections – [4] Select the desired protocol (In this scenario I have used TCP/IP and Named Pipes)
    • Restart the SQL Server.
    Scenario 2

    Open TCP Port 1433 – This is the default port that SQL server use for remote connections. By default windows XP firewall deny access of this port:

    • Open [1] Control Panel – [2] Windows Firewall – [4] Click on 'Add Port'
    • Add: Name = SQL Server (Any meaningful name), Port Number = 1433 (by default, if you have changed the default port you have to make sure you enter the corresponding port number)

    Azure Storage Account Types

    Defferent Types of Blobs Block blobs store text and binary data. Block blobs are made up of blocks of data that can be managed individually...