Skip to main content

Matt Goebel's Blog

Go Search
Home
  

Random thoughts and experiences on .NET, SmartClient/ClickOnce development, best practices and management.
Upcoming Presentations

I'll be presenting twice over the next week on two different topics. In case you are in Louisville, KY or Fort Wayne, IN…see below.

Kentucky Day of .NET – 9/6/2008 (see site for details)

Topic: Building Dynamic Queries at Runtime with LINQ

"LINQ at development/compile time is great, but what if you need to allow the user to select criteria, filter results, and change that query at runtime?  This presentation will explore three different approaches to building "dynamic" LINQ queries at runtime.  The advantages and disadvantages will be discussed through the use of discussion and code demonstrations.  A beginner level understanding of LINQ is suggested."

.NET Users of Ft. Wayne – 9/9/2008 (see site for details)

Topic: Smart Client Applications

"Discuss the pros and cons of Smart Client development from a technical and business point of view."

Finding Northwind…

For it being one of the most commonly referenced and used databases out there have you ever tried to find the creation scripts for the Northwind database? No easy task as I found out today. I have SQL 2000, SQL 2005, SQL 2005 Express, VS 2003, 2005 and 2008 installed on my laptop (the joys of supporting old applications) and to my surprise I didn't have a single instance of the Northwind DB when I went to look for it. At first I didn't think it was a big deal and I would just quickly go download the scripts…surely they had to be on Microsoft's website…well sort of.

After several Google searches, searches on Microsoft's site and reading some blogs I was finally able to find the 2000 version off this blog post.

I still can't believe that the create script isn't more accessible than this though. I guess I could have found the installation disk for any version of SQL and installed from there but who has those laying around all the time? I surely don't. This is a lesson though…I guess I'm going to leave that "install samples and documentation" checkbox checked from now on. : )

‘Dynamic’ Expression Predicates in LINQ

Before I dive into the meat of this entry I would like to make the comment that if you haven't used LINQ yet, it's amazing. Assuming that you are reading this article because you do use LINQ and are trying to build a dynamic where clause or the such, you are aware of this.

The project uses LINQ to SQL and we're taking effort to stay consistent by using LINQ whenever possible. (Which so far is all the time we're finding out as LINQ hasn't let us down yet) The problem was a fairly simple and mundane. A user can select one or many States from a list and a second list is populated with all the cities in the states that the user selected. In theory, or by using a simple SQL query, this would be pretty simple. A quick loop and StringBuilder would be all that is needed to dynamically build the needed query string to return all the cities for the selected states. Since we were sticking to LINQ though, we had to find a different way…the LINQ way.

Initially, I thought this would be a piece of cake, mainly because everything thus far using LINQ was. After some initial searches on the Internet I was turning up only solutions that seemed far too complicated, and didn't really fit in with the sexiness of LINQ. One particular solution that kept coming up (could have been due to my search terms) was O'Reilly's (Joseph Albahari) solution discussed in the book "C# 3.0 In a Nutshell". This PredicateBuilder solution would have probably worked, but like I mentioned, and you can see, it wasn't as simple as I had become accustom to with LINQ…there had to be a better way.

My next logical (or at least in my brain :P ) was to simply put a LINQ statement into a loop, evaluate it for each selected state and concatenate the returned list of cities along the way. There are a couple key things wrong with this approach I won't get into, but again it wasn't fitting into the simple and easy mantra of LINQ….there still had to be a better way.

Well it turns out there is. I have to credit Adam Woods, a coworker, for figuring out the final solution, but it is so simple I had to share as it demonstrates the cool power of LINQ. LINQ has the ability to include outside List, not the List or data source you are "querying" off of, but additional List. The below code snippet demonstrates what I'm talking about.

//Gets all selected states from the lstStates listbox

List<string> states = new List<string>();

 

foreach (ListItem item in lstStates.Items)

{

    if (item.Selected)

{

    states.Add(item.Value);

}

}

 

//Gets all Cities from the database where the LocationStateID is one of the selected states from above.

var cities = from c in db.Cities

where states.Contains(c.LocationStateID.ToString())

orderby c.LocationStateID

select c.LocationCity;

 

lstCities.DataSource = cities;

This snippet demonstrates when you would need to build a dynamic WHERE clause with a bunch of OR evaluations. (If you are familiar with LINQ you wouldn't have to extrapolate this concept far to build a WHERE clause with AND and OR evaluations dynamically.) It also demonstrates the powerful ability LINQ has, as well as breaks down some mental walls on how you think about building LINQ statements and where LINQ can be used. Hopefully this helps or enlightens.

Setting up ADO.NET Data Services (a.k.a. “Astoria”)

Recently I started educating myself more on the emerging ADO.NET Data Services Framework. In doing so I, of course, had to setup an environment to play with the new framework. If I read the entire download page for the ADO.NET Entity Framework Tools I would have caught all the needed prerequisites but since I simply clicked to download I missed the following requirements.

  • Supported editions of Visual Studio 2008 RTM
  • XML Editor QFE – VS2008 Extension (VS90-KB945282.exe)
  • ADO.NET Entity Framework Beta 3 – Found HERE

I wasn't taking notes while going through the install process but if my memory serves me right there is an order that they need to be installed in and I believe that order is as follows.

  1. VS 2008, obviously…
  2. XML Editor QFE
  3. ADO.NET Entity Framework Beta 3
  4. ADO.NET Entity Framework Tools
  5. Finally, to get the ADO.NET Data Services Framework, ASP.NET 3.5 Extensions Preview

Why this information isn't on the ASP.NET 3.5 Extensions Preview page I don't know.

The ADO.NET Data Services Framework is included as part of the .NET 3.5 SP1 Beta 1 and Visual Studio 2008 SP1 Beta 1. So if you are using both Beta's you shouldn't have to jump through the hurdles above. More details and information about the framework can be found using the two links below. They've both been very helpful.

http://astoria.mslivelabs.com/

http://blogs.msdn.com/astoriateam/

Quick Wizard Functionality

I've recently come across 3 projects that needed 'Wizard-like' functionality. What I mean by this is that there was a need to guide the user through a process of either data entry or questions to get to an end result. This is pretty common functionality but there are numerous ways to implement, and the effectiveness of a certain implementations is sometimes questionable. I'd like to offer a walkthrough on one of these approaches I've been using recently to at least offer a jump off point to someone that could be solving the same issue.

Prerequisites: (What I'm using and assuming you know…)

  • VS 2008 (VS 2005 would be fine as well)
  • Infragistics v7.2 or later 3rd party controls
    • There are additional features in the Infragistics version of the TabControl control that I take advantage of. This functionality is still possible with the generic, "out of box", .NET TabControl control, but use of the Infragistics control makes life easier.

In a new Windows Forms Application, on the Form, add the UltraTabControl from the Toolbox. Set the 'Style' property to "Wizard". This removes the classic "tab" look from the tab control. We'll be able to use button navigation now that is typically associated with a wizard. Following widely accepted design, I place three buttons the "Shared controls page" of the UltraTabControl, "Cancel" on the bottom-left, "Back" and "Next"/"Finish" on the bottom-right.

Since the Back button won't be needed on the first tab/step of the wizard I set the Visible property to false. Next I implement the events for the Back and Next buttons.

private void cmdNext_Click(object sender, EventArgs e)

{

if (tbcWizard.SelectedTab.Index == tbcWizard.Tabs.Count - 1)

{

// Save/Final button was clicked (last step)

return; // May or may not be needed, depends on above

}

 

// Optional, implement if you need to do validation on a step or process

// information before reaching the end of the wizard

switch (tbcWizard.SelectedTab.Index)

{

case 0: // Step 1

break;

case 1: // Step 2

break;

case 2: // Step 3...

break;

default:

break;

}

 

if (tbcWizard.SelectedTab.Index < tbcWizard.Tabs.Count - 1)

{

// Move to the next step

tbcWizard.SelectedTab = tbcWizard.Tabs[tbcWizard.SelectedTab.Index + 1];

 

// Optional, in this case there is a label on the Shared page that is

// updated with the current tab/steps text...helpful for the user

lblStepHeader.Text = tbcWizard.SelectedTab.Text;

 

// Check if the tab is the last one, change "Next" text to "Finish"

if (tbcWizard.SelectedTab.Index == tbcWizard.Tabs.Count - 1)

cmdNext.Text = "Finish";

else

cmdNext.Text = "Next";

 

// Check if the tab is the first one, disable the "Back" button

if (tbcWizard.SelectedTab.Index == 0)

cmdBack.Visible = false;

else

cmdBack.Visible = true;

}

}

private void cmdBack_Click(object sender, EventArgs e)

{

if (tbcWizard.SelectedTab.Index == 0)

return;

 

// Move to the previous step

tbcWizard.SelectedTab = tbcWizard.Tabs[tbcWizard.SelectedTab.Index - 1];

 

// Optional, in this case there is a label on the Shared page that is

// updated with the current tab/steps text...helpful for the user

lblStepHeader.Text = tbcWizard.SelectedTab.Text;

 

// Check if the tab is the last one, change "Next" text to "Finish"

if (tbcWizard.SelectedTab.Index == tbcWizard.Tabs.Count - 1)

cmdNext.Text = "Finish";

else

cmdNext.Text = "Next";

 

// Check if the tab is the first one, disable the "Back" button

if (tbcWizard.SelectedTab.Index == 0)

cmdBack.Visible = false;

else

cmdBack.Visible = true;

}

The inline comments do a decent job of explaining what this simple chunk of code is doing. I'd be more than happy to provide a more robust and functioning solution if you use Infragistics controls and are interested. Please leave a comment and I'll be happy to send it along. So while very simple, this approach has been effective and efficient in all my latest needs to implement a wizard. Enjoy!

Indy DevCares

Tomorrow at the Indianapolis Microsoft office I will be presenting the May (even though it is in June) DevCares with a coworker, Neal Schneider. The details and material that will be covered can be found at the below link.

http://www.msdnevents.com/devcares/

File Associations for a ClickOnce Application

This bit of information came across my email yesterday and thought I would share. In my opinion this is a huge step forward for ClickOnce deployment. Hopefully VS2008 SP1 is full of these little goodies. =) The BETA is available now to play with.

How to: Create File Associations For a ClickOnce Application

VS 2008 Report Viewer Redistributable

In a recent post I provided a link to the Report Viewer redistributable and referenced another article that showed how to "hack" the EXE. The link points to the VS 2005 redistributable and so here is the link for the VS 2008 redistributable if you are running into issues with the older one.

http://www.microsoft.com/downloads/details.aspx?FamilyID=CC96C246-61E5-4D9E-BB5F-416D75A1B9EF&displaylang=en

Report Viewer – “More than one data set” Error

As the previous post suggest I'm currently spending some of my time around implementing the Reporting Services Report Viewer into a smart client application that was recently migrated from .NET 1.1 to 2.0. Yesterday we ran into an issue that didn't seem to have much information about it on-line. It was easy to diagnose and fix once taking a look under the covers, but for those weekend warriors of coding I hope you find this helpful.

The error you get when compiling is "More than one data set, data region, or grouping in the report has the name 'Assembly Name'. Data set, data region, and grouping names must be unique within a report." and is due to duplicate data sets in the .rdlc file you created for your report. In my case this occurred when I was using an object data source.

By right-clicking the .rdlc file in the Solution Explorer, selecting Open With, and then XML Editor you'll be able to see the XML that makes up the report you are designing. By collapsing the "DataSet" elements found within the "DataSets" element you'll quickly see the duplicates. By expanding the elements back out, scrolling down and finding the "rd:ObjectDataSourceType" element you'll see what is going on.

In the .NET 1.1 project we had used the AssemblyInfo.cs file to assign a version number of 1.0.*, the default if I remember correctly. Since we were not using the version number for anything and actually controlled versioning a different way this was never changed.

Therefore, each compile created a new ObjectDataSourceType because the version number kept incrementing. The quick and easy way to fix is to make the version number static and remove the duplicate dataset. So not very tricky or hard, but incase you were getting stumped I hope this helps out.

Report Viewer Redistributable Hack

I recently was able to get the approval to move an old project's reporting "engine" from a hacked together combination of grids, print documents and drawing to using Reporting Services' Report Viewer. The idea, as this is only one change in a set, is to get the application slimmed down to only requiring the .NET 2.0+ Framework. By removing other dependencies (in this case by moving to Reporting Services we're able to get rid of a 3rd party PDF driver) the end goal is someday to be able to move this application to ClickOnce deployment.

Today I ran into an issue with a user testing an application that uses the Report Viewer. There were DLLs missing, DLLs that I thought and assumed were installed by the .NET 2.0 Framework. After some digging around I found that this is fairly common, or at least it seems to be by the number of forum postings about the matter. There is a redistributable for the Report Viewer, but it drops files in the GAC and since that requires Admin rights this wasn't good news.

After some digging around I stumbled upon a great blog post by a Dennis J. Bottjer, a ASP.NET MVP. Below are the steps he laid out to get around needing Admin rights while still being able to deploy using ClickOnce.

The following steps were performed by co-worker John Arcidiacono and myself [Dennis J. Bottjer] to successfully solve the challenge described above.

  1. Download Report Viewer Redistributable
  2. Use favorite Zip Utility to extract the MSI.exe to a folder of your choice
  3. Find the file ReportV1.cab in extract folder from step #2
  4. Use favorite Zip Utility to extract ReportV1.cab to a folder of your choice
  5. Open the new folder from step 4 and find 4 files
  6. Rename: FL_Microsoft_ReportViewer_Common_dll_117718_____X86.3643236F_FC70_11D3_A536_0090278A1BB8 To Microsoft.ReportViewer.Common.dll
  7. Rename: FL_Microsoft_ReportViewer_ProcessingObject_125592_____X86.3643236F_FC70_11D3_A536_0090278A1BB8 To Microsoft.ReportViewer.ProcessingObjectModel.dll
  8. Rename: FL_Microsoft_ReportViewer_WebForms_dll_117720_____X86.3643236F_FC70_11D3_A536_0090278A1BB8 To Microsoft.ReportViewer.WebForms.dll
  9. Rename: FL_Microsoft_ReportViewer_WinForms_dll_117722_____X86.3643236F_FC70_11D3_A536_0090278A1BB8 To Microsoft.ReportViewer.WinForms.dll
  10. Copy these dlls to your smart client project and reference them
  11. Now they will be part of the Smart Client's Build Output and Click Once Deployment
1 - 10 Next

 ‭(Hidden)‬ Admin Links