Recently, I received an invite for Google Voice. Google Voice is an application that helps to integrate your potentially many phone numbers into one. It provides some great features like voice mail transcription, email/text voice mail alerts, indexed call logs, multi-phone ringing – even based on schedules/contact groups, discriminant call greetings, and a great deal more. Since it is currently in Beta, it is invitation only. It seems like the invitation process only takes a few weeks, so sign up now! If you have an Android phone, it integrates even further…
Click Here to Request an Invitation
Happy Holidays!
I just read an interesting article on a software development tool called Kanban. Kanban is a Japanese word meaning “visual card”. Kanban emphasizes on delivering small chunks of business value to the client frequently. It limits the amount of work in progress and encourages the use of a physical object to illustrate the position of items or cards in whats called a Value Stream. It is a “lean” tool for development that aims to eliminate waste. One of Kanban’s principles I find interesting is the minimized focus on estimation. Estimation is said to be a time waster and rather than focusing on accurate estimation, one should focus on delivering the product.
Here’s an article I found interesting regarding Kanban:
http://www.infoq.com/articles/hiranabe-lean-agile-kanban
Another from someone from the other side of the fence:
http://www.agile-software-development.com/2009/05/kanban-applied-to-software-development.html
The world of WCF or Windows Communication Foundation can be construed as a rather deep one. There are plenty of things that can go wrong depending on how much you’ve tweaked your configuration and more importantly how much you know about WCF. These problems can be difficult to resolve at times, because quite frequently the error message you get from a WCF client has nothing whatever to do with the actual problem in your client configuration, on the server, etc. One of these cases is the following error message or something similar:
System.ServiceModel.Security.SecuritySessionClientSettings`1 + ClientSecurityDuplexSessionChannel[System.ServiceModel.Channels.IDuplexSessionChannel], cannot be used for communication because it is in the Faulted state.
This particular problem will also manifest on the server as a System.ServiceModel.CommunicationException with the following message:
The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue.
This can mean any number of things, but one that many times will slip the mind is the amount of data being returned from the server. If the server tries to return too much data the client will close the connection and throw the first mentioned error message. To solve this problem, you should very carefully inspect the settings on your client binding. Pay special attention to the reader quotas and maxReceivedMessageSize. Try increasing these to the value of int.max or something similar to rule thsis out or determining an approriate value based on the data being returned from the server.
SharePoint really gets on my nerves sometimes. It has its uses, and tries to do so much more failing mostly. One of the things I actually do like about it is the simplicity with which end users can modify the content in a SharePoint site. We have the standard version of Microsoft Office SharePoint Server and use it for our company intranet. Its a very nice looking site that I put together (with some help) that looks nothing like SharePoint. To do this, we use some custom webparts and some built-in ones – especially the Content Query Web Part which I am very fond of.
One of the annoyances I’ve encountered is the automatic (forced) caching of all static content. It is kind of difficult to get the cache to refresh so for a while we were using our login scripts to delete cached content on client machines. I’ve gotten the actual process of refreshing the cached css styles related to themes down though as well as that of JavaScript. This may seem a bit of an odd way, but we done this to avoid any unnecessary custom development. Basically, we use add a query string parameter to each static file that we want to control the version of. This means that the script element referencing jquery-1.3.2.js is changed to jquery-1.3.2.js?rev=1.0.0.0 or something of that nature. Our css files are referenced from our theme using @import statements, so the theme css is just a list of css imports. This makes it a bit more difficult because you aren’t actually referencing the css on a page where you can change the path. We add the rev to the css imports though, and then the most difficult part is to get SharePoint to update its stored version of the theme’s css. This can be done with the following steps:
- Update the theme css file.
- Restart IIS.
- Change the site theme to another and apply.
- Change the site theme to the custom theme and apply.
Viola, your cached css is updated. I’ll be posting some other findings as time allows. Let me know your thoughts!
A minor update: if you have multiple subsites that use the same theme and the theme’s css has been updated changing the theme at the root site alone will not force those children to be refresh thier cached css. In order to make this happen I use a custom STSADM command gl-applytheme, part of a wonderful collection of custom STSADM commands that can be found at:
http://stsadm.blogspot.com/2007/08/stsadm-commands_09.html
Details about the gl-applytheme command can be found at:
http://stsadm.blogspot.com/2008/01/apply-theme.html
The modified steps to make this work are as follows:
- Update the theme css file.
- Restart IIS.
- Running the following commands:
stsadm -o gl-applytheme -url "http://yoursite.com" -theme none -recurse stsadm -o gl-applytheme -url "http://yoursite.com" -theme CustomTheme -recurse
Note the “none” in the first command will set SharePoint to the default theme. The CustomTheme should be replced with the name of your custom SharePoint theme and the url with the url of your site collection root (or the root of sites you want to update).
I was recently tasked with converting a legacy database and all its associated schema objects and reports to SQL Server 2005 and Reporting Services. In my endeavor, I came across a particular WTF that stood out. A stored procedure exists that uses cursors to grab comma separated lists of data for a particular fields in a child tables. There are one to many relationships for each of the children to the main parent object. For confidentiality, I will replace the actual tables with the parent Office and the children Client and Employee. In this example, Client and Employee both have a full name property. I would like to display the full names of all Client and Employees as comma separated lists along with some basic Office information (OfficeID, Name, Location, etc). This seems a pretty simple task which can be solved multiple ways. The aforementioned WTF chose to handle this using a cursor and varchar variable:
Declare curClientsForOffice Cursor LOCAL Fast_Forward FOR SELECT FullName FROM Client WHERE OfficeFK = @OfficeID ORDER BY FullName Open curClientsForOffice Fetch Next FROM curClientsForOffice INTO @clientForOffice SELECT @clientsForOffice = '' While @@Fetch_Status = 0 Begin IF Len(@clientsForOffice) > 0 Begin SELECT @clientsForOffice = @clientsForOffice + ', ' + @clientForOffice End Else Begin SELECT @clientsForOffice = @clientForOffice End Fetch Next FROM curClientsForOffice INTO @clientForOffice End Close curClientsForOffice Deallocate curClientsForOffice
The same thing is repeated to obtain the employee list, and the results are returned using a select statement. This is the worst way to produce the required result set. One other popular method requires the use of a single select statement, and coalesce:
DECLARE @clientList varchar(8000) SELECT @clientList = COALESCE(@clientList + ', ', '') + FullName FROM Client WHERE OfficeFK = @OfficeID SELECT @clientList
This method is much more efficient, simple, and maintainable. There is also a third option. I have created a user defined aggregate CLR function called CommaJoin in C#. This function simply builds a list using the Join method of the string class. Declaring a CLR function is really simple using Visual Studio 2005 or 2008 (as I am using). The simplest way is to create a new project using the C# or VB.NET SQL Server (or SQL CLR) project template. This allows you to set the database connection and automatically deploy the assembly and create any references to the user defined objects. The C# class is as follows:
using System; using System.Data; using System.Data.SqlClient; using System.Data.SqlTypes; using Microsoft.SqlServer.Server; using System.Xml.Serialization; using System.Xml; using System.IO; using System.Collections; using System.Text; [Serializable] [SqlUserDefinedAggregate(Format.UserDefined, MaxByteSize = 8000)] public struct CommaJoin : IBinarySerialize { ArrayList _list; public void Init() { _list = new ArrayList(); } public void Accumulate(SqlString value) { if (!value.IsNull) { _list.Add(value.Value); } } public void Merge(CommaJoin group) { _list.AddRange(group._list); } public SqlString Terminate() { string[] strings = new string[_list.Count]; for (int i = 0; i < _list.Count; i++) { strings[i] = _list[i].ToString(); } return new SqlString(string.Join(",", strings)); } #region IBinarySerialize Members public void Read(BinaryReader r) { int itemCount = r.ReadInt32(); _list = new ArrayList(itemCount); for (int i = 0; i < itemCount; i++) { this._list.Add(r.ReadString()); } } public void Write(BinaryWriter w) { w.Write(_list.Count); foreach (string s in _list) { w.Write(s); } } #endregion }
You can find more information about the requirements and usage of a user defined CLR aggregate here. This particular method allows you to use the CommaJoin as you would use Min, Max, Sum, or any other SQL aggregate. Using the new method, the code is simplified as follows:
SELECT O.OfficeID, O.Name, O.Location, master.dbo.CommaJoin(E.FullName) AS Employees, master.dbo.CommaJoin(C.FullName) AS Clients FROM Office O LEFT JOIN Employee E ON O.OfficeID = E.OfficeFK LEFT JOIN Client C ON O.OfficeID = C.OfficeFK GROUP BY O.OfficeID, O.Name, O.Location
I’ve not tested the perfomance of such a method but its simplicity and ease of use is interesting… Let me know your thoughts.
I’ve recently been given the task of integrating Sharepoint 2007 in a business application. Specifically, we are creating an app using Sharepoint that will allow users to “attach” files to objects stored in a database on a server in a farm of database servers. Using Sharepoint Services 3.0 (free version) and WCF, I have developed a solution that uses our membership database to provide user information and allows files to be attached to business objects stored in the database. Of the many custom UI components that were to be included, I found myself creating a tree view. I found a few existing tree view solutions on open source sites, but was disheartened that they did not quite provide the functionality we were looking for. Upon further research, I found a tree view control that is actually included in the Sharepoint library.
The control is SPTreeView. It is located in the Microsoft.Sharepoint.WebControls namespace in the Microsoft.Sharepoint assembly.
(I plan on posting additional resources regarding the use of that and other assemblies, developing on a non-server platform, and other obstacles that I have overcome during this process)
This class inherits the TreeView class from System.Web.UI.WebControls. To use it for hierarchal data from the Sharepoint application, you need to define a SPHierarchyDataSourceControl which can be found in the same assembly as the SPTreeView class.
Example:
(You must create a reference to the Microsoft.Sharepoint assembly in the page to use the controls. The following example assumes that you have done so and given it the alias: Sharepoint)
<SharePoint:SPHierarchyDataSourceControl runat="server" id="TreeViewDataSource" RootContextObject="Web" IncludeDiscussionFolders="true" ShowWebChildren="true" ShowDocLibChildren="true" ShowListChildren="true" ShowFolderChildren="true" /> <SharePoint:SPRememberScroll runat="server" id="TreeViewRememberScroll" onscroll="javascript:_spRecordScrollPositions(this);" Style="overflow: auto;height: 400;width: 150;" > <Sharepoint:SPTreeView id="WebTreeView" runat="server" ShowLines="false" DataSourceId="TreeViewDataSource" ExpandDepth="0" SelectedNodeStyle-CssClass="ms-navheader" NodeStyle-CssClass="ms-navitem" NodeStyle-HorizontalPadding="2"> </Sharepoint:SPTreeView> </SharePoint:SPRememberScroll>
Take note of a few important properties of the control:
RootContextObject – this defines the root object in which the control will obtain the Sharepoint data.
ShowXXXXChildren – this determines if the associated type of folders will be displayed.
ShowLines – this defines whether or not the standard lines will be displayed to the left of the hierarchy.
ExpandDepth – this defines the default exapanded depth of the hiearchy.