Development

One Click CheckBox in a Silverlight DataGrid

by JBrooks 16. December 2011 09:54

I found this code on the internet to create a CheckBox in a DataGrid that wouldn’t require more than one click to change.  But it didn’t work for me.

<sdk:DataGridTemplateColumn>
    <sdk:DataGridTemplateColumn.CellEditingTemplate>
        <DataTemplate>
            <CheckBox IsThreeState="False" IsChecked="{Binding Path=IsActive, Mode=TwoWay}" 
                      HorizontalAlignment="Center" VerticalAlignment="Center" />
        </DataTemplate>
    </sdk:DataGridTemplateColumn.CellEditingTemplate>
</sdk:DataGridTemplateColumn>
The reason it didn’t work is because I also had this as part of my DataGrid
<sdk:DataGrid CurrentCellChanged="dgTotals_CurrentCellChanged" …

 

That method had a dgTotals.BeginEdit(); in it to allow the user to begin editing the DataGrid cells without having to first click on them.  The simple solution was to just skip that for my CheckBox column.

if(dgTotals.CurrentColumn != null && dgTotals.CurrentColumn.DisplayIndex != 1)
    dgTotals.BeginEdit();

Tags:

Development | Silverlight

Troubleshooting the Silverlight to RIA Services Connection

by JBrooks 6. December 2011 11:41

The first thing I do when troubleshooting a Silverlight startup error (like where it just has the startup progress bar sitting at 100% forever) is to make sure I can reach the RIA Service and it doesn’t return an error.

This example shows how to decode the URL back to the service:

http://localhost:1893/MWEntry5-Web-Services-ScheduleDomainService.svc

This is just the path delimited with dashes to the service as seen in your project and also with all of the periods changed to dashes and then just add “.svc” at the end.

clip_image001

You should see a page for the RIA Service if you put this URL in a browser.

When calling a method it is ClientBin first, followed by the name space, followed by “binary” and then your method name.  Here are 2 examples:

http://localhost:1893/ClientBin/MWEntry5-Web-Services-ScheduleDomainService.svc/binary/GetSchedulableEntities

http://MyDomain/MyWebApp/ClientBin/MyWebApp-Web-AuthenticationService.svc/binary/GetUser

I just needed to put this somewhere so I can reference it in the future.

Tags:

Silverlight | Development

.NET and Silverlight Rounding

by jbrooks 10. November 2011 09:09
In Excel 34.5 will round to 35 and in .NET it will round to 34. This is because .NET defaults to using the MidpointRounding.ToEven mode – also called banker’s rounding.  In .NET you have the option of overriding this by passing in the other MidpointRounding like:
 
    Math.Round(34.5, MidpointRounding.AwayFromZero);   // Now rounds to 35

A little annoying since in all of the applications that I’ve ever written the users expect rounding to match Excel’s and I’ve never needed the default. Worse, in Silverlight you don’t even have the option to change the default when using Math.Round().

So I had to write my own extension that can be used like:

 
    double myValue = 34.5;
    
    int myRoundedValue = (int) myValue.RoundCorrect(0);  // rounds to 35
 

Here is the extension code:

public static class DoubleExtensions
{
    public static double RoundCorrect(this double d, int decimals)
    {
        double multiplier = Math.Pow(10, decimals);
 
        if (d < 0)
            multiplier *= -1;
                                   
        return Math.Floor((d * multiplier) + 0.5) / multiplier;
    }
}

or if you want the code for a normal method it would be

public double RoundCorrect(double d, int decimals)
{
    double multiplier = Math.Pow(10, decimals);
 
    if (d < 0)
        multiplier *= -1;
 
    return Math.Floor((d * multiplier) + 0.5) / multiplier;
 
}

Tags:

Development | Silverlight

MSBuild with Silverlight / RIA Services

by jbrooks 2. November 2011 09:36

I introduced a Silverlight project to my ASP.Net application and every thing worked fine in development but when I went to build it with out automatic build process MSBuild would kick out the following error:

[msbuild]   App.xaml.cs(2,17): error CS0234: The type or namespace name 'Services' does not exist in the namespace 'MyProject.Web' (are you missing an assembly reference?)

Had to change a registration key, the DefaultToolsVersion was set to 2.0, changed  it to 4.0

image

 

Ran the install selecting repair for the Silverlight 5.0 sdk (silverlight5_sdk.exe) – it was originally installed as part of the tool kit, but found others saying the real install still needed to be done to get it to work with MSBuild.

Next go the error:  error MSB4044: The "CreateRiaClientFilesTask" task was not given a value for the required parameter "ClientFrameworkPath"

Had to install EntityFramWork4.1 on my build server and then reboot.

Now on to the next problem.

Tags:

Silverlight | Development

Binding a Silverlight DataGrid’s ComboBox

by JBrooks 6. May 2011 07:13

Some things in Silverlight seem harder than they should be.  Binding a list of strings to a combobox is one such thing.  This involves the following steps to do a simple bind.

1. Have a private list of the strings in your page code.

 
private List<string> FuelList = new List<string>()
         {
             "OIL",
             "GAS",
             "COAL"
         };
 

2. In your initialization method add the list as a resource so your XAML can use it.  This must be done before the InitializeComponent(); call.

 
        public ucMyUserControl()
        {
            this.Resources.Add("FuelList", FuelList);
 
            InitializeComponent();
        }

 

3. Then you can reference the list as the source to you combo box.

<sdk:DataGrid AutoGenerateColumns="False"  MinHeight="130" Name="dgMyDataGrid" 
    ItemsSource="{Binding Path=ScheduleRows, Mode=TwoWay}"  
        CurrentCellChanged="dgMyDataGrid_CurrentCellChanged">
 
 
  <sdk:DataGrid.Columns>
     <sdk:DataGridTemplateColumn  Header="Fuel Type">
         <sdk:DataGridTemplateColumn.CellTemplate>
             <DataTemplate>
                  <TextBlock Margin="2" VerticalAlignment="Center"  
            HorizontalAlignment="Left" Text="{Binding FUEL_TYPE}" Width="120" />
             </DataTemplate>
             </sdk:DataGridTemplateColumn.CellTemplate>
                  <sdk:DataGridTemplateColumn.CellEditingTemplate>
                     <DataTemplate>
                         <ComboBox Height="23" Name="cbxFuelType" 
                                 ItemsSource="{StaticResource FuelList}"
                                 SelectedValue="{Binding Path=FUEL_TYPE, Mode=TwoWay}" Width="120">
 
                         </ComboBox>
                      </DataTemplate>
             </sdk:DataGridTemplateColumn.CellEditingTemplate>
        </sdk:DataGridTemplateColumn>
...

4. One last item, notice the datagrid’s current cell changed event is wired up to start editing without requiring the user to click first.  That method is simple enough:

private void dgMyDataGrid_CurrentCellChanged(object sender, EventArgs e)
  {
      dgMyDataGrid.BeginEdit();
  }

 

 

That gives me my datagrid with my combo box:

image

 

 

Here is a sample solution that has the full code: 

 ComboBoxTest.zip (1,016.17 kb)

Tags:

Development | Silverlight

Having the Result Set of a Stored Proc Sent to You by RSS Feed.

by JBrooks 14. December 2010 12:44

I wanted to monitor one of my system from my desk top and from my phone.  I found a simple solution whereby I can subscribe to the result set of a stored proc by using RSS. So I can have this feed MS Outlook, my phone or my web browser.

First, Visual Studio 2010 makes creating an RSS feed a simple matter that is about 1 page of code.

I simply add an ASPX page to my project and remove most of the markup so it only has 2 lines:

 
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="rss.aspx.cs"  Inherits="RSS.rss"  %>
 
<%@ OutputCache Duration="60" VaryByParam="none" %>

Next the code behind simply calls the stored proc placing the results into a table and then loading up some of the RSS related collections VS2010 gives you. 

 
using System;
using System.Data;
using System.ServiceModel.Syndication;
using System.Web;
using System.Collections.Generic;
using System.Xml;
 
namespace RSS
{
public partial class rss : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
 
        string id = Request.QueryString["id"];
 
        // I don't want just anyone to subscribe, so you have to know the GUID.
        if (id== null || id != "23F14EA1-1B20-443B-9B94-92C4EA4A8099")
            throw new Exception("Guid not reconized");
 
 
        Response.ContentType = "application/atom+xml";
 
        // this gets the data from the database and populates a table.
        DataTable dt = cDB.getFeed();
        SyndicationFeed myFeed = new SyndicationFeed();
 
        myFeed.Title = TextSyndicationContent.CreatePlaintextContent("SampleApp Activity");
        myFeed.Description = TextSyndicationContent
            .CreatePlaintextContent(@"A syndication of the most recently 
                    SampleApp activity including exceptions.");
        myFeed.Links.Add(SyndicationLink.CreateAlternateLink(
            new Uri(GetFullyQualifiedUrl("/rss.aspx"))));
        myFeed.Links.Add(SyndicationLink.CreateSelfLink(
            new Uri(GetFullyQualifiedUrl(Request.RawUrl))));
        myFeed.Copyright = TextSyndicationContent
                        .CreatePlaintextContent("Copyright SampleApp");
        myFeed.Language = "en-us";
 
 
        List<SyndicationItem> feedItems = new List<SyndicationItem>();
        foreach (DataRow dr in dt.Rows)
        {
 
            SyndicationItem item = new SyndicationItem();
            item.Title = TextSyndicationContent.CreatePlaintextContent(dr["title"].ToString());
            SyndicationPerson authInfo = new SyndicationPerson();
            authInfo.Email = "SampleApp@YourDomain.com";
 
            item.Authors.Add(authInfo);
            // RSS feeds can only have one author.
 
            // The stored proc returns different categories of data that I am interested in.
            switch (dr["category"].ToString())
            {
                case "WindFarms":
                case "WindFarms ":
                    item.Links.Add(SyndicationLink.CreateAlternateLink(
                        new Uri(GetFullyQualifiedUrl("/WindFarms.aspx"))));
                    authInfo.Name = "SampleApp WindFarm";
                    break;
 
                case "Exceptions":
                    item.Links.Add(SyndicationLink.CreateAlternateLink(
                        new Uri(GetFullyQualifiedUrl("/ErrorLog.aspx"))));
                    authInfo.Name = "SampleApp Exception";
                    break;
 
                default:
                    authInfo.Name = "SampleApp";
                    break;
 
            }
            item.Summary = TextSyndicationContent.CreatePlaintextContent(
                dr["summary"].ToString());
 
            item.Categories.Add(new SyndicationCategory(dr["category"].ToString()));
            item.PublishDate = DateTime.Parse(dr["pubdate"].ToString());
            item.LastUpdatedTime = item.PublishDate;
            item.Id = item.PublishDate.ToString();
 
            // Add the item to the feed
            feedItems.Add(item);
        }
 
 
        myFeed.Items = feedItems;
 
 
        XmlWriter feedWriter = XmlWriter.Create(Response.OutputStream);
 
        // Use Atom 1.0 
        Atom10FeedFormatter atomFormatter = new Atom10FeedFormatter(myFeed);
        atomFormatter.WriteTo(feedWriter);
 
        feedWriter.Close();
 
    }
 
    private string GetFullyQualifiedUrl(string s)
    {
        Uri u = new Uri(HttpContext.Current.Request.Url, s);
        return u.ToString();
 
    }
 
}
}

 

To have this feed my Outlook RSS folder I just need to right click “RSS Feeds” and select “Add a New RSS Feed…”. 

AddFeedMenu

Then enter the URL of my RSS feed.   Don’t forget to add the GUID at the end with     ?id=23F14EA1-1B20-443B-9B94-92C4EA4A8099

AddFeed

If you site uses authentication in your site you will have to turn it off for the rss.aspx page.  To do this you would add an entry in your web config file:

 
<location path="rss.aspx">
    <system.web>
        <authorization>
            <allow users="*"/>
        </authorization>
    </system.web>
</location>

You should now have a folder in Outlook that will get populated by the feed.

Tags:

ASP.Net | Development

Logging Exceptions and the Commands that Caused Them.

by jbrooks 14. December 2010 12:12

For a long time I’ve been logging all of my exceptions to a table that looks like:

ExceptionTable

Now for exceptions that were generated by the database I want to also log the exact database call that caused the exception.  It will make debugging go a lot faster.  For this I have a new table:

ExceptionCmdTable

This will get the exceptionId from the first table and the a string that caused the exception.  Something like:

saveSomething @id=100, @UserName='Jakey',  @IPAddress='100.100.100.100',  @editBy='JBrooks'

So the first part of logging the Exception is two methods on my base class.

 
public void LogException(Exception ex)
{
    LogException(ex, null);
}
 
 
public void LogExceptionx(Exception ex, SqlCommand objBadCmd)
{
    int id = 0;
 
    if (this._WritingErrorLog == true)
    {
        // don't want to get in an infinite loop.
        return;
    }
    else
    {
        this._WritingErrorLog = true;
    }
 
 
    try
    {
 
        SqlCommand objCmd = this.GetNewCmd("dbo.insertException");
 
        string message = "";
 
        // Get the userId of the user that got the error.
        if (System.Web.HttpContext.Current.Request.Cookies["UID"] != null)
            message = System.Web.HttpContext.Current.Request.Cookies["UID"].Value + "|";
 
        // Add the command that gave the error.
        if (objBadCmd != null && !string.IsNullOrEmpty(objBadCmd.CommandText))
            message += objBadCmd.CommandText + "|";
 
 
        message += ex.ToString();
 
        if (message.Length > 8000)
            message = message.Substring(0, 7999);
 
 
        objCmd.Parameters.Add("@Message", SqlDbType.VarChar, 8000).Value = message;
 
        objCmd.Parameters.Add("@StackTrace", SqlDbType.VarChar, 8000).Value =
            ((ex.StackTrace == null) ? "no stack trace." : ex.StackTrace);
 
        //insert the exception and get the new ID.
        id = this.ExecuteScalarInt(objCmd);
 
        if (id > 0 && objBadCmd != null)
            insertExceptionCmd(id, objBadCmd);
 
    }
    catch (Exception ExLogException)
    {
        this.LogExceptionToFile(ex);
        this.LogExceptionToFile(ExLogException);
    }
 
    this._WritingErrorLog = false;
 
}

Now the part that generates the command string is another method that gets called be the method above:

 
private string getCmdString(SqlCommand objBadCmd)
{
 
    if (objBadCmd == null)
        return string.Empty;
 
    if (objBadCmd.Parameters == null || objBadCmd.Parameters.Count == 0)
        return objBadCmd.CommandText;
 
    StringBuilder sb = new StringBuilder();
 
    try
    {
        sb.Append(objBadCmd.CommandText+" ");
 
        for (int i = 0; i < objBadCmd.Parameters.Count; i++)
        {
            SqlParameter p = objBadCmd.Parameters[i];
 
            if (i > 0)
                sb.Append(", ");
 
            sb.Append(p.ParameterName + "=");
 
            if (p.Value == null)
                sb.Append("null");
            else
            {
                switch (objBadCmd.Parameters[i].SqlDbType)
                {
                    case SqlDbType.BigInt:
                    case SqlDbType.Int:
                    case SqlDbType.Float:
                    case SqlDbType.Decimal:
                    case SqlDbType.SmallInt:
                    case SqlDbType.Money:
                    case SqlDbType.Image:
                    case SqlDbType.Real:
                    case SqlDbType.SmallMoney:
                    case SqlDbType.TinyInt:
                    case SqlDbType.DateTimeOffset:
                        sb.Append(p.Value.ToString());
                        break;
 
                    default:
                        sb.Append("'" + p.Value.ToString() + "'");
                        break;
 
                }
            }
        }
    }
    catch (Exception ex)
    {
        return "getCmdString created its own error processing:  " + 
            sb.ToString() + "      +++++" + ex.ToString().Substring(0, 8000);
    }
 
    return sb.ToString();
}

So now I have a complete logging of exception in my applications and it is a simple matter to make a page where I can view them, or make an RSS feed where they are sent to me.

Tags:

Development | SQL | ASP.Net

Change Connection String Password Programmatically

by jbrooks 6. December 2010 13:07

On one of our projects we have a requirement that we have to change our passwords every 90 day.  So I created a page where I can just click a button and it will generate a password, change the password on the database for the user in the connection string and then change the password in the web.config.

The page just has a button that say “Change Password” and a label called lblMessage to show the results.

The code behind just calls the class that does all of the work and then shows a message:

 
 
const string CONNSTRINGNAME = "SampleAppConnString";
const string WEBCONFIGFILE = "~/Web.Config";
 
protected void btnChangePassword_Click(object sender, EventArgs e)
{
 
if (cConnStringPasswordChanger.ChangePassword(HttpContext.Current.Server.MapPath(WEBCONFIGFILE), 
        CONNSTRINGNAME))
    this.lblMessage.Text = "Password has been changed";
else
    this.lblMessage.Text = "Password has NOT been changed";
 
}

Now for the class that does all of the work:

 
public static class cConnStringPasswordChanger
{
 
    public static bool ChangePassword(string webConfig, string ConnStringName)
    {
        return ChangePassword(webConfig, ConnStringName, 
                Guid.NewGuid().ToString().Replace("-", "").Substring(0, 9));
    }
 
 
    // 1. Open the web.config.
    // 2. Change the connString to have the new password.
    // 3. Change the password on the database.
    // 4. Commit the web.config change.
    public static bool ChangePassword(string webConfig, string ConnStringName, 
        string newPassword)
    {
 
        bool changed = false;
        string oldConnString;
 
        FileInfo fi = new FileInfo(webConfig);
 
        if (fi.IsReadOnly)
        {
            File.SetAttributes(webConfig, FileAttributes.Normal);
        }
 
        fi = null;
 
        XmlDocument cfgDoc = new XmlDocument();
        cfgDoc.Load(webConfig);
 
        XmlNode connNode = cfgDoc.SelectSingleNode("//connectionStrings");
 
        XmlNode myNode = connNode.SelectSingleNode("//add[@name='" + ConnStringName + "']");
        oldConnString = myNode.Attributes["connectionString"].Value;
 
        string oldPassword = getValue(oldConnString, "Password");
 
        string newConnString = oldConnString.Replace(oldPassword, newPassword);
 
        myNode.Attributes["connectionString"].Value = newConnString;
 
        string userId = getValue(newConnString, "User Id");
 
        XmlTextWriter writer = new XmlTextWriter(webConfig, null);
        writer.Formatting = Formatting.Indented;
 
        // last possible second change it on the database.
        changePassword(oldConnString, userId, oldPassword, newPassword);
        try
        {
            cfgDoc.WriteTo(writer);
            writer.Flush();
            writer.Close();
            changed = true;
        }
        catch (Exception ex)
        {
            // error saving web.config change, so change it back on the database.
            changePassword(newConnString, userId, newPassword, oldPassword);
            throw;
        }
 
        writer = null;
        cfgDoc = null;
 
        return changed;
    }
 
    // This function is passed a connection string like:
    // "data source=.\SQLEXPRESS;Initial Catalog=MyDb;User Id=McUser;Password=c99c0472e;"
    // and a partName like "User Id" or "Password".
    // it returns the value for that partName.
    private static string getValue(string connString, string partName)
    {
        int partStart = connString.ToLower().IndexOf(partName.ToLower());
        int partEndSemi; 
        int partEndQuote;
            
 
        if (partStart > -1)
            partStart += partName.Length + 1;
        else
            throw new Exception(partName + " not found in connection string");
 
        partEndSemi = connString.Substring(partStart).IndexOf(";");
        partEndQuote = connString.Substring(partStart).IndexOf("\"");
            
 
        if (partEndQuote == -1)
            partEndQuote = connString.Length - partStart - 1;
 
        if (partEndSemi == -1)
            partEndSemi = connString.Length - partStart - 1;
                        
        return connString.Substring(partStart, Math.Min(partEndQuote, partEndSemi));
    }
 
    // Call the database to change the password.
    private static bool changePassword(string connString, string loginName, 
        string oldPassword, string newPassword)
    {
        bool changed = false;
 
        using (SqlConnection connection = new SqlConnection(connString))
        {
            SqlCommand command = new SqlCommand("dbo.sp_password", connection);
            command.CommandType = CommandType.StoredProcedure;
            command.Parameters.Add("@loginame", SqlDbType.NVarChar, 128).Value = loginName;
            command.Parameters.Add("@old", SqlDbType.NVarChar, 128).Value = oldPassword;
            command.Parameters.Add("@new", SqlDbType.NVarChar, 128).Value = newPassword;
            command.Connection.Open();
            command.ExecuteNonQuery();
            changed = true;
        }
 
 
        return changed;
 
    }
}
 

Tags:

ASP.Net | Development | SQL

Data Access Layer Architecture

by JBrooks 23. November 2010 10:59

I wanted to document how we do our DAL.  First, performance is important so we use ADO.NET instead of the Entity Framework, NHibernate, etc.   Second, in our main project we have 1,743 stored procs  and counting, so a stored proc centered approach is necessary. 

So over the years I have reduced it down so that the following is possible:

 
protected void Page_Load(object sender, EventArgs e)
{
        if (!Page.IsPostBack)
        {
            this.GridView1.DataSource = cApp.DB.getIPAddresses();
            this.GridView1.DataBind();
        }
    }
}

Normally you would have a business layer in there, but you get the idea from this sample.  Encapsulated in that one call is the logic to get the connection string, open and close connections, log exceptions, etc. 

Here is how access to the method works.  We have a central class called cApp that contains a lot of stuff having to do with the application in general.  One of those items is the database object.

 
using System.Configuration;
 
namespace SampleApp
{
    public sealed class cApp
    {
 
        static readonly SampleApp.cDB _cDB = new SampleApp.cDB(
            ConfigurationManager.ConnectionStrings["SampleAppConnString"].ToString());
        
        public static cDB DB
        {
            get
            {
                return _cDB;
            }
        }
       
 
        static cApp()
        { }
    }
}

 

The thing to note here is that there is only every one database object, it is created in a thread safe way, and it stays around for the full length of the application.  Since the applications that I develop are almost always data driven applications I didn’t think it made much sense to create,destroy and garbage collect a data object for every request.  Nothing performs faster than the work that you don’t do.

So what does this cDB class look like?   Here is a sample with only a few methods.

 
using System;
using System.Data;
using System.Data.SqlClient;
 
namespace SampleApp
{
    public class cDB : DBLib.cDBBase
    {
        public cDB(string ConnString)
            : base(ConnString)
        { }
 
        public cDB()
            : base()
        { }
 
 
        public DataTable getIPAddresses()
        {
            SqlCommand cmd = GetNewCmd("dbo.getIPAddresses");
            return GetDataTable(cmd);
        }
 
        public void saveIPAddresses(Int32 id, String UserName, String IPAddress, String editBy)
        {
            SqlCommand cmd = GetNewCmd("dbo.saveIPAddresses");
 
            cmd.Parameters.Add("@id", SqlDbType.Int).Value = id;
            cmd.Parameters.Add("@UserName", SqlDbType.VarChar, 150).Value = UserName;
            cmd.Parameters.Add("@IPAddress", SqlDbType.VarChar, 50).Value = IPAddress;
            cmd.Parameters.Add("@editBy", SqlDbType.VarChar, 150).Value = editBy;
 
            ExecuteNonQuery(cmd);
        }
 
        public DataTable getExceptions(DateTime exTime)
        {
            SqlCommand cmd = GetNewCmd("dbo.getExceptions");
            cmd.Parameters.Add("@exTime", SqlDbType.DateTime).Value = exTime;
 
            return GetDataTable(cmd);
        }
 
 
    }
}

We follow the pattern where the method name and parameters are the same as the stored procs.   I had previously blog about how I automatically generate the code for these methods based on the stored procs HERE , which really helps when there are a lot of parameters. 

Most of the work is found in the class that the cDB class inherits from, DBLib.cDBBase.  It is a class I use in every project and it has methods for getting DataTables, Integers, GUIDs, etc.  It will log exceptions to a table and then fail over to a text file if needed.  

You can download a sample application that has the full source code including cDBBase HERE

Tags:

Development | General

Stripping Out Passwords

by jbrooks 19. November 2010 07:44

I have a page in my ASP.Net application where I show all of my AppSettings, connection strings, etc.  But I didn’t want to show the passwords.  I wanted something like:

server=MyServer;uid=MyUserId;pwd=*******;database=MyDatabase;

So here is the code to strip out the password:

 
 
        private string stripPassword(string connString)
        {
            if (string.IsNullOrEmpty(connString))
                return connString;
 
            int pos = connString.ToLower().IndexOf("pwd");
            if (pos > -1)
                pos += 4;
            else
            {
                pos = connString.ToLower().IndexOf("password");
                if (pos > -1)
                    pos += 9;
                else
                    return connString;
            }
 
            return connString.Substring(0, pos) + "*******" + 
                connString.Substring(pos + connString.Substring(pos).IndexOf(";"));
        }
 

Tags:

ASP.Net | Development