Search

Tuesday, July 22, 2008

Locking in Microsoft SQL Server

Introduction

In this article, I want to tell you about SQL Server 7.0/2000 Transaction Isolation Levels, what kinds of Transaction Isolation Levels exist, and how you can set the appropriate Transaction Isolation Level, about Lock types and Locking optimizer hints, about deadlocks, and about how you can view locks by using the sp_lock stored procedure.

Transaction Isolation Levels

There are four isolation levels:

  • READ UNCOMMITTED
  • READ COMMITTED
  • REPEATABLE READ
  • SERIALIZABLE

    Microsoft SQL Server supports all of these Transaction Isolation Levels and can separate REPEATABLE READ and SERIALIZABLE.

    Let me to describe each isolation level.

    READ UNCOMMITTED

    When it's used, SQL Server not issue shared locks while reading data. So, you can read an uncommitted transaction that might get rolled back later. This isolation level is also called dirty read. This is the lowest isolation level. It ensures only that a physically corrupt data will not be read.

    READ COMMITTED

    This is the default isolation level in SQL Server. When it's used, SQL Server will use shared locks while reading data. It ensures that a physically corrupt data will not be read and will never read data that another application has changed and not yet committed, but it not ensures that the data will not be changed before the end of the transaction.

    REPEATABLE READ

    When it's used, the dirty reads and nonrepeatable reads cannot occur. It means that locks will be placed on all data that is used in a query, and another transactions cannot update the data.

    This is the definition of nonrepeatable read from SQL Server Books Online:

    nonrepeatable read When a transaction reads the same row more than one time, and between the two (or more) reads, a separate transaction modifies that row. Because the row was modified between reads within the same transaction, each read produces different values, which introduces inconsistency. 
     

    SERIALIZABLE

    Most restrictive isolation level. When it's used, the phantom values cannot occur. It prevents other users from updating or inserting rows into the data set until the transaction will be completed.

    This is the definition of phantom from SQL Server Books Online:

    phantom Phantom behavior occurs when a transaction attempts to select a row that does not exist and a second transaction inserts the row before the first transaction finishes. If the row is inserted, the row appears as a phantom to the first transaction, inconsistently appearing and disappearing. 
    You can set the appropriate isolation level for an entire SQL Server session by using the SET TRANSACTION ISOLATION LEVEL statement.
    This is the syntax from SQL Server Books Online:

    SET TRANSACTION ISOLATION LEVEL      {         READ COMMITTED          | READ UNCOMMITTED          | REPEATABLE READ          | SERIALIZABLE     } 
    You can use the DBCC USEROPTIONS statement to determine the Transaction Isolation Level currently set. This command returns the set options that are active for the current connection. This is the example:

    SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED GO DBCC USEROPTIONS GO 
     

    Lock types

    There are three main types of locks that SQL Server 7.0/2000 uses:

  • Shared locks
  • Update locks
  • Exclusive locks

    Shared locks are used for operations that do not change or update data, such as a SELECT statement.

    Update locks are used when SQL Server intends to modify a page, and later promotes the update page lock to an exclusive page lock before actually making the changes.

    Exclusive locks are used for the data modification operations, such as UPDATE, INSERT, or DELETE.

    Shared locks are compatible with other Shared locks or Update locks.

    Update locks are compatible with Shared locks only.

    Exclusive locks are not compatible with other lock types.

    Let me to describe it on the real example. There are four processes, which attempt to lock the same page of the same table. These processes start one after another, so Process1 is the first process, Process2 is the second process and so on.

    Process1 : SELECT
    Process2 : SELECT
    Process3 : UPDATE
    Process4 : SELECT

    Process1 sets the Shared lock on the page, because there are no another locks on this page.
    Process2 sets the Shared lock on the page, because Shared locks are compatible with other Shared locks.
    Process3 wants to modify data and wants to set Exclusive lock, but it cannot make it before Process1 and Process2 will be finished, because Exclusive lock is not compatible with other lock types. So, Process3 sets Update lock.
    Process4 cannot set Shared lock on the page before Process3 will be finished. So, there is no Lock starvation. Lock starvation occurs when read transactions can monopolize a table or page, forcing a write transaction to wait indefinitely. So, Process4 waits before Process3 will be finished.
    After Process1 and Process2 were finished, Process3 transfer Update lock into Exclusive lock to modify data. After Process3 was finished, Process4 sets the Shared lock on the page to select data.

    Locking optimizer hints

    SQL Server 7.0/2000 supports the following Locking optimizer hints:

  • NOLOCK
  • HOLDLOCK
  • UPDLOCK
  • TABLOCK
  • PAGLOCK
  • TABLOCKX
  • READCOMMITTED
  • READUNCOMMITTED
  • REPEATABLEREAD
  • SERIALIZABLE
  • READPAST
  • ROWLOCK

    NOLOCK is also known as "dirty reads". This option directs SQL Server not to issue shared locks and not to honor exclusive locks. So, if this option is specified, it is possible to read an uncommitted transaction. This results in higher concurrency and in lower consistency.

    HOLDLOCK directs SQL Server to hold a shared lock until completion of the transaction in which HOLDLOCK is used. You cannot use HOLDLOCK in a SELECT statement that includes the FOR BROWSE option. HOLDLOCK is equivalent to SERIALIZABLE.

    UPDLOCK instructs SQL Server to use update locks instead of shared locks while reading a table and holds them until the end of the command or transaction.

    TABLOCK takes a shared lock on the table that is held until the end of the command. If you also specify HOLDLOCK, the lock is held until the end of the transaction.

    PAGLOCK is used by default. Directs SQL Server to use shared page locks.

    TABLOCKX takes an exclusive lock on the table that is held until the end of the command or transaction.

    READCOMMITTED
    Perform a scan with the same locking semantics as a transaction running at the READ COMMITTED isolation level. By default, SQL Server operates at this isolation level.

    READUNCOMMITTED
    Equivalent to NOLOCK.

    REPEATABLEREAD
    Perform a scan with the same locking semantics as a transaction running at the REPEATABLE READ isolation level.

    SERIALIZABLE
    Perform a scan with the same locking semantics as a transaction running at the SERIALIZABLE isolation level. Equivalent to HOLDLOCK.

    READPAST
    Skip locked rows. This option causes a transaction to skip over rows locked by other transactions that would ordinarily appear in the result set, rather than block the transaction waiting for the other transactions to release their locks on these rows. The READPAST lock hint applies only to transactions operating at READ COMMITTED isolation and will read only past row-level locks. Applies only to the SELECT statement.
    You can only specify the READPAST lock in the READ COMMITTED or REPEATABLE READ isolation levels.

    ROWLOCK
    Use row-level locks rather than use the coarser-grained page- and table-level locks.

    You can specify one of these locking options in a SELECT statement.
    This is the example:

    SELECT au_fname FROM pubs..authors (holdlock)

    Deadlocks

    Deadlock occurs when two users have locks on separate objects and each user wants a lock on the other's object. For example, User1 has a lock on object "A" and wants a lock on object "B" and User2 has a lock on object "B" and wants a lock on object "A". In this case, SQL Server ends a deadlock by choosing the user, who will be a deadlock victim. After that, SQL Server rolls back the breaking user's transaction, sends message number 1205 to notify the user's application about breaking, and then allows the nonbreaking user's process to continue.

    You can decide which connection will be the candidate for deadlock victim by using SET DEADLOCK_PRIORITY. In other case, SQL Server selects the deadlock victim by choosing the process that completes the circular chain of locks.

    So, in a multiuser situation, your application should check the error 1205 to indicate that the transaction was rolled back, and if it's so, restart the transaction.

    Note. To reduce the chance of a deadlock, you should minimize the size of transactions and transaction times.

    View locks (sp_lock)

    Sometimes you need a reference to information about locks. Microsoft recommends using the sp_lock system stored procedure to report locks information. This very useful procedure returns the information about SQL Server process ID, which lock the data, about locked database, about locked table ID, about locked page and about type of locking (locktype column).

    This is the example of using the sp_lock system stored procedure:

    spid   locktype                            table_id    page        dbname ------ ----------------------------------- ----------- ----------- --------------- 11     Sh_intent                           688005482   0           master 11     Ex_extent                           0           336         tempdb 
    The information, returned by sp_lock system stored procedure needs in some clarification, because it's difficult to understand database name, object name and index name by their ID numbers.

    Check the link below if you need to get user name, host name, database name, index name object name and object owner instead of their ID numbers:
    Detailed locking view: sp_lock2

    Literature

    1. SQL Server Books Online

    2. Transaction Isolation Level

    3. Locking in SQL Server 6.5

    4. Detailed locking view: sp_lock2

    5. INF: Analyzing and Avoiding Deadlocks in SQL Server
  •  


    Article By:

    Alexander Chigrik
    chigrik@mssqlcity.com

    http://www.mssqlcity.com/Articles/Adm/SQL70Locks.htm




    Thursday, July 17, 2008

    DIV Rounded Corners

    CSS

    <style type="text/css">
    .spiffy{display:block}
    .spiffy *{
    display:block;
    height:1px;
    overflow:hidden;
    font-size:.01em;
    background:#B3A400}
    .spiffy1{
    margin-left:3px;
    margin-right:3px;
    padding-left:1px;
    padding-right:1px;
    border-left:1px solid #ded791;
    border-right:1px solid #ded791;
    background:#c6ba3f}
    .spiffy2{
    margin-left:1px;
    margin-right:1px;
    padding-right:1px;
    padding-left:1px;
    border-left:1px solid #f7f5e5;
    border-right:1px solid #f7f5e5;
    background:#c1b530}
    .spiffy3{
    margin-left:1px;
    margin-right:1px;
    border-left:1px solid #c1b530;
    border-right:1px solid #c1b530;}
    .spiffy4{
    border-left:1px solid #ded791;
    border-right:1px solid #ded791}
    .spiffy5{
    border-left:1px solid #c6ba3f;
    border-right:1px solid #c6ba3f}
    .spiffyfg{
    background:#B3A400}
    </style>

    HTML

    <div>
    <b class="spiffy">
    <b class="spiffy1"><b></b></b>
    <b class="spiffy2"><b></b></b>
    <b class="spiffy3"></b>
    <b class="spiffy4"></b>
    <b class="spiffy5"></b></b>

    <div class="spiffyfg">
    <!-- content goes here -->
    </div>

    <b class="spiffy">
    <b class="spiffy5"></b>
    <b class="spiffy4"></b>
    <b class="spiffy3"></b>
    <b class="spiffy2"><b></b></b>
    <b class="spiffy1"><b></b></b></b>
    </div>

    Wednesday, July 16, 2008

    Convert DOC to PDF Files (Convert Word To PDF Files) using Command in ASP.Net

    I used http://www.softinterface.com/Convert-Doc/Features/Convert-DOC-to-PDF.htm and then Executed the command from Asp.Net to conver the MS Word files to PDF.

    Code

            // write the command
            string exec = TextBox1.Text;

            // Create the ProcessInfo object
            System.Diagnostics.ProcessStartInfo psi = new System.Diagnostics.ProcessStartInfo("cmd.exe");
            psi.UseShellExecute = false;
            psi.RedirectStandardOutput = true;
            psi.RedirectStandardInput = true;
            psi.RedirectStandardError = true;
           
            // The path where Application is installed
            psi.WorkingDirectory = "C:\\Program Files\\Softinterface, Inc\\Convert Doc";


            // Start the process
            System.Diagnostics.Process proc = System.Diagnostics.Process.Start(psi);

            // Open the batch file for reading

            //System.IO.StreamReader strm = System.IO.File.OpenText(strFilePath);
            System.IO.StreamReader strm = proc.StandardError;
            // Attach the output for reading
            System.IO.StreamReader sOut = proc.StandardOutput;
            // Attach the in for writing
            System.IO.StreamWriter sIn = proc.StandardInput;

            sIn.WriteLine(exec);

            strm.Close();

            sIn.WriteLine("EXIT");

            // Close the process
            proc.Close();

            // Close the io Streams;
            sIn.Close();
            sOut.Close();

    ASPX File

                 <div>
            <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
            <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Button" /></div>


    Commands ( Ref: http://www.softinterface.com/ )

    Converting a Single File

    To convert a single file, say D:\MyFolder\Doc1.DOC to C:\Results Folder\Doc1.PDF use the following syntax:

       ConvertDoc /S "D:\MyFolder\Doc1.DOC" /T "C:\Results Folder\Doc1.PDF" /F9 /C12 /M2 /V

    • The /S and /T switches above specify Source (input) and Target (output) path respectively and are both required when converting a single file.  It is always a good idea to use double quotes around the path especially if there are space characters within the path.

    • The /M2 switch tells 'Convert Doc' to use the 'Convert Doc' method (it is one of 3 different possible Conversion Methods). 

    • /F9 is the original (input) file type, which in this case is a word DOC file.  Looking up the file types within the File Type Constants Specification for the 'Convert Doc' method will show that the numeric value of 9 corresponds to a DOC file.

    • /C12 is the target (output) file type, which in this case is a PDF file.  Looking up the file types within the File Type Constants Specification for the 'Convert Doc' method will show that the numeric value of 12 corresponds to a PDF file.

    • Finally, the /V (for Verbose) switch is used to give instant feedback by having the program report the status of the conversion with a message box.  You can remove this once you have perfected your command line specification.  You can also (or instead of /V) create a Log file that will contain the results of the conversion by using the /L switch.

    • HINT: You may use the /W switch to specify a File Open password.  The Example below makes the word Apples the password to open the newly created PDF file:

           ConvertDoc.EXE     /S "c:\input files\tryme.doc" /T "c:\input files\tryme.pdf" /F9 /C12 /M2 /V /WApples

    Note: It is highly encouraged that you use the Verbose (/V) switch initially to see what the status of your conversion is and to help you perfect your command line.  When in verbose mode the program will tell you what went wrong or right with your command line using message boxes.

    Numeric TextBox (ASP.Net / HTML Input)

    <
    script language="javascript">

    function KeyCheck(e)

    {

    var KeyID = (window.event) ? event.keyCode : e.which;

    if((KeyID >= 65 && KeyID <= 90) || (KeyID >= 97 && KeyID <= 122) || (KeyID >= 33 && KeyID <= 47) ||

    (KeyID >= 58 && KeyID <= 64) || (KeyID >= 91 && KeyID <= 96) || (KeyID >= 123 && KeyID <= 126))

    {

    return false;

    }

    return true;

    }

    </
    script>
    HTML Text Box

    <

    input type="text" ID="txt_TextBox" onkeypress="return KeyCheck(event);" runat="server" style="width: 28px" />

    ASP.Net TextBox

    <asp:TextBox ID="txt_ASPTextBox" runat="server" ></asp:TextBox>

    Code Behind for Asp.Net TextBox

    txt_ASPTextBox.Attributes.Add("onkeypress","return KeyCheck(event);");

    Insert / Delete / Update Row in DataGrid at Runtime from Other GridView

    Grid 1
    <asp:GridView ID="gv_Source" runat="server" AutoGenerateColumns="False" OnRowCommand="gv_Source_RowCommand" Width="100%">

    <Columns>

    <asp:BoundField DataField="TemplateName" HeaderText="Template Name" />

    <asp:TemplateField>

    <ItemTemplate>

    <input type="text" ID="txt_TextBox" runat="server" style="width: 28px" />

    <asp:Label ID="lbl_Required" runat="server" Font-Bold="True" ForeColor="Red" Text="*"

    Visible="False"></asp:Label>

    </ItemTemplate>

    </asp:TemplateField>

    <asp:TemplateField>

    <ItemTemplate>

    <asp:LinkButton ID="lnkbt_SelectTemplates" runat="server" Text="Select" CommandArgument='<%# ((GridViewRow)Container).RowIndex %>' ValidationGroup="SelectGroup"></asp:LinkButton>

    </ItemTemplate>

    </asp:TemplateField>

    </Columns>

    </asp:GridView>

    Grid2
    <asp:GridView ID="gv_Selected" runat="server" AutoGenerateColumns="False" HorizontalAlign="Left" Width="100%" OnRowCommand="gv_Selected_RowCommand">

    <Columns>

    <asp:BoundField DataField="Column1" HeaderText="Column Name" />

    <asp:BoundField DataField="Column2" HeaderText="Column2 Nam" />

    <asp:TemplateField>

    <ItemTemplate>

    <asp:LinkButton ID="lnkbt_Delete" runat="server" Text="Remove" CommandArgument='<%# ((GridViewRow)Container).RowIndex %>' CausesValidation="False" OnClientClick="return confirm('Are you sure?');"></asp:LinkButton>

    </ItemTemplate></asp:TemplateField>

    </Columns>

    </asp:GridView>
    CODE BEHIND
    Grid 1 RowCommand (Add to Grid 2)

    protected void gv_Source_RowCommand(object sender, GridViewCommandEventArgs e)

    {

    GridViewRow gv_row = gv_Source.Rows[int.Parse(e.CommandArgument.ToString())];

    DataTable dt = new DataTable("Selected");

    dt.Columns.Add(

    "Column1");

    dt.Columns.Add(

    "Column2");

    dt.AcceptChanges();

    DataRow dr = null;

    //Check if the grid has already some rows

    if (gv_Selected.Rows.Count > 0)

    {

    foreach (GridViewRow gr in gv_Selected.Rows)

    {

    dr = dt.NewRow();

    dr[0] = gr.Cells[0].Text;

    dr[1] = gr.Cells[1].Text;

    dt.Rows.Add(dr);

    dt.AcceptChanges();

    }

    }

    if (((HtmlInputText)gv_row.FindControl("txt_TextBox")).Value.Length != 0)

    {

    dr = dt.NewRow();

    dr[0] = gv_row.Cells[0].Text;

    dr[1] = ((

    HtmlInputText)gv_row.FindControl("txt_TextBox")).Value;

    dt.Rows.Add(dr);

    dt.AcceptChanges();

    Label lbl_temp = (Label)gv_row.FindControl("lbl_Required");

    lbl_temp.Visible =

    false;

    }

    else

    {

    Label lbl_temp = (Label)gv_row.FindControl("lbl_Required");

    lbl_temp.Visible =

    true;

    }

    gv_Selected.DataSource = dt;

    gv_Selected.DataBind();

    }

    Grid 2 RowCommand (Delete Row)

    protected void gv_Selected_RowCommand(object sender, GridViewCommandEventArgs e)

    {

    DataTable dt = new DataTable("Selected");

    dt.Columns.Add(

    "Column1");

    dt.Columns.Add(

    "Column2");

    dt.AcceptChanges();

    DataRow dr = null;

    if (gv_Selected.Rows.Count > 0)

    {

    for (int i = 0; i < gv_Selected.Rows.Count;i++)

    {

    GridViewRow gr = gv_SelectedTemplates.Rows[i];

    if (i.ToString() != e.CommandArgument.ToString())

    {

    dr = dt.NewRow();

    dr[0] = gr.Cells[0].Text;

    dr[1] = gr.Cells[1].Text;

    dt.Rows.Add(dr);

    dt.AcceptChanges();

    }

    }

    }

    gv_SelectedTemplates.DataSource = dt;

    gv_SelectedTemplates.DataBind();

    }

    Monday, July 14, 2008

    GridView (Multiple check Boxes Selection / Get Checked Check Boxes)

    Grid View

    <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False">

    <Columns>

    <asp:BoundField DataField="Column1" HeaderText="Column" />

    <asp:TemplateField HeaderText="TextBox">

    <ItemTemplate>

    <asp:TextBox ID="txt_TextBox" runat="server"></asp:TextBox>

    </ItemTemplate>

    </asp:TemplateField>

    <asp:TemplateField HeaderText="Select">

    <HeaderTemplate>

    Select All

    <input id="chkAll" onclick="javascript:SelectAllCheckboxes(this);" runat="server"

    type="checkbox" />

    </HeaderTemplate>

    <ItemTemplate>

    <asp:CheckBox ID="chk_Selected" runat="server" />

    </ItemTemplate>

    </asp:TemplateField>

    </Columns>

    </asp:GridView>

    JavaScript

    <

    script language="javascript">

    function SelectAllCheckboxes(spanChk){

    var oItem = spanChk.children;

    var theBox= (spanChk.type=="checkbox") ?

    spanChk : spanChk.children.item[0];

    xState=theBox.checked;

    elm=theBox.form.elements;

    for(i=0;i<elm.length;i++)

    if(elm[i].type=="checkbox" &&

    elm[i].id!=theBox.id)

    {

    if(elm[i].checked!=xState)

    elm[i].click();

    }

    }

    </

    script>

    Code Behind

    string str_Result = "";

    for (int i = 0; i < gv_SearchedDocuments.Rows.Count; i++)

    {

    GridViewRow gv_row = GridView1.Rows[i];

    bool isChecked = ((CheckBox)gv_row.FindControl("chk_Selected")).Checked;

    if (isChecked)

    {

    str_Result += GridView1.Rows[i].Cells[0].Text;

    TextBox txt_Temp = ((TextBox)gv_row.FindControl("txt_ViewOrder"));

    str_Result +=

    txt_Temp.Text+"<,br/>";

    }

    }

    lbl_Message.Text = str_Result ;

    Tuesday, July 8, 2008

    Exception Handling (Exception logging)

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Diagnostics;

    namespace ExceptionLogger
    {
    public class ExceptionLogger
    {
    public static void WritetoCustomEventLog(Exception paramEntry, EventLogEntryType paramType)
    {
    string str_Source = "ERROR LOG"; //Source Name
    string str_LogType = "My Test LOG"; //Your log Name if it does not exists then first it will create and then add entry
    string str_Machine = "."; //machine name

    if (!System.Diagnostics.EventLog.SourceExists(str_Source, str_Machine))
    {
    System.Diagnostics.EventLog.CreateEventSource(str_Source, str_LogType, str_Machine);
    }
    string ExceptionDetail = "";
    ExceptionDetail = "Message : " + paramEntry.Message + "\n";
    ExceptionDetail += "Stack Trace : " + paramEntry.StackTrace + "\n";
    ExceptionDetail += "Source : " + paramEntry.Source + "\n";
    ExceptionDetail += "Target Site:" + paramEntry.TargetSite + "\n";
    ExceptionDetail += "Inner Exception:" + paramEntry.InnerException + "\n";
    ExceptionDetail += "Help Link:" + paramEntry.HelpLink + "\n";
    EventLog CustomLog = new EventLog(str_LogType, str_Machine, str_Source);

    CustomLog.WriteEntry(ExceptionDetail, paramType);

    }
    }
    }

    Monday, July 7, 2008

    Access Master Pages Properties

    There are different techniques to access MasterPages properties

    Master Page
    Code file:

    public partial class MyMasterPageClass: System.Web.UI.MasterPage
    {
    public string ErrorMessage
    {
    set
    {
    lblErrorPane.Text = value;
    panelErrorMessage.Visible = true;
    }
    }
    }


    Content Page

    1) ((MyMasterPageClass)Master).ErrorMessage = " Test Error Message";


    After adding this <%@ MasterType VirtualPath="~/MasterPage.master" %> under Page Directive you can access the property of master page directly.

    2) this.Master.ErrorMessage = "Test Message";

    Friday, July 4, 2008

    Unique String Generator

    using System;
    using System.Security.Cryptography;

    public class UniqueStringGenerator
    {
    private static int DEFAULT_MIN_STRING_LENGTH = 20;
    private static int DEFAULT_MAX_STRING_LENGTH = 20;


    private static string STRING_CHARS_LCASE = "abcdefgijkmnopqrstwxyz";
    private static string STRING_CHARS_UCASE = "ABCDEFGHJKLMNPQRSTWXYZ";
    private static string STRING_CHARS_NUMERIC = "23456789";
    private static string STRING_CHARS_SPECIAL = "*$-+?_&=!%{}/";

    public static string Generate()
    {
    return Generate(DEFAULT_MIN_STRING_LENGTH,
    DEFAULT_MAX_STRING_LENGTH);
    }

    public static string Generate(int length)
    {
    return Generate(length, length);
    }

    public static string GenerateWithSpecialCharacters()
    {
    return GenerateWithSpecialCharacters(DEFAULT_MIN_STRING_LENGTH,
    DEFAULT_MAX_STRING_LENGTH);
    }

    public static string GenerateWithSpecialCharacters(int length)
    {
    return GenerateWithSpecialCharacters(length, length);
    }

    public static string GenerateWithSpecialCharacters(int minLength,
    int maxLength)
    {

    if (minLength <= 0 || maxLength <= 0 || minLength > maxLength)
    return null;

    char[][] charGroups = new char[][]
    {
    STRING_CHARS_LCASE.ToCharArray(),
    STRING_CHARS_UCASE.ToCharArray(),
    STRING_CHARS_NUMERIC.ToCharArray(),
    STRING_CHARS_SPECIAL.ToCharArray()
    };

    int[] charsLeftInGroup = new int[charGroups.Length];

    for (int i = 0; i < charsLeftInGroup.Length; i++)
    charsLeftInGroup[i] = charGroups[i].Length;

    int[] leftGroupsOrder = new int[charGroups.Length];

    for (int i = 0; i < leftGroupsOrder.Length; i++)
    leftGroupsOrder[i] = i;

    byte[] randomBytes = new byte[4];

    RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
    rng.GetBytes(randomBytes);

    int seed = (randomBytes[0] & 0x7f) << 24 |
    randomBytes[1] << 16 |
    randomBytes[2] << 8 |
    randomBytes[3];

    Random random = new Random(seed);

    char[] UniqueString = null;

    if (minLength < maxLength)
    UniqueString = new char[random.Next(minLength, maxLength + 1)];
    else
    UniqueString = new char[minLength];

    int nextCharIdx;

    int nextGroupIdx;

    int nextLeftGroupsOrderIdx;

    int lastCharIdx;

    int lastLeftGroupsOrderIdx = leftGroupsOrder.Length - 1;

    for (int i = 0; i < UniqueString.Length; i++)
    {

    if (lastLeftGroupsOrderIdx == 0)
    nextLeftGroupsOrderIdx = 0;
    else
    nextLeftGroupsOrderIdx = random.Next(0,
    lastLeftGroupsOrderIdx);


    nextGroupIdx = leftGroupsOrder[nextLeftGroupsOrderIdx];


    lastCharIdx = charsLeftInGroup[nextGroupIdx] - 1;


    if (lastCharIdx == 0)
    nextCharIdx = 0;
    else
    nextCharIdx = random.Next(0, lastCharIdx + 1);


    UniqueString[i] = charGroups[nextGroupIdx][nextCharIdx];


    if (lastCharIdx == 0)
    charsLeftInGroup[nextGroupIdx] =
    charGroups[nextGroupIdx].Length;

    else
    {

    if (lastCharIdx != nextCharIdx)
    {
    char temp = charGroups[nextGroupIdx][lastCharIdx];
    charGroups[nextGroupIdx][lastCharIdx] =
    charGroups[nextGroupIdx][nextCharIdx];
    charGroups[nextGroupIdx][nextCharIdx] = temp;
    }

    charsLeftInGroup[nextGroupIdx]--;
    }

    if (lastLeftGroupsOrderIdx == 0)
    lastLeftGroupsOrderIdx = leftGroupsOrder.Length - 1;

    else
    {

    if (lastLeftGroupsOrderIdx != nextLeftGroupsOrderIdx)
    {
    int temp = leftGroupsOrder[lastLeftGroupsOrderIdx];
    leftGroupsOrder[lastLeftGroupsOrderIdx] =
    leftGroupsOrder[nextLeftGroupsOrderIdx];
    leftGroupsOrder[nextLeftGroupsOrderIdx] = temp;
    }
    lastLeftGroupsOrderIdx--;
    }
    }

    return new string(UniqueString);
    }

    public static string Generate(int minLength,
    int maxLength)
    {

    if (minLength <= 0 || maxLength <= 0 || minLength > maxLength)
    return null;

    char[][] charGroups = new char[][]
    {
    STRING_CHARS_LCASE.ToCharArray(),
    STRING_CHARS_UCASE.ToCharArray(),
    STRING_CHARS_NUMERIC.ToCharArray(),
    };

    int[] charsLeftInGroup = new int[charGroups.Length];

    for (int i = 0; i < charsLeftInGroup.Length; i++)
    charsLeftInGroup[i] = charGroups[i].Length;

    int[] leftGroupsOrder = new int[charGroups.Length];

    for (int i = 0; i < leftGroupsOrder.Length; i++)
    leftGroupsOrder[i] = i;

    byte[] randomBytes = new byte[4];

    RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
    rng.GetBytes(randomBytes);

    int seed = (randomBytes[0] & 0x7f) << 24 |
    randomBytes[1] << 16 |
    randomBytes[2] << 8 |
    randomBytes[3];

    Random random = new Random(seed);

    char[] UniqueString = null;

    if (minLength < maxLength)
    UniqueString = new char[random.Next(minLength, maxLength + 1)];
    else
    UniqueString = new char[minLength];

    int nextCharIdx;

    int nextGroupIdx;

    int nextLeftGroupsOrderIdx;

    int lastCharIdx;

    int lastLeftGroupsOrderIdx = leftGroupsOrder.Length - 1;

    for (int i = 0; i < UniqueString.Length; i++)
    {

    if (lastLeftGroupsOrderIdx == 0)
    nextLeftGroupsOrderIdx = 0;
    else
    nextLeftGroupsOrderIdx = random.Next(0,
    lastLeftGroupsOrderIdx);


    nextGroupIdx = leftGroupsOrder[nextLeftGroupsOrderIdx];


    lastCharIdx = charsLeftInGroup[nextGroupIdx] - 1;


    if (lastCharIdx == 0)
    nextCharIdx = 0;
    else
    nextCharIdx = random.Next(0, lastCharIdx + 1);


    UniqueString[i] = charGroups[nextGroupIdx][nextCharIdx];


    if (lastCharIdx == 0)
    charsLeftInGroup[nextGroupIdx] =
    charGroups[nextGroupIdx].Length;

    else
    {

    if (lastCharIdx != nextCharIdx)
    {
    char temp = charGroups[nextGroupIdx][lastCharIdx];
    charGroups[nextGroupIdx][lastCharIdx] =
    charGroups[nextGroupIdx][nextCharIdx];
    charGroups[nextGroupIdx][nextCharIdx] = temp;
    }

    charsLeftInGroup[nextGroupIdx]--;
    }

    if (lastLeftGroupsOrderIdx == 0)
    lastLeftGroupsOrderIdx = leftGroupsOrder.Length - 1;

    else
    {

    if (lastLeftGroupsOrderIdx != nextLeftGroupsOrderIdx)
    {
    int temp = leftGroupsOrder[lastLeftGroupsOrderIdx];
    leftGroupsOrder[lastLeftGroupsOrderIdx] =
    leftGroupsOrder[nextLeftGroupsOrderIdx];
    leftGroupsOrder[nextLeftGroupsOrderIdx] = temp;
    }
    lastLeftGroupsOrderIdx--;
    }
    }

    return new string(UniqueString);
    }
    }





    There are two functions

    * Generate
    o This function returns the string with no special characters
    * GenerateWithSpecialCharacters
    o This function returns the string with special characters included
    * Example Code
    o string temp = UniqueStringGenerator.GenerateWithSpecialCharacters(20,20);
    o string temp = UniqueStringGenerator.GenerateWithSpecialCharacters(20);
    o string temp = UniqueStringGenerator.GenerateWithSpecialCharacters();
    o .......................
    o string temp = UniqueStringGenerator.Generate(20,20);
    o string temp = UniqueStringGenerator.Generate(20);
    o string temp = UniqueStringGenerator.Generate();