Jay Nathan's Weblog
Monday, June 21, 2004
 
I'm Moving my Weblog
I've gotten my personal web site reestablished and have moved my blog to it. Here is my new RSS feed. The address is http://www.jaynathan.com/blog. I'm going to try to port all of my content from blogger.com to the new blog, but the old stuff isn't going away. I'm now running .Text. Very cool if you are looking to host your own weblog.

Thanks for reading, and I'll see you on the new site!
Friday, June 18, 2004
 
SharePoint Terminology: Virtual Server
As promised, here's some more SPS terminology:

What is a SharePoint Virtual Server?

A virtual server is, quite simply, an object corresponding to a virtual directory or Web site created under the Web Site node in IIS (for example, http://Web_site_name). In IIS, one virtual server can host one or more SharePoint sites, including portal sites. After you have created a new web site or virtual directory in IIS, there are two ways for SharePoint to extend it:

1. Extend and Create Content Database
Allows you to create an entirely new content database to capture data stored in your WSS sites.

2. Extending and Map to Another Virtual Server
Allows you to map your new virtual server to an existing content database. This is useful for installing clustering your SharePoint implementation across several machines.
 
New Code Access Security Article
My fellow Charlottean, Maxim Karpov, just published a new article on Code Access Security called Code Access Security (CAS) – "Guilty until proven Innocent" (Partially Trusted Code). I had a chance to review this article after Maxim finished his first draft, and it is packed with some great information. Great work, Max!
Thursday, June 17, 2004
 
Evolutionary SharePoint
Peter O'Kelly writes about the past, present and future of SharePoint technologies and Microsoft's collaboration and integration strategy moving forward.

Of particular interest is Peter's opinion that "pure-play portal vendors" will be pushed off of the scene by integration of portal and collaborative services into the platform itself as we see with Windows SharePoint Services and Windows Server 2003. Microsoft has the added advantage of owning a large share of the productivity tools market with its Office products which fit well into its overall integration and collaboration strategy.
Wednesday, June 16, 2004
 
Lutz Roeder's .NET Reflector
You've probably seen this tool in the July 2004 issue of MSDN magazine's ".NET Tools" article, but if you haven't downloaded Lut'z .NET Reflector yet, you are missing out! It is an invaluable time-saver when dealing with questions about best-practices for .NET development. Just reflect on the base class library to see how MS implements stuff within the Framework, and at the very least, you'll have some guidance on how to proceed with your own development issues. Thanks for the great tool, Lutz.
 
Mono
Mono, an open source .NET Framework sponsored by Novell, just released its Beta 3 version. Mono not only implements the base class library of the Common Language Specification, but also implements ASP.NET and ADO.NET. They have some good screen shots of Windows Forms written in C# running on a Linux box. There are ASP.NET modules for Apache and there is another light-weight web server written in C# called XSP. You can create your own web server modules with the System.Web.Hosting namespace. Very cool!
Tuesday, June 15, 2004
 
Configuring Forms Authentication on an ASP.NET Subdirectory
One of the most common security posts that I've seen in the newsgroups lately asks how Forms Authentication can be configured on a subdirectory using a <location> tag in the web.config file. It is commonplace to have a folder such as "/Admin" within our ASP.NET apps to which only privileged users may browse.

The <authentication> element is not supported in a subdirectory of an application that is not configured as an IIS application, so do not try to add it to the web.config file of the subdirectory. The trick is to use the <location> tag in the root web.config file. The first thing we'll do though, is set up our <authentication> tag for Forms authentication as a normal course of action in our web.config, like this:


<authentication mode="Forms">
<forms loginUrl="login.aspx"/>
</authentication>

<authorization>
<allow users="*" />
</authorization>


This tells our application that we want to allow all users into the root of our application (<authorization> element), and that we want our site to use Forms Authentication. We also specified the login url. Now the only thing left to do is to add the <location> tag to our configuration. Add this below the closing </system.web> tag but before the closing <configuration> tag:


<location path="admin">
<system.web>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
</location>

This tells ASP.NET Forms Authentication that we don't want any unauthenticated users entering the "admin" directory.

As opposed to the <authentication> element, the <authorization> element actually can be used by a web.config file in a site subdirectory. Authorization settings in a subdirectory web.config will override those of the root directory's web.config by default (you can disallow overriding from a subdirectory by adding allowOverride="false" to the opening <location> tag for a directory).
 
Layered Architecture and Web Services
Scott Hanselman recently laid out a wonderful description of what a common layered design should look like.

As developers begin to build software on top of web services, where does the separation between our business layer and data access layer exist? Well, it really depends on what the core functionality of the web services looks like. Some web services simply serve us by retrieving data while others apply supply specific business functionality and logic.

If I utilize a simple data retrieval web service to retrieve information (Amazon.com search service for a nice generic example) I would most likely wrap calls to it into my DAL. To me this is similar to abstracting my software away from any changes that might occur to an underlying database. In today's integrated environment, it is becomming more rare to find an enterprise application that doens't pull and push data to and from multiple data sources. Web services are just another source of data that can be made available to the business layer of an application through a uniform data access layer.

On the other hand, if a web service presents me with data that has already been processed through a logic engine, I might not include this in my DAL, but I would most likely wrap it up under some type of business layer class that gives me the benifit of abstraction.
 
RS Web Parts - A few more items to clean up
Okay, so I've got a few more items to clean up as far as my Reporting Services Web Parts go. Here's the list:

  1. Bread Crumb Trail - The My Reports folder in RS is a special case that I need to handle within my bread crumb trail control that is a part of the Catalog Browser web part. When that particular folder is selected, the path becomes something like Home --> Users Folders --> MARINER jnathan --> My Reports. Obviously if an underprivilged user clicks either Users Folders or MARINER jnathan we're going to have a problem, so I'm just going to display it like it should be displayed: Home --> My Reports. More on the differences of that folder to other catalog items later.

  2. Cascading parameters - this is not supported in Report Viewer web part when running in stand-alone mode.

  3. Aesthetics of the Report Viewer web part - need to do some general refinement of look and feel under each tab.

  4. Refactoring - This has been the funnest part so far. What I have begun is a back-end overhaul of my code that interacts with RS. I'm unifying the object model a little which means that I'm going to have a nice little Reporting Services library to share when all is said and done, so stay tuned for that if you are interested.



Other than these web parts, I've not had a whole lot to post about, but I do have a bunch of post titles waiting for some content.
Monday, June 14, 2004
 
New Site Coming up Soon
I just got my new web site set up on WebHost4Life. Couldn't have been simpler, and the feature-set that I get for $9.95 a month is astounding to me after having spent years hosting sites on ****. There are several things that come included with my package that would cost me an arm and a leg with ****, such as SQL Server (150mb) and unlimited sub-domains. This rocks!

Within the next week or so, I'm going to start the process of moving my blog to my site, and the URL will be something like http://blogs.jaynathan.com/jnathan. I'm also going to use the site to post useful .NET code and examples. I'll keep you posted on my progress!

[Update 06-17-04]: Since this entry has been pointed to a couple of times I did want to udpate and add that I did check out my old provider (aka: ****) and their rates have come down a lot since I first started hosting with them, and I though that their customer service was really good. I still like the feature-set of wh4l a little better so far, and the reliability of wh4l remains to be seen for me.
Thursday, June 10, 2004
 
Reporting Services Web Parts - Screen Shots


Here are a couple of early snapshots at our Reporting Services Web Parts for SharePoint Portal Services. The catalog browser web part allows you to surf through the rs catalog just like you would in report manager. The RS Report Viewer web part can either stand alone or be connected to the catalog browser web part to get information about which report it needs to display. John Welch and I are going to be publishing an article about this technology soon, as well as releasing most of the source.







RS Catalog Browser Web Part

Allows you to browse through an RS catalog.





RS Report Viewer Web Part

Allows you to see all of the details that you normally would about a report.




These web parts are based on a several controls that I have written which can be used outside of SharePoint to integrate Reporting Services into other ASP.NET applications.


Tuesday, June 08, 2004
 
.NET Delegates Simplified
First of all, think of delegates as a way to decouple objects from one another. When the President of the United States gets invited to a dinner with the NRA, he may not have time in his schedule to attend, but since this organization is important to him, he will send someone in his stead (a delegate) to attend on his behalf.

Now think about an ASP.NET appliction. The Page_Load(object sender, EventArgs e) functionof the ASP.NET Page class prescribes to a predefined method signature that is defined by the System.EventHandler delegate. The System.Web.UI.Page class does not implement the logic of the Page_Load event, you do that when the Page raises the Load event. So now, all the page class has to do is to notify everyone who is listening that the page has loaded via the Load event, and everyone can implement custom logic to react to the fact that the Load event has occured.

It may be simpler to look at how you might implement a delegate of your own. Lets say that you are creating a reusable login control that developers can drop on a page and use for them selves. You have implemented the logic to go out and check a security credentials data store, and you simply want to notify whoever is listening to the events on that control that the login either succeeded or failed.

The first thing you do is declare a custom delegate:


// define new delegate for my login event
public delegate void LoginEventHandler(object sender, bool loginSucceeded);


Now use that custom delegate when creating your login web control like this:

	
public class LoginControl : System.Web.UI.WebControls.WebControl
{
      // define loging event with new delegate type
      public event LoginEventHandler Login;

      private void Page_Load(object sender, System.EventArgs e)
      {

      }

      // create a handler method to raise the event when it occurs
      protected void OnLogin(bool loginSucceeded)
      {
            if(this.Login != null)
            this.Login(this, loginSucceeded);
      }

      // private method that contains my credential checking logic
      private bool DoLogon(string username, string password)
      {
            if(username == "jnathan" && password == "margaritaville")
                  return true;
            else
                  return false;
      }
}


Now, if I drop this control onto my page, I would subscribe to this event with the following line of code in the Page_Init event:

LoginControl.Login += new LoginEventHandler(this.LoginControl_Login);

You create the LoginControl_Login function in the code behind of the page that uses your Login control and respond to the event as you need to from there.

Check out the basis of the event model in .NET by researching the Observer design pattern [GOF].

Monday, June 07, 2004
 
Transit of Venus
Off my usual topics, but...

Tomorrow, Venus will pass between Earth and the Sun for the first time since 1882. The transit will not be visible in the US since it will occur before our sunrise, but there will be plenty of live web casts from Europe and Africa.

Astronomers of old used to use transits to take measures of our solar system such as the Astronomical Unit (AU - distance of Earth from the Sun). Even though we now have much more sophisticated techniques and technologies for performing these measurements, these events still capture the hearts and minds of astronomers all over the world due to their historical significance. Here is some more history on the significance of a planetary transit.

I found these words by American astronomer William Harkness in 1882 (a day before the last transit of Venus) to be particularly nostalgic:

"We are now on the eve of the second transit of a pair, after which there will be no other till the Twenty-First century of our era has dawned upon the earth, and the June flowers are blooming in 2004.... What will be the state of science when the next transit season arrives God only knows."

If only he could see what has been accomplished over the last 122 years.
Sunday, June 06, 2004
 
Syndication for SharePoint
I've spent most of my day today creating a syndication engine for SharePoint Portal Services and right now it is working, but there are a few things that are missing. The most obvious is a mapping between the data that is returned by the Lists.GetListItems(...) web method. Jonathan Malek has created a similar tool to mine and he uses an custom configuration section for his mappings which are template-based (great concept!), and general settings which include which sites to syndicate. With my syndication egine, one my main goals is to minimize the amount configuration that has to occur on the server. This will obviously mean that the mapping logic of my RSS generator will be compiled either as XML in a resource file or as a set of classes. Neither are as attractive as the configuration that Jonathan uses, but I think it'll be okay, and here's why: Since new syndication formats will be released less frequently than new sites will be created on a given company's SharePoint Portal Server, it should net less work on the back end.

Perhaps, the best of both worlds would be to use SpsRssGen's custom mapping piece with SPSyndication's setup.
 
XPath - Handling Default Namespaces in System.Xml
After being burned by this one time, I've learned my lesson... When you want to query and XML document that has a default namespace in the .NET framework you must use an XmlNamespaceManager to help with the queries, like this:

XmlNamespaceManager nsMan = new XmlNamespaceManager(xmlListCollection.NameTable);
nsMan.AddNamespace("ns", "http://schemas.microsoft.com/sharepoint/soap/");

XmlNodeList lists = xmlListCollection.SelectNodes("//ns:Lists/ns:List", nsMan);


The XmlNamespaceManager handles a variety of namespace-related tasks within the .NET Framework. The XPath specification makes no provision for the concept of a default namespace.

Friday, June 04, 2004
 
Demo Blues
This week I got pulled in at the last minute to do a demo at ITEC (which my company, MARINER) is sponsoring in Charlotte next week. I'll be demoing a product called Panorama NovaView 4.0 which is a Business Intelligence and Analytics server that sits on top of SQL Server Analysis Services. Should be a good change of pace, but it certainly has distracted me from my current project which is creating a custom delivery extension from Reporting Services to... Ha! You didn't think I was going to give it away that easily did you?

Well, if you read my blog, you can probably guess who the recipient of the delivery extension might be, but I need to make sure that I'm not divulging valuable Mariner IP first...
Thursday, June 03, 2004
 
Success in Software Development
"Success in software development depends on making a carefully planned series of small mistakes in order to avoid making unplanned large mistakes"

-Steve McConnell - Software Project Survival Guide (on planning a project)
 
Coffee with Maxim Karpov
I met Maxim Karpov and his managing partner (his lovely wife) & son for coffee today after he contacted me concerning an article I recently wrote for 15seconds.com. It was great to meet Max for the first time. He's got a great outlook on life and has some great ideas and insights on .NET development.

Looking forward to getting to know him better in the future!
 
Base Class for Creating Table Data Gateway Classes for .NET
One of the more interesting challenges of moving to .NET from VB6 is deciding to do with all of the object oriented features of a CLS-compliant language such as C# or VB.NET. When it comes to defining the architecture of your next .NET application, there are many things that must be considered, not the least of which is how you are going access relational data in a clean, consistent manner. Here is a generic SQL data provider that I have built to be subclassed by specific data providers that you may need within your application. For instance I could create an OrderDataProvider class that implements all of the necessary database interaction features that an Order class may need to use. Depending on the size of your application, you could even create one data provider class for all of your data interaction.

I used the SqlHelper Application Block from Microsoft within this class. A nice thing to be able to do would be to switch out the data provider on the back end of the domain specific data access classes (to facilitate changing the back-end database). I've also built an OracleDataProvider and an OleDbDataProvider base class that are basically exactly the same as the SqlDataProvider class.

Any and all feedback as to this method is more than welcome, so feel free to leave your comments on this. Here's the code, enjoy!


using System;
using System.Data;
using System.Data.SqlClient;
using Microsoft.ApplicationBlocks.Data;

namespace JNathan.Data.Sql
{
///
/// Summary description for DataProvider.
///

public abstract class SqlDataProvider : IDisposable
{
public SqlDataProvider()
{
this.ConnectionString = System.Configuration.ConfigurationSettings.AppSettings["ConnectString"];
}


private string connectionString = String.Empty;
protected string ConnectionString
{
set{this.connectionString = value;}
get{return this.connectionString;}
}


#region Common

protected SqlConnection Connection = null;
public SqlConnection Open()
{
try
{
Connection = new SqlConnection(this.ConnectionString);
Connection.Open();

return Connection;
}
catch(Exception exp)
{
throw exp;
}
}


SqlTransaction Transaction = null;
public SqlTransaction BeginTransaction()
{
if(Connection == null)
this.Connection = new SqlConnection(this.ConnectionString);

if(Connection.State != ConnectionState.Open)
this.Connection.Open();

try
{
this.Transaction = Connection.BeginTransaction();
}
catch(Exception e)
{
throw e;
}

return Transaction;
}


public void CommitTransaction()
{
if(this.Transaction != null)
this.Transaction.Commit();
}


public void RollbackTransaction()
{
if(this.Transaction != null)
this.Transaction.Rollback();
}


public void Close()
{
this.CleanUp();
}


protected void CleanUp()
{
if(this.Connection != null)
{
if(this.Connection.State != ConnectionState.Closed)
this.Connection.Close();

this.Connection.Dispose();
}
}


#endregion


#region ExecutionMethods

protected DataSet ExecuteDataSet(string sql)
{
if(this.Transaction != null)
return SqlHelper.ExecuteDataset(this.Transaction, CommandType.Text, sql);
else if(this.Connection != null)
return SqlHelper.ExecuteDataset(this.Connection, CommandType.Text, sql);
else
return SqlHelper.ExecuteDataset(this.ConnectionString, CommandType.Text, sql);
}


protected SqlDataReader ExecuteReader(string sql)
{
if(this.Transaction != null)
return SqlHelper.ExecuteReader(this.Transaction, CommandType.Text, sql);
else if(this.Connection != null)
return SqlHelper.ExecuteReader(this.Connection, CommandType.Text, sql);
else
return SqlHelper.ExecuteReader(this.connectionString, CommandType.Text, sql);
}


protected SqlDataReader ExecuteReader(string commandText, CommandType commandType, SqlParameter[] parameters)
{
if(this.Transaction != null)
return SqlHelper.ExecuteReader(this.Transaction, commandType, commandText, parameters);
else if(this.Connection != null)
return SqlHelper.ExecuteReader(this.Connection, commandType, commandText, parameters);
else
return SqlHelper.ExecuteReader(this.connectionString, commandType, commandText, parameters);
}


protected int ExecuteNonQuery(string sql)
{
if(this.Transaction != null)
return SqlHelper.ExecuteNonQuery(this.Transaction, CommandType.Text, sql);
else if(this.Connection != null)
return SqlHelper.ExecuteNonQuery(this.Connection, CommandType.Text, sql);
else
return SqlHelper.ExecuteNonQuery(this.connectionString, CommandType.Text, sql);
}


protected int ExecuteNonQuery(string commandText, CommandType commandType, SqlParameter[] parameters)
{
if(this.Transaction != null)
return SqlHelper.ExecuteNonQuery(this.Transaction, commandType, commandText, parameters);
else if(this.Connection != null)
return SqlHelper.ExecuteNonQuery(this.Connection, commandType, commandText, parameters);
else
return SqlHelper.ExecuteNonQuery(this.connectionString, commandType, commandText, parameters);
}


protected object ExecuteScalar(string sql)
{
if(this.Transaction != null)
return SqlHelper.ExecuteScalar(this.Transaction, CommandType.Text, sql);
else if(this.Connection != null)
return SqlHelper.ExecuteScalar(this.Connection, CommandType.Text, sql);
else
return SqlHelper.ExecuteScalar(this.ConnectionString, CommandType.Text, sql);
}


protected object ExecuteScalar(string commandText, CommandType commandType, SqlParameter[] parameters)
{
if(this.Transaction != null)
return SqlHelper.ExecuteScalar(this.Transaction, commandType, commandText, parameters);
else if(this.Connection != null)
return SqlHelper.ExecuteScalar(this.Connection, commandType, commandText, parameters);
else
return SqlHelper.ExecuteScalar(this.ConnectionString, commandType, commandText, parameters);
}


#endregion

#region IDisposable Members

public void Dispose()
{
System.GC.SuppressFinalize(this);
this.Close();
}


#endregion
}
}


 
SharePoint Terminology (does it confuse you sometimes, too?)
As I have learned to work with SharPoint a little over the past couple of months, I have learned rather quickly to take its terminology with a grain of salt. Some of the terms used for SharePoint can be very confusing at best, so here are some common terms and their meanings. I'll try to post more of these as I run across them in my day-to-day work. Here are some fundamental ones to start with, though:

STS - SharePoint Team Services: This term describes the first version of SharePoint that was built using ASP, IIS 5.0 and an ISAPI extension. This technology was meant to fill the same space that WSS (next term) is currently filling in version 2.0 of SharePoint, which is essentially list and document collaboration.

WSS - Windows SharePoint Services: Describes the second generation (v2.0) of SharePoint. WSS is based on ASP.NET and IIS 6.0 running on Windows 2003 Server. WSS provides end users with list and document management, but the data completely stored in a SQL Server database, and as such is much more flexible than v1. WSS also provides a rich framework for extending the functionality and including external data into SharePoint.

SPS - SharePoint Portal Server: SPS is a super set of Windows SharePoint Services. SPS provides aggregation and organization to a relatively unorganized set of WSS web sites. Portal Server installs on top of WSS and provides other services such as indexing and search services, personal web sites and user profiles. SPS would be used to aggregate large numbers of web sites that are created across an enterprise.

In my opinion, the terminology used in SharePoint development often gets in the way in much the same way as the term "Microsoft .NET" made life very confusing for some. But hey, once we get past some of the terminology difficulties, it's mostly down hill from there, right?

Wednesday, June 02, 2004
 
Going back to SharpReader
Just downloaded the latest version of SharpReader and I'm switching back from RssReader. I'm not having much luck exporting from RssReader and importing into SharpReader, though. Doesn't look like RssReader supports the UserLand OPML specification (someone please let me know if I am wrong).

This is the perfect opportunity to weed out and organize my subscription list. Mine isn't as bad as others that I have read about (100+ feeds), but I've still got about 50 feeds and I usually add 2 or 3 a week. I think I'm going to be more judicious from now on with my subscriptions because I seem to be getting a lot of white noise rather than news and valuable info...
 
Using RSS.NET within an ASP.NET page


Here's how simple RSS.NET is to use within an ASP.NET application. You can create a feed from virtually any data that exists within your enterprise. At this stage in the game, it is going to be more difficult to get users to begin using some type of aggregator/news reader than it is going to be to produce the news feeds! Thanks to George Tsiokos for putting this together.




RssChannel rssChannel = new RssChannel();
rssChannel.Title = "Jay's Web Log";
rssChannel.PubDate = DateTime.Now;
rssChannel.Link = "http://blogspot.jaynathan.com";
rssChannel.LastBuildDate = DateTime.Now;
rssChannel.Description = "Description of my channel...";

foreach(Item i in feedItems)
{
RssItem rssItem = new RssItem();
rssItem.Author = item.Author;
rssItem.Link = item.Link;
rssItem.PubDate = item.ModDate;
rssItem.Title = item.Name;
rssItem.Description = description;
rssChannel.Items.Add(rssItem);
}


RssFeed rssFeed = new RssFeed();
rssFeed.Channels.Add(rssChannel);
Response.ContentType = "text/xml";
Response.ExpiresAbsolute = DateTime.MinValue;
rssFeed.Write(Response.OutputStream);




Tuesday, June 01, 2004
 
Reporting Services Report Viewer Web Part
The project plan for my reporing services/sharepoint integration project has two main tasks on it for the week:

1. Add dynamic paramter support to Report Viewer web part.
2. Support drillable reports (was having a problem with this when simply rendering the report to the web part).

By dropping an <IFrame> onto the report viewer web part and passing it the reporting server url to access the proper report in the HTML Viewer I was able to fulfill both of these objectives in about 10 minutes. So now I'm using a combination of the reporting services SOAP interface and URL access for my reporting services web parts.

Here are the pros and cons of using URL access the way I have in my report viewer web part:

Pros
- Simple to implement (like I said, it only took me 10 minutes)
- Parameter input and validation are taken care of
- Paging and exporting is handled by default

Cons
- Bulky interface for a web part (needs to be in the center content pane to look decent)
- Right now the IFrame doesn't remember its state when post backs occur on the SharePoint page (will solve that by building an IFrame web control
- If the look and feel of SharePoint is customized, you may not want the default reporting services HTML Viewer interface on your page

Either way you slice it, less code, more productivity for me, and I have time to work on the cons listed. All of the cons are pretty much cosmetic and the pros are mainly functional.

 
RSS.NET
RSS.NET: An open-source .NET class library for RSS feeds

This will be very nice to have.
 
Generating RSS Feeds for SharePoint
Am going to start implementing a similar solution when I get back to work on Tuesday: Generating RSS Feeds for SharePoint Sites.

Allowing users to choose which content they get from SharePoint will begin to make it transparent. Will be posting more on this in the near future.

Powered by Blogger