Tuesday, April 22, 2008

DevConnections, Day 2

I went to a session early morning that focused on making Office Documents (Word specifically in this session) searchable via SharePoint Advanced Search.

The Advanced Search is only available in MOSS, not WSS (Windows SharePoint Services, the free version of SharePoint).

There is a core SharePoint concept of Content Type, which was defined as:

  • Collection of fields & non-field properties
  • Non-field properties are:

- Info Rights Management

- Workflow

- Template Files

  • Office Templates

- Contain all boilerplate text

- Can have fields & quickparts

- Quickparts can connect to a property.

Properties appear prominently for a document in Word 2007. There is a Document Information Panel (DIP) at the top of the document, below the ribbon.

Custom properties can also be accessed in Word 2003, though not as conveniently.

Word 2007: Insert -> QuickParts are put in as a data field. This way, the User will not have to make any extra effort to provide Metadata that can be used for searching later.

The Metadata will appear as columns on the columns of shared documents in SharePoint.

All Metadata items are added to “crawl properties”. In order to use for searching, will need to add them as “Managed Properties”.

Monday, April 21, 2008

Blogging from DevConnections

I'm at DevConnections in Orlando, Florida, learning about .NET 3.5, Visual Studio 2008, and SharePoint 2007.

The first breakout session I went to this morning was "Understanding How to Develop Office Business Applications from the Client to SharePoint and Beyond". This session was presented by Steve Fox from Microsoft.

Steve broke the Office Platform down into 4 general categories:

1. Servers

  • SharePoint
  • OCS
  • Exchange

2. Tools

  • VSTO
  • SharePoint Designer

3. Services

  • Search
  • Workflow

4. Applications

  • Excel
  • Word
  • Outlook, etc.

He stated that the goal of OBAs (Office Business Applications) is to unlock Line of Business Data and get it to the right people.

There was a bit of discussion about InfoPath vs. Smart Clients. Basically, InfoPath is a thin Web Service client, does not provide rich client functionality.

4 Key pieces when developing OBAs:

1. Smart Client

Smart Clients are Extensions for Office Client Applications. They can be developed in VS.NET. Visual Studio .NET has a Ribbon Designer. Custom Ribbons can be developed for Office 2007 applications. There is a Power Pack for VS.NET that includes a number of icons for custom Ribbons.

2. Smart Client Deployment

In earlier versions, deployment was a blocker. Now, with VS.NET 2008 and Office 2007, we can deploy a Smart Client from a share using ClickOnce Deployment. This will handle all the dependencies and put a simple Setup.exe on a share that will install the Smart Client. The User will need permission to get to the share. On the user's machine, he or she will need to go into MS Office -> Word Options -> Trusted Locations, and provide the shared drive as a trusted location. Then ClickOnce Deployment from the share can occur.

If there are thousands of users, the addition of the share to the trusted locations can be done as part of a startup or configuration script managed by IT.

3. Smart Client & SharePoint Integration

We went through a demo where we have a Document Library in SharePoint and a user comes in and clicks "New" on a particular document type. ContentType is used to integrate a Smart Client OBA with SharePoint.

4. SharePoint BDC (Business Data Catalog)

This appears to have been very problematic in the past (based on banter from the speaker and audience feedback). It was only touched upon briefly, but there is a BDC Editor that is a free giveaway in the SDK which can be used to create the App Def File (ADF).

Saturday, April 19, 2008

LINQ-to-SQL

Well, I've started building my first application using LINQ over the past 2 weekends (and quite a few evenings), and am reusing / converting old code I had from NHibernate-1.2.1.4000 GA to LINQ.

Thus far, my experience with LINQ is that there are some huge wins over NHibernate. However, there are definitely some drawbacks as well. Here are the pros and cons I have encountered while moving from NHibernate to LINQ.

Pros:

1. Tight integration between your Data Model / Class Diagram (which is presented in Visual Studio .NET 2008 in the form of a DBML file that you can edit via drag-and-drop) and your model classes.

In Visual Studio .NET 2008, to create a new set of LINQ to SQL classes, you select "Add New Item", and select "LINQ to SQL Classes" from the Visual Studio Installed Templates.


LINQ-to-SQL in Visual Studio

This will create a DBML file, and corresponding model classes. It will also create your DataContext, which is analogous to a Hibernate Session.

All you have to do to create your model classes is open up the Server Explorer, make a connection to your database (the connection string is then stored in a Settings.setting file, which is used by the Application during runtime to connect the DataContext to the database, and begin dragging and dropping tables from the Server Explorer into your DBML file's workspace.



2. Strongly-typed Queries. Actually, this is a bit of a double-edged sword. On one hand, it's nice to have your query syntax integrated as part of the language and to have query syntax errors flagged at compile-time instead of runtime.

On the other hand, it makes it more difficult to do queries that are built during runtime.

With NHibernate HQL (and ADO.NET SqlCommand), the query is simply a string. Consequently, it is easy to build up this String dynamically during runtime, i.e.



if (profileSearch.FindUserFlag)
{
whereClauseBuilder.Append("AND upper(user.user_name) like @userName ");
parameters.Add(new SqlParameter("@userName", profileSearch.UserName.Trim().ToUpper() + "%"));
}

You don't have this dynamic ability to build up your query using LINQ.

I have read that dynamic query construction can be done via expression trees, but it sure is a lot more complicated then just doing an IF statement when appending where clauses to your StringBuilder.

3. Lazy-loading (termed "Delay Loading") on a per-property basis. This is something that I really wanted in NHibernate for the following reason. I do a left outer join from my User to Profile on a table. The Profile table has a column "About Me", which is of type NTEXT and has the potential to be very large. For something like a Profile Summary (this is a snapshot of the Profile that does not have the full details and definitely should not have "About Me", I do not wish to pull in a potentially large column).


4. Elegant syntax without a lot of scaffolding code in your DAO. Here is one of my DAO methods pre-LINQ conversion:



///
/// This method will retrieve StateProvince objects by language.
///
///
/// The language for which we wish to find
/// all State/Province definitions.
/// Strongly-typed List of StateProvince objects, ordered by
/// CountryCode and DisplayOrder.
public IList RetrieveStateProvincesByLanguage(String language)
{
// Strongly-typed List of StateProvince objects.
IList stateProvinces;
ISession session = NHibSupport.SessionFactory.OpenSession();
StringBuilder hql = new StringBuilder();
// Define HQL Statement
hql.Append("from StateProvince as stateProvince ");
hql.Append("where stateProvince.Language = :language ");
hql.Append("order by stateProvince.CountryCode, stateProvince.DisplayOrder");
IQuery query = session.CreateQuery(hql.ToString());
query.SetParameter("language", language.ToLower(), NHibernateUtil.String);
stateProvinces = query.List();
log.Info("Found " + stateProvinces.Count + " State/Provinces for language '" + language + "'.");
session.Close();
return stateProvinces;
}

And here is the same DAO method after LINQ conversion:



///
/// This method will retrieve StateProvince objects by language.
///
///
/// The language for which we wish to find
/// all State/Province definitions.
/// Strongly-typed List of StateProvince objects, ordered by
/// CountryCode and DisplayOrder.
public IEnumerable RetrieveStateProvincesByLanguage(String language)
{
PersonQDataContext dataContext = DataContextFactory.Instance.GetDataContext();
return from stateProv in dataContext.StateProvinces
where stateProv.Language == language
orderby stateProv.CountryCode, stateProv.DisplayOrder
select stateProv;
}

Cons:


1. From my research to-date, there is no way to update the data model based on table changes. If you add a new column to the table, you have to manually right-click on the class that represents the table, choose "Add Property", and add the property.

2. Does not convert common database column naming conventions to common C# Property conventions. I didn't think it would be that difficult to have the column about_me in my profile table to come in as Profile.AboutMe. However, it comes in with the column's actual name, so I have a property Profile.about_me. This is just a minor annoyance, but it means that every time I drop a table into the data model, I have to manually go through all the properties and name them something sane.

3. ReSharper does not currently support LINQ and C# 3.0 language constructs.

Thursday, April 10, 2008

YetAnotherForum.NET (YAF) Upgrade and my approach to maintaining changes to Open Source Applications

I'm upgrading my YetAnotherForum.NET instance on my production server from 1.9.1.6 to 1.9.1.8.

This has been a surprisingly challenging process that has taken me 2 evenings now.

One of the problems I've run into with my personal development projects is how to mod an open source application like YetAnotherForum.NET or BlogEngine.NET and still be able to upgrade.

Here's my general approach to managing changes to open source software:

  1. Only modify core classes WHEN ABSOLUTELY NECESSARY. Most applications (like both YAF and BlogEngine.NET) support themes. In my "International" Theme for BlogEngine.NET, I've injected a lot of code into the Master Page Codebehind. If you can stick to placing code in plug points like a theme, you don't have to deal with the headache of re-applying your changes manually when upgrading the application.
  2. I create a Subversion project for my modified files, and a README that describes the version against which the modified files were made. It also describes a series of steps for getting back to a buildable version of YAF 1.9.1.6 + MODS. I also store off a downloaded ZIP file of the application source version against which I made modifications, just in case it becomes difficult to obtain this application version in the future.
  3. When upgrading to a new version (going from YAF 1.9.1.6 to 1.9.1.8), I take a pure copy of the source from 1.9.1.6 and 1.9.1.8. I use WinMerge to diff the two files. If they are the same, great, work is done for that file. If they are different, then I take the newer version and manually re-apply my changes into that version.

I can't help but wonder if there's a better way to do this. I probably should be leveraging Subversion's merging capabilities, but at least this process has worked for me (albeit a bit painful).

Sunday, April 6, 2008

i18n for Chinese in .NET Framework 3.5

Well, I upgraded my development and production servers to .NET Framework 3.5.

I'm really not taking advantage of the .NET Framework 3.5 additions (lambda functions, LINQ) yet. Also, I haven't made a decision yet on LINQ vs. NHibernate. However, I've already made a significant time investment in NHibernate, so it would really take a lot of convincing to move to LINQ, unless there is a clear path for doing so from NHibernate.

I had noticed that on all the MSDN pages there was a little section that appeared as follows:





Figured it would be best to get on the latest version.

For the most part, the upgrade is painless. It was just a matter of downloading / running the .NET Framework 3.5 installer dotNetFx35setup from Microsoft. No code changes were required. However, one change I feel I should make, even though everything is still running is refactoring my project so that it uses the latest (and correct) Neutral Cultures for Simplified and Traditional Chinese. Here are the entries for both from the latest MSDN System.Globalization.CultureInfo documentation.

Culture/Language Name

Culture
zh-Hans Chinese (Simplified)
zh-Hant Chinese (Traditional)

I've had a bit of discussion with Shawn Steele on his blog about the latest .NET Framework changing Simplfied from zh-CHS to zh-Hans and Traditional from zh-CHT to zh-Hant. Although the old zh-CHS and zh-CHT are still supported, I don't want to use a deprecated method for getting to my RESX files.