Friday, 22 December 2006

DropDownList SelectedValue exception

'DropDownList1' has a SelectedValue which is invalid because it does not exist in the list of items. Parameter name: value

I have found many people have this exception in their dropdownlist databinding. It's annoying when upgrade an application from asp.net 1.1 to 2.0. Code used to work now breaks.

This exception is caused when a drop down list has been bound to a datasource, with a item selected, the .net runtime tries to bind the drop down list to a new datasource with different data, and the selected value is not in the new datasource.

There is one work around which needs to set the AppendDataBoundItems property to true. This works if the drop down list is bound only once. If it's bound several times, the item list will grow with repeatitive values.

The only workable solution I have found is to do this:

In the stored procedure, put a union select top item for the list as
select Text, Value from Customer
union
select '', null

In the client side, whenever before the list is bound, put a line of code like this:

Dropdownlist1.SelectedValue = ''


This would solve the problem.

Sunday, 3 December 2006

In custom control the ID property of child controls must be unique

When we create a composite web custom control, often we create child controls in the override CreateChildControls() method. At this time we assign a text value to ID property of each child control. There is a little bit tricky of this ID property. The ID value should be unique across the whole page, not only in the control itself. Say if we have a TextBox with ID "txtValue", when we put two controls in a page and call FindControl("txtValue"), the runtime throws an exception says there are multiple controls have the same txtValue ID value. This is because asp.net assign the same txtValue to two text box in a same page.

To solve this problem, we alway prefix the control's client id with the child id to ensure uniqueness. For example, we can set this.ClientID + "txtValue" to the TextBox ID property. Because the ClientID of a custom control is unique, the ID of each child control is always unique too.

Saturday, 25 November 2006

Order by clause is no longer supported in SQL Server 2005 Views

We used to have an application running on Sql Server 2000. And we built a view in which we put a Order By clause to pull data out in order. This works fine and the data is ordered. When we migrate the database to Sql Server 2005, we found the view is no longer ordered even the order by clause is still there. After some research on the net, we found out the Order By clause in a view has never been documented anywhere and this is not supported in Sql Server 2005 anymore.

To make an experiment, I created a view in Sql Server 2000 and 2005 as below:

Create View EmployeeView AS
SELECT TOP (100) PERCENT EmployeeID, LastName, FirstName
FROM dbo.Employees
ORDER BY LastName

When I run a query as Select * from EmployeeView against Sql Server 2000, I get the correct result with LastName ordered. When I run the same query against Sql Server 2005, the result is in the same order in the Employees table, not ordered by the LastName.

Monday, 20 November 2006

.net collection classes summary

There are many collection classes provided in .net framework. To use these classes appropriately is important for a developer. And it's quite essential to understand the subtle difference between each class.

These classes are summarized below.

1. ArrayList. This is the most common class used in .net 1.1. It's a resizeable, index-based collection of objects. Some important methods include Add, AddRange, Remove, Contains and IndexOf. This class is replaced by generic List<> class in .net 2.0. To interate over items in an ArrayList, we can use IEnumerator interface. eg.
IEnumerator en = ar.GetEnumerator();
while(en.MoveNext())
{
string s = en.Current as string;
...
}
ArrayList provides a Sort method. The sort method uses the default implementation of IComparer - Comparer class to do the comparison.

2. Queue class. This is a collection of objects with first-in, first-out feature. The Dequeue method read and remove the item from the front of the queue. The Enqueue method adds an item to the end of the queue. And Peek method only read the first item in the queue.

3. Stack class. The Stack class is a last-in, first-out collections of object. The Pop method reads and removes an ite mfrom the top of the stack. The Push method adds an item on the top of the stack. And the Peek method reads the last item without removing it.

4. Hashtable class. This is a dictionary class, providing a key/value pair of lookup table. eg.
Hashtable ht = new Hashtable();
ht["Key"] = "Value";
which is equivalent to :
ht.Add("Key", "Value")
If you try to Add a same key twice, the class will throw an exception, but you can assign a same key as many times as you like, only the last value is kept.
One important feature of Hashtable is that wecannot access item by index number.

5. SortedList class. This class sort items when adding them to the list, and items can be accessed by their index. The use of this class is quite similar to Hashtable.

6. ListDictionary class. This class is lightweight and efficient class for a small number of items. The usage is identical to Hashtable.

7. HybridDictionary class. HybridDictionary is a dynamic collection. When the number of items are small, it implement itself as ListDictionary. Whe number of items becomes two large, it turns itself to Hashtable.

8. OrderedDictionary class. This class provides the same functionalities of Hashtable, as well as the ability of access items by their index number.

9. BitArray. This class holds an array of boolean values. eg.
BitArray ba = new BitArray(2);
ba[0] = false; ba[1] = true;

10. BitVector32 structure. The BitVector32 structure is very useful to maintain individual stutus in a single integer. Its useful in WebService when network bandwidth is limited. With one integer value, it can contain 32 true or false status.

11. StringCollection class. A simple collection only stores strings.

12. StringDictionary class. Similar to Hashtable, except both key and value are strings.

13. CollectionUtil class can create Hashtable and SortedList that are case insensitive. eg.
CollectionUtil.CreateCaseInsensitiveHashtable();

14. NameValueCollection class is similar to StringDictionary class. The difference of this class is it allows multiple values share a same key. And values can be retrieved by index as well as key. eg.

NameValueCollection col = new NameValueCollection ();
col.Add("key", "Value 1");
col.Add("key", "Value 2"); // then we have two values with same key.

If you add two values with same indexer, only the last value is kept. eg.]
col["key"] = "Value1";
col["key"] = "Value2"; // only Value2 is kept.


15. Generic collection classes. Almost for each class mentioned above there is a generic version. The use of these generic classes are similar. The only difference of Dictionary classes is the way of retriving items. The generic class KeyValuePair is used to read each item. eg.
SortedList list = new SortedList();
list["one"] = 1;
list["two"] = 2;
foreach(KeyValuePair i in list)
{
string s = i.Key;
int value = i.Value;
}

Sunday, 19 November 2006

Working with Isolated Storage

Isolated Storage is a special and safe place in the computer to save user data in machine/assembly scope or user/assembly scope. It eliminates the risk of making damages to the whole system by the user, and the application will run regardless of whether it's running uder partial, limited or full trust security level.

There are two important classes for isolated storage: IsolatedStorageFile and IsolatedStorageFileStream class. The IsolatedStorageFile class is responsible for creating files and directories in isolated storage. The IsolatedStorageFileStream class inherites from FileStream and is responsible for file IO.

To read data from isolated storage:

IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForAssembly();
// here we could also call IsolatedStorageFile.GetMachineStoreForAssembly() to get store for the application scope.

IsolatedStorageFileStream stream = new IsolatedStorageFileStream(fileName, FileMode.Open, store);
StreamReader rdr = new StreamReader(theFile);
string s = rdr.ReadToEnd();

To write data is similar:
IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForAssembly();
// here we could also call IsolatedStorageFile.GetMachineStoreForAssembly() to get store for the application scope.

IsolatedStorageFileStream stream = new IsolatedStorageFileStream(fileName, FileMode.Create, store);
StreamWriter writer = new StreamWriter(theFile);
writer.WriteLine("hello");

Saturday, 18 November 2006

Various ways of file read/write in .net framework

I have summarized various ways of file IO in .net framework. There are many ways of doing same thing in file IO.


string path = @"C:\Temp\Test.txt";
// 1. Using System.IO.File.ReadAllLines to read lines of file content into an array of strings
string[] lines = File.ReadAllLines(path);

// 2. Using System.IO.File.ReadAllText to read the whole content of a file to a string
string s = File.ReadAllText(path);

// 3. Using StreamReader, File and FileStream calsses
FileStream theFile = File.Open(path, FileMode.Open, FileAccess.Read);
StreamReader rdr = new StreamReader(theFile);
string s = rdr.ReadToEnd();

// 4. Using StreamReader class only
StreamReader rdr = new StreamReader(path);
string s = rdr.ReadToEnd();

// 5. Using StreamReader and File class
StreamReader rdr = File.OpenText(path);
string s = rdr.ReadToEnd();

// 6. Using MemoryStream class to write to file to improve performance
MemoryStream memStrm = new MemoryStream();
StreamWriter writer = new StreamWriter(memStrm);
writer.WriteLine("hello");
writer.Flush();

// write content in MemoryStream to file stream
FileStream theFile = File.Create(path);
memStrm.WriteTo(theFile);

// 7. Using BufferedStream to improve performance.
FileStream file = File.Create(path);
BufferedStream buffered = new BufferedStream(file);
StreamWriter writer = new StreamWriter(buffered);
writer.WriteLine("hello");
writer.Close();

// 8. Using GZipStream to compress data. The source file is a normal file. The destination file is compressed.
FileStream source = File.Open(inFileName);
FileStream dest = File.Open(outFileName);

GZipStream comStrm = new GZipStream(dest, CompressionMode.Compress);

int theByte = source.ReadByte();
while (theByte != -1)
{
comStrm.WriteByte((byte)theByte);
int theByte = source.ReadByte();
}

// 9. Using GZipStream to decompress data. The source file is a compressed file. The destination file is normal.
FileStream source = File.Open(inFileName);
FileStream dest = File.Open(outFileName);

GZipStream comStrm = new GZipStream(dest, CompressionMode.Decompress);

int theByte = source.ReadByte();
while (theByte != -1)
{
comStrm.WriteByte((byte)theByte);
int theByte = source.ReadByte();
}

// 10. Using FileInfo class
FileInfo fileInfo = new FileInfo(path);
FileStream stream = fileInfo.Open(FileMode.Open, FileAccess.Read);

Saturday, 11 November 2006

SCOPE_IDENTITY() Returns Decimal

We use SCOPE_IDENTITY() function quite often to get the last identity value generated by Sql Server. What confuses me first is this function returns Decimal value. I have a sql insert stored procedure which has this last statement:

Select SCOPE_IDENTITY(),

The table has a auto incremental id field.

In my client code I have this:

int id = (int) command.ExecuteScalar(...)

Initially I thought I would read the integer id value without problem, but soon I got an InvalidCastException. And I discovered that the return value from DbCommand is of Decimal value type.

When I looked into MSDN, which is here:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/tsqlref/ts_sa-ses_6n8p.asp

I found out the return data type of this function is sql_variant. So the actual datatype could be any as Microsoft may change it in the future.

This is really interesting. To return an integer from stored procedure, we could cast it to integer as : Select CAST(SCOPE_IDENTITY() as Int) . This solves problem.

Tuesday, 31 October 2006

GridView RowDataBound event

When we bind a data source to a GridView in asp.net, we often need to do some processing for each row. For example, we might need to change a row's background color based on some rules. GridView provides a RowDataBound event in which we can get the data of the row and do whatever required.

The event takes two parameters: RowDataBound(object sender, GridViewRowEventArgs e).

To determine if a row's type, we could use: if (e.Row.RowType == DataControlRowType.DataRow).
To get a field data for that row, we could use: string status = (string) DataBinder.Eval(e.Row.DataItem, "PropertyName").

Friday, 20 October 2006

Dead lock encountered today

I have met with a dead lock today in my web service application. It's very hard to find initially because each stored procedure is running fine individually but when tie them together a dead lock occurs.

What happens is I have several stored procedures having insert/update, others only have read access. I put all insert/update stored procedures inside a transaction using ado.net transaction, while decide to leave read only stored procedures outside of transaction. It worked fine until I put a insert statement in one of my read stored procedure, which insert into a same table which is under transaction control. Then dead lock occurs here because the table is locked.

I tried hard to figure out this bug and decided to put everything inside the transaction. Then the bug was fixed.

Thursday, 19 October 2006

Some T-SQL cool tricks

1. How to find second highest number in a column:

SELECT MAX(OrderQty) FROM Orders where OrderQty not in (select top 1 OrderQty FROM Orders ORDER BY OrderQty DESC)

2. Using COALESCE to concatenate Comma-Delimited string, this has been discussed in my previous post. This is another solution.

DECLARE @EmployeeList varchar(100) SELECT @EmployeeList = COALESCE(@EmployeeList + ', ', '') + CAST(Emp_UniqueID AS varchar(5)) FROM SalesCallsEmployees WHERE SalCal_UniqueID = 1 SELECT @EmployeeList


3. SELECT * FROM CUSTOMERS ORDER BY 1.
This trick is from my collegue. Note order by 1 works in SQL Server, not sure about other databases. It will order by the first column. Similarly you could order by 2, 3 ..

Wednesday, 18 October 2006

.net Deployment project variables setup

I was struggling with finding the variebles of a .net compact framework cab deployment project. The variable I wanted to change is Manufacturer. Microsoft says to change it in properties window. I right clicked on the deployment project, and selected Properties. And I was not able to find the variebles.

Where on hell is the Manufacturer variable? I did ask so. After struggling for a while, I found out it's in "Property Window", which means I need to click on the project, and press F4 key. Then I got the window.

Tuesday, 17 October 2006

Play around with T-SQL decimal and float data types

Decimal data type in t-sql is an interesting thing. It automatically trunc data decimal part in a division operation if precision part is not defined. I have this query:

declare @result decimal
select @result = 2 / 10
select @result

The result is 0.

if I change @result to float, the result is 0.0.

To get what we need for a floating point value, we could do this:

declare @result float
select @result = cast(2 as float) / cast(10 as float)
select @result

Then the result is 0.200000000001

Friday, 6 October 2006

Code Access Security article

There is a great article explaining details of Code Access Security (CAS) in .NET.

http://www.codeproject.com/dotnet/UB_CAS_NET.asp

Monday, 2 October 2006

Infer schema from xml data file when loading data to a dataset

In a Windows Mobile project I have a strongly typed dataset to store relational data, store the data in a xml file, and reload the data from the xml file into the dataset when application starts. One of the requirements from business needs some dynamic table to be added to the dataset. That means the dataset consists of typed, well defined tables and undefined, dynamic tables as well.

When writing data to file system, there is no problem, I simply call dataset.WriteXml(fileName) method and all tables are saved correctly. The problem comes when re-loading data from xml file to the dataset again. When I call dataset.ReadXml(fileName), only defined tables are loaded. Those dynamically generated tables are not.

When looking into some overriding methods of ReadXml, I found dataset.ReadXml(fileName, XmlReadMode.InferSchema) solved my problem. When adding this parameter, all tables are read into the dataset.

Reading more information from Microsoft online resources, I found out this relates to the concept of inference. The actual structure of the xml data itself is the basis for a relational schema. The system tries to load the xml data file into dataset and generate table structures based on the xml data itself.

Thursday, 28 September 2006

Embedded source file for asp.net custom control

When designing a custom control in asp.net, often we have a client side javascript file for the control. We have two options to include the script file: either declare it in asp.net webform directly or embed it in custom control dll file.

In asp.net 1.1 we could include the javascript in the custom control project's resource file and consume the resource at run time. This elimilated the need of the dependent on the external script file. Or we could create a HTTP Module or Handler to handle the script file name. While this works, it requires some extra coding.

In asp.net 2.0 we have a new and simplified approach. We could embed the script file in the external dll file and consume it using the built-in WebResource.axd http handler.

The following is an example of applying this strategy.

I have created a simple custom control to show alert message in a web form. This control simply call javascript alert function.

First I create a Web Control project, with namespace as MyWebControl. Then I create a javascript file Alert.js in the folder JavaScript. There is only one function in the Alert.js:

function ShowAlert(message)
{
alert(message);
}

Then I open the property of this file, change it's Build Action property to Embedded Resource.

By default, any resource in the dll file is not accessible externally. To enable web form access this resource, we need to define it in the AssemblyInfo.cs file:

[assembly: WebResource("MyWebControl.JavaScript.Alert.js", "text/javascript", PerformSubstitution = true)]

Note I also include the control namespace as prefix to the resource name, this is required.

The resource is available now, what we do is to consume it in the control source code. The following function is an example:

protected override void OnPreRender(EventArgs e)
{
ClientScriptManager cs = Page.ClientScript;
if (!cs.IsClientScriptIncludeRegistered("WebAlert"))
{
cs.RegisterClientScriptInclude("WebAlert", cs.GetWebResourceUrl( this.GetType(),
"MyWebControl.JavaScript.Alert.js" ));

} if (Text != string.Empty)
{
string message = Text.Replace("\r", "").Replace("\"", "\\\"").Replace("\n", @"\r\n");
cs.RegisterStartupScript(this.GetType(), "AlertScript", "ShowAlert(\"" + message + "\");", true);
}
Text = string.Empty; // make sure to clear the message to remove the alert for the next page load.
base.OnPreRender(e);
}

That's all we need. After put this control in a webform, asp.net put this line in the html:


The script is cached in the client side so it's downloaded into user's browser only once.

SQL Server Date and Time Functions

Date Time functions can be found in the online book. I list them here for quick reference.


DATEADD(datepart, integer, datetoadd) Returns a new datetime value based on the parameters.
DATEDIFF(datepart, date1, date2) Returns number of time units between two dates
DATENAME(datepart, datetoadd) Returns the full name of the date part. eg

select DATENAME(month, getdate()) as month_name
month_name
----------------------
July
DATEPART(datepart, date) Returns part of a date
DAY(date), MONTH(date), YEAR(date) Returns specific part of a date.

Date Part abbreviations


year yy, yyyy
quarter q, qq
month m, mm
day of year dy, y
day of month day
week ww, wk
hour hh,
minute mi, n
second ss, s
millisecond ms

Frequently used SQL Server string functions quick reference

These string functions can be found in the SQL Server online book. I list them here to make it easier for me to lookup.

Function Description
LEFT(string, lenght)
RIGHT(string, length) Returns leftmost or rightmost of a string with given length.
LEN(string) Returns the length of a string.
LOWER(string)
UPPER(string) Returns string lower or upper case.
LTRIM(string)
RTRIM(string) Trim a string in left or right side. For a full trim, use LTRIM(RTRIM(string))
PATINDEX(pattern, string) Returns the starting position of the first instance of pattern in the string. If the pattern is not found, then returns zero. eg

select PATINDEX('%CAT%', 'CATALOGUE') as Match
Match
------------------
1

REPLACE(string to replace, for, with) Returns a string in which all occurrences if the second expression within the first expression are replaced with the third expression. eg.
SELECT REPLACE('ABCDE', 'AB', '***') as Result
-------------
***CDE
QUOTENAME(string, quote) Returns a string with quotation mark surrounded. The quote can only be ', ", or [ or ]. eg
SELECT QUOTENAME('hello', '[') as Result
---------------
[hello]
REPLICATE(string, times) Replicate a string with number of times.
SPACE(length) Return blanks with given length.
SUBSTRING(string, start, length)

Friday, 22 September 2006

Custom aggregation in SQL Server - best solution ever (3)

I came across another elegant solution does the same job for custom aggregate. The only disadvantage of this approach is we need to write a function for each table needed. This is not a problem if performance is the key.

The function is quite short:

CREATE FUNCTIONS EmployeeList(@DeptID int)
RETURNS VARCHAR(8000)
AS
BEGIN
DECLARE @Result VARCHAR(8000) , @delim VARCHAR (2)

-- set empty string for delimitor. The comma would be set to it after the first select statement. Excelent!
SELECT @Result = ', ', @delim = ''
SELECT @Result = @Result + @delim + EmpID, @delim = ', '
FROM Emps
WHERE DeptID = @DeptID

RETURN @Result
END

This is the best approach I have seen so far and is very fast. It is about 3 time's faster than cursor approach. I have changed all my functions to follow this approach.

Thursday, 21 September 2006

Custom aggregation in SQL Server 2005 with .net class (2)

In my previous post, I discussed how to aggregate detail records into comma separated string using cursor. From what I have observed, cursor has performance issues in a large table.

In Sql Server 2005, we can write our own class in any .net language. The fowllowing article discusses an approach that meets my needs very well. If we use SQL Server 2005 we should consider this approach.
The original URL is here:
http://www32.brinkster.com/srisamp/sqlArticles/article_44.htm

To make it easier to understand. I just copy the code from the article and show how to use it.
The Aggregation function is written is vb.net.


Imports System
Imports System.Data.Sql
Imports System.Data.SqlTypes
Imports System.Text
Imports System.Runtime.Serialization

_
IsInvariantToOrder:=False, IsNullIfEmpty:=True, MaxByteSize:=8000)> _
Public Class FormDelimitedString
Implements IBinarySerialize

' Variable to hold the return result
Private returnString As StringBuilder

Public Sub Init()
' Initialize the string builder. This method will be called for each
' group that is accumulated
returnString = New StringBuilder()
End Sub

Public Sub Accumulate(ByVal value As SqlString)
' Accumulate the value into the string builder. If the value is NULL
'do not do anything. This method is called for each row of data
If (value.IsNull) Then
Return
Else
returnString.Append(value.Value).Append(", ")
End If
End Sub

Public Sub Merge(ByVal value As FormDelimitedString)
' Merge the current instance with the other instance.
returnString.Append(value.returnString)
End Sub

Public Function Terminate() As SqlString
' Return the result if the string has some content
If (returnString.Length > 0) Then
Return New SqlString(returnString.ToString(0, returnString.Length - 2))
End If

' Return an empty string otherwise
Return New SqlString("")
End Function

Public Sub Read(ByVal r As System.IO.BinaryReader) Implements System.Data.Sql.IBinarySerialize.Read
returnString = New StringBuilder(r.ReadString())
End Sub

Public Sub Write(ByVal w As System.IO.BinaryWriter) Implements System.Data.Sql.IBinarySerialize.Write
w.Write(returnString.ToString())

End Sub
End Class


To use this class, the dll needs to be deployed to the Sql Server 2005. The following example shows how to use it:

SELECT
ph.PurchaseOrderID, [Items] = dbo.FormDelimitedString (p.ProductNumber)

FROM
Purchasing.PurchaseOrderHeader ph
INNER JOIN Purchasing.PurchaseOrderDetail pd ON ph.PurchaseOrderID = pd.PurchaseOrderID
INNER JOIN Production.Product p ON pd.ProductID = p.ProductID
WHERE
ph.PurchaseOrderID < 10
GROUP BY
ph.PurchaseOrderID


The result would look like:
PurchaseOrderID Items
1 Item1, Item2, Item3
2 Item2, Item4

Wednesday, 20 September 2006

A weird bug in Visual Studio 2005

I met a weird bug that drove me crazy before I figured it out by a chance. When I open an existing project in Visual Studio, I normally open it from the start page. And the project will be openned automatically. But this doesn't apply to my recent reporting service project. When I click on the project name on the start page, the project would open, but when I open a report in report designer, the IDE failed to load it with hanging there for one minute.

I have re-installed my visual studio, SQL Server 2005 client component but that didn't help. Finally I got it working today by select menu Open Project, and then point to the project file. The bug is gone and report can be loaded into IDE successfully. What a frastracting bug!

Sunday, 17 September 2006

Master - Details hierarchical structure in one table

This is a traditional manager-employee hierarchy problem that have many ways of solving it, but I chose implementing it with a SQL function.

We have a need to represent parent - children relationship in one table. The table is relatively simple. In the Employee table we have EmployeeID, Name, DepartmentID, and ManagerID. The ManagerID field links to another EmployeeID in the same table.

The table is easy to implement and development in our application. But when we come to display the data, it's becoming tricky. What we need is to display an Employee and his manager in a report. And a manager could have his upper level manager too. After many hours of search through web, I came up with a function as below:

CREATE FUNCTION dbo.GetEmployeeTree
(@DepartmentID int, @EmployeeID int, @depth int)
RETURNS @EmployeeTree TABLE (EmployeeID int, Depth int, Name nvarchar(50))
AS
BEGIN
DECLARE @ParentID int
DECLARE @nextDepth int
SET @nextDepth = @depth + 1
SET @ParentID = (SELECT ManagerID FROM Employee WHERE EmployeeID = @EmployeeID and DepartmentID =@DepartmentID)

IF @ParentID <> 0
/* We stop if a container has no parent */

INSERT INTO @EmployeeTree (EmployeeID, Depth, Name)
SELECT EmployeeID, @depth As Depth, Name
FROM Employee C
Where C.EmployeeID = @ParentID and C.DepartmentID=@DepartmentID
UNION
--Recurseively call the function to append
SELECT EmployeeID, Depth, Name FROM GetContainerTree(@DepartmentID, @ParentID, @nextDepth)
RETURN
END


To use this function for an employee's manager and upper managers, we need to write a stored procedure or just a simple query like this:


CREATE PROCEDURE dbo.GetManagers
(
@EmployeeID int,
@DepartmentID int
)
AS
select EmployeeID, Name, Depth from GetEmployeeTree(@DepartmentID, @EmployeeID, 0)

RETURN

GO

Custom aggregation in SQL Server with cursor (1)

If we have a Customer and a Order table, with one to many relationship, often we need to concatenate details records into master table for displaying. The details could be separated by semi colon into master table. The following is the Function required to get the concatenated value:

CREATE FUNCTION dbo.GetConcatenatedOrderNumbers
(
@CustomerID int
)
RETURNS nvarchar(500)
AS
BEGIN

declare @item nvarchar(200)
declare @result nvarchar(400)

select @item = ''
select @result = ''

declare item_cursor cursor for
select OrderNumber from Order Order.CustomerID=@CustomerID

Open item_cursor
Fetch next from item_cursor into @item

while @@FETCH_STATUS = 0
begin
select @result = @result + @item + '; '
Fetch next from item_cursor into @item
end

CLOSE item_cursor
DEALLOCATE item_cursor

-- remove space in the end
select @result = rtrim(@result)
-- remove the ; at the end
if(Len(@result) > 1)
begin
set @result = substring(@result, 1, len(@result) - 1)
end

RETURN @result
END

Saturday, 16 September 2006

Some useful short cut keys in Visual Studio 2005

These are some short cut keys I learnt today:

Alt + Shift + Enter = full screen
Type prop and press Tab twice will trigger property code snippet.
F12 = goto variable definition. Shift + F12 = find refferences
Ctrl + K + C = comment; Ctrl + K + U = remove comment
Ctrl + m + m = collapse or expand a region or function
If a function does not exist and Ctrl + k + m is pressed, the function is implemented by default.

ATLAS UpdatePanel and FileUpload control

It's a known issue that asp.net FileUpload control doesn't work inside a UpdatePanel in a ajax enabled web page. There is a work around to solve this problem if you still need to put FileUpload control inside the UpdatePanel for page layout reasons. The work around is as follows:

In the asp.net page, define a FileUpload and a Button control inside ContentTemplate:

<atlas:UpdatePanel ID="multiViewUpdatePanel" runat="server" Mode="Conditional">
<ContentTemplate>
<asp:FileUpload ID="attachmentFile" runat="server" />
<asp:Button ID="uploadButton" runat="server" Text="Upload" />
</ContentTemplate>
</atlas:UpdatePanel>

Still in the asp.net page, put a "fake" link button outside of UpdatePanel as this:

<asp:LinkButton ID="fakeUploadButton" style="display:none" runat="server" Text="upload" OnClick="fakeUploadButton_Click" />

In the code behind file, put this code in the Page_Load event:

protected void Page_Load(object sender, EventArgs e)
{
this.uploadButton.Attributes.Add("onclick", Page.ClientScript.GetPostBackEventReference(fakeUploadButton, ""));
}

After this line of code is executed, when a user click on the uploadButton, which is located beside the FileUpload control, the "fake" link button's OnClick event will be triggered because of the JavaScript added to the client side onclick event of uploadButton.

Then in the server side catch the OnClick event of the link button as below:

protected void fakeUploadButton_Click(object sender, EventArgs e)
{
if (attachmentFile.HasFile)
{
string fileName = attachmentFile.FileName;
// ...
}
}

Then when you click on the Upload button, an explict postback is triggerred which will post the file to the server side.

Another note, if you have a control inside UpdatePanel and you write file down load code directly in your asp.net code behind, the file won't be downloaded to client side. What you could do is to change the LinkButton to a HyperLink, which points to another WebForm, with whatever parameter passed with URL. In the second WebForm, you could write code to down load a file because that page has no UpdatePanel. And it works.