Pages

-->

Thursday, December 9, 2010

Tip On Preventing Web Discover Service Error

I have been using .asmx web services for about 7 years now, and have not run into too many issues while consuming valid URLs in VS.NET. And although I am not creating new .asmx services today because I prefer (as should you) WCF services, I still have some that have yet to be converted.

One service in particular was giving me issue upon consuming in VS.NET 2010 with the following error message in a dialog box named "Web Discovery Service":

"Unable to download following files from . https://myservice08.mycompany.com:3567/MyService.asmx?wsdl Do you want to skip these files and continue?"

Well I found very little documentation on this issue (actually only 2 sites on Google), but I did manage to find the solution which on the surface appears quite obvious after explanation.

It started when I moved an upgraded copy of this service (Framework 4.0, IIS 7.5, etc) to a new server. However I had the "myservice" SSL certificate imported and applied to this service in IIS that was named "myservice08" (name was only for testing). I knew ahead of time that I would get certificate mismatch errors ("myservice" <> "myservice08") because the SSL cert didn’t match the sites host name, but I figured for testing I could bypass and be OK. Well this ended up being a showstopper, because not only did I get the warning about the SSL certificate not matching the host name upon consumption, I got the other error reported above as well (which is not nearly as indicative of the SSL mismatch being as issue). In fact I was really thrown off because the .asmx service would still come up in the browser showing the wsdl just fine.

So if you ever are working with legacy .asmx services and are receiving the same error I did upon consuming within VS.NET, make sure that the SSL cert name and the hostname of the service are identical.

Tuesday, November 30, 2010

How To Use Impersonation in ASP.NET Configuration With IIS 7.5 And Integrated Pipeline

While working on a few applications that have Impersonation="True" in my ASP.NET configuration, I ran into the following error upon deployment to an IIS 7.0 or 7.5 server:

"HTTP Error 500.24 - Internal Server Error An ASP.NET setting has been detected that does not apply in Integrated managed pipeline mode"

This is essentially a 'flag' warning that is raised when using Impersonation in your web.config and deploying to an IIS 7.0+ server using an Application Pool with Integrated Pipeline. If this configuration was mistakenly added, you can remove the configuration or set it to 'False' to fix the problem. There are (2) solutions for those that do need Impersonation="True".

1. Switch the Application Pool on IIS to use a 'Classic' Pipeline.
2. Add the following configuration to your web.config under the <system.webserver> section:

<system.webServer>
<!--When using 'Integrated Pipeline' on IIS on the server, and if your application does not rely on impersonating the requesting user in the 'BeginRequest' and 'AuthenticateRequest' stages (the only stages where impersonation is not possible in Integrated mode), but still requires Impersonation in other areas of the application, ignore this error (500 - Internal Server Error) by adding the following to your application’s web.config-->
<validation validateIntegratedModeConfiguration="false"/>
</system.webServer>

I actually used the explanation from the following helpful IIS Blog link (here) to create the XML comment above the configuration, and I reccomend reading it for a more in depth explination direct from an IIS Program Manager.

Monday, November 22, 2010

Book Review: Professional ASP.NET Design Patterns By Scott Millett

Let me begin by stating that Professional ASP.NET Design Patterns is a fantastic book that was worth every minute I spent reading it. The author, Scott Millett, is a great community leader and extends himself in several ways including spending time on the forums contributing to others into his strong insight of Domain Driven Design, Architecture, and Design Patterns. He has extended that helpfulness by writing this book that takes a dive into Design Patterns and Architecture from an ASP.NET UI centric view. However I would not get too fixated on the 'ASP.NET' in the title as probably more than half of this book could just as well have been called "Professional .NET Design Patterns" as it provides design patterns that are truly useful to all types of .NET applications once moving below the topmost UI layer. There are several chapters devoted to ASP.NET patterns including MVC which makes this still focused mostly on ASP.NET, but I would still recommend this book to WinForms and other SmartClient developers as well.

This book's target audience is broad and could reach to several different types of software engineers. It is probably suited best for Senior Engineers, Architects, Leads, or generally seasoned developers. It is not really an introductory book (this is a good thing; there are plenty of those books out there already), so if you don't know what acronyms like OOP, OOD, UI, BLL, or DAL mean at a minimum already then you may want to read something along the lines of an introduction to Object Oriented Programming book 1st to gain some traction. This is however a terrific book for those that do have a lot of experience with a traditional 3-layer logical architectures, and are looking to bridge the gap to more sophisticated architectures using Domain Driven Design and other implementations of either Martin Fowler's or the GoF design patterns within.

Scott does a wonderful job of layering the book (chapters) as you would an application. Each chapter takes either a single layer or design pattern and goes into detail on its responsibilities, relationship to other layers, and implementation with easy to follow along code samples. In fact I highly recommend downloading the code samples from the WROX website (WROX Code Download) The entire set of code samples are in C#, but don't let this slow up any VB.NET devs out there. I am actually a VB.NET developer (C# in the past) but we all know that you don't get too far in this industry without reading both so this should not be any problem.

The 1st third of the book (roughly) concentrates mostly on individual logical layers of an application and how they work together to build an application. Within each layer, there are examples of Design Patterns (both Fowler and GoF) that are used and shown why they are useful within that particular layer. There is also a section on IoC and DI which I really enjoyed and are reoccurring patterns in the layers throughout the book. The 2nd third of the book concentrates mostly on ASP.NET architectures and techniques like MVC, MVP, and AJAX patterns. The last third is devoted to a case study example that uses the knowledge gained from the previous chapters. The book reads and flows extremely well and was one of the reasons I enjoyed reading it so much.

I will also note that this is a great book for those of you familiar or have read the GoF book Design Patterns Elements of Reusable Object Oriented Software. As we all know code examples used to conceptually explain design patterns are not always critical, but Scott's book puts a fresh '.NET' perspective on several of the GoF patterns which is really nice. This helps to see how these patterns apply directly in .NET instead of taking the SmallTalk or C++ examples from the GoF book and translating them into .NET.

The book wraps up with a full case study example putting all of the chapters together (Agath's e-commerce store). This again strengthens the flow of the book with an extended example using everything learned from the previous chapters, This solution is included in the 'Chapter 14' folder in the downloadable code and is a nice reference to show everything from the book.

Well I will wrap this review up by saying this book is one for the shelf of 'Top Reference' books that go right next to the development machine. This is one of those books that you think, "How do I do that in the Repository Layer...", and then pick up the book to get a refresher. I would definitely recommend this book and keep an eye out for future books from Scott Millett. Nice Job!

Monday, November 15, 2010

Day 2: Visual Studio Live! Orlando 2010

Another beautiful and informative day here in Orlando, FL at Visual Studio Live 2010. I would have to pull my old conference materials for comparison, but I really like the 4-5 class per day format. I seem to remember 6 or more classes per day in the past which can maker for a really long day especially if learning new concepts. However, this format has been perfect.

Had great classes with industry leaders like Leonard Lobel, Gus Emery, and Jason Bock. I also must note that one of the best byproducts of these conferences is the side conversations and networking that occurs informally. Everyone at these conferences is here for one reason: they are the leaders of their respective teams or companies in reference to software development and related ares and love what we do. For this reason, everyone is eager to chat, talk, and discuss their jobs, work, applications, etc which is quite nice.

One new product I learned of today was Microsoft WebMatrix. http://www.asp.net/webmatrix This is an all inclusive tool to rapidly build web applications using shared pre-built components built by the development community, and hosted on IIS Development Express. What I was instantly amazed by was how Rabi Satter (@rsatter42) wrote in '1' line of code (no joke) a Twitter client that searches for a keyword that took me probably 500-1000 lines of code (between Silverlight client and WCF proxy service) to do essentially the same thing. OK no denying that is nice, and the technology offers more of the same (Twitter, Facebook, Wiki, Blogs, PayPal, etc). It is still in Beta and will probably not revolutionize web development and having everyone run away from their current platforms, but it certainly will have a presence.

Lastly, I had the unique and great pleasure of dining this even with 2 prominent leaders of our industry, Rocky Lhotka and Jason Bock. Seeing how I have listened to them speak, read books from them, Twitter feeds, blogs, etc. it was a real nice experience to hang out with not only some top echelon developers, but also some just really cool guys. Everyone at dinner liked the band Rush (hey, who doesn't) which made for some good conversation as well. I hope to hook up with you guys more in the future and highly recommend those of you in the industry to check these guys blogs out as well: http://www.lhotka.net/ and http://www.jasonbock.net/JB/Default.aspx

Sunday, November 14, 2010

Day 1: Visual Studio Live! Orlando 2010

This is my 3rd Visual Studio Live (formally VSLive!) and I am enjoying it as much as ever. If you ever get the chance to attend one of these events (Orlando, Las Vegas, Redmond, etc.) try and do it. The information is terrific and the insight from experienced presenters, peers in our industry, and general networking make it all worth it.

Today I attended the workshop named "Making Effective Use of Silverlight and WPF" with Rocky Lhotka (@RockyLhotka on Twitter) and Billy Hollis (http://slmasters.net/). Talk about (2) all-stars of our industry. Billy's knowledge of UI design with Silverlight and WPF (and other UI technolgoes too WinForms, etc) combined with Rocky's experience being an experienced OOP and Architecture engineer (research CSLA.NET) makes for a wonderfully informative day.

I had the pleasure of being on the front row for the presentation and enjoyed the back and fourth complimentary knowledge of .NET technologies. Unfortunately for me, I asked a question regarding a .xap being downloaded to the client, potentially exposing sensitive code (for a small % of shops, or developers still using antiquated architectures with wayyy too much code in the UI including sensitive connection strings, password, etc.) and made me sound like a noob. People were coming up to me at break telling me, "Just leave that code on the server and make a WCF call..." Yep, I know. Just was trying to raise a quick conversation for ex-web developers to understand Silverlight's deployment. Ow well. :)

Rocky (4th time attending presentations) and Billy (2nd time hearing presentations) can offer a lot of knowledge for those wanting to a lot about A+ presentation design (Billy) or OOP and .NET Technologies (Rocky) and should be checked out this week. If you are attending Visual Studio Live I highly recommend any presentations by either these (2) presenters. Nice job gentlemen.

Monday, November 1, 2010

Download FTP Files Using FTP Over SSL (SFTP) in .NET

Recently with the help of some folks over at StackOverflow I got pointed in the right direction for working with files on a FTP server and communicating with it via FTP over SSL. I had success using FTP client software to view and download files, but needed the code to do this programmatically in .NET. It is essentially easier than I thought and just casts a 'WebRequest' object to an 'FTPWebRequest' object and then the methods called are identical to those used in a traditional WebRequest call.

The main 3 obstacles I ran into where the following:

- Getting the Object parameter values set properly to match those of the client software I was already having success connecting with.
- Validating the Server's SSL Certificate programmatically.
- Getting around a 550 error I kept receiving when accessing the file directly.

So let’s get right to all of the code:

'Using an anonymous method, check to make sure the SSL Certificate being served up is the correct one.
'This method of inlining the validation is good for a simple single certificate check; for more involved
'checking you will want to use the line below and long hand the method call.
ServicePointManager.ServerCertificateValidationCallback = Function(obj As [Object], certificate As X509Certificate, chain As X509Chain, errors As SslPolicyErrors) (certificate.Subject.Contains("CN=ftp.domain.com"))
'Wire up a method that will be called upon creating the FTtpWebRequest and will validate the SSL Certificate
'ServicePointManager.ServerCertificateValidationCallback = New System.Net.Security.RemoteCertificateValidationCallback(AddressOf CertificateValidation)

'Create an FTPWebRequest providing the URI of the FTP server to connect
Dim Request As System.Net.FtpWebRequest = DirectCast(WebRequest.Create(New Uri("ftp://sftp.domain.com/MyFiles/Folder1/MyFile.txt")), System.Net.FtpWebRequest)
'Set that we will be downloading a file
Request.Method = WebRequestMethods.Ftp.DownloadFile
'We are going to enable SSL for the communication with the FTP server as required by the remote server.
Request.EnableSsl = True
'The credentials needed to log onto the server
Request.Credentials = New Net.NetworkCredential("UserName", "Password")
'Use a 'Passive' data transfer process. This setting was the same in my FTP client software.
Request.UsePassive = True
'Create a 'Reponse object getting the downloaded file
Dim Response As System.Net.FtpWebResponse = DirectCast(Request.GetResponse(), System.Net.FtpWebResponse)

'Read the File using a StreamReader:
Dim sr As New StreamReader(Response.GetResponseStream())
To solve my 1st issue I mentioned (get object parameters set), I essentially found and configured the FTPWebRequest object to mimic an existing workable connection from my client software. A lot of the FTP 'lingo/jargon' is identical between .NET and FTP client software. I recommend connecting to the FTP server as I did 1st using client software (i.e. FileZilla) to make sure you do have everything correct before running in circles with code that would never work because you don’t have permissions anyway.

Next I had to validate the servers SSL certificate programmatically. I was able to use a Lambda Expression using an anonymous method to to create a delegate that would check to make sure the proper SSL Certificate was being presented. If the validation logic is more elaborate, you can use the commented out call to wire up an event to a method named 'CertificateValidation()' by assigning the ServerCertificateValidationCallback on the ServicePointManager object.

Private Shared Function CertificateValidation(ByVal sender As Object, ByVal cert As X509Certificate, ByVal chain As X509Chain, ByVal [error] As System.Net.Security.SslPolicyErrors) As Boolean

'Make sure the correct certificate is being used:
If cert.Subject.Contains("CN=ftp.domain.com") Then
Return True
Else
Return False
End If

End Function
Now either method could technically just return 'True' and all certificates would be trusted and validated, but I wanted to make sure I am actually presented with the correct certificate. You can easily find out the name of the certificate by turning on tracing in the web.config (explained below). Then you can make sure the proper certificate was served, and then Return True. This process actually exists the 1st time you probably connected to the FTP server with the FTP client software (i.e. FileZilla). A dialog probably presented itself asking if you trusted the certificate. This code is dealing with that process programmatically.

The last fix was in regards to solving this specific issue:

"The remote server returned an error: (550) File unavailable (e.g., file not found, no access)."

Now this was probably an issue specific to my setup, but I mention it because the oversight is probably common. I received this upon getting the FtpWebResponse object. The issue stemmed from the URI I was providing. I knew the URI had to be the full path to the file I wanted to download, so I used ftp://sftp.domain.com/MyFiles/Folder1/MyFile.txt I tried in a browser and had the same issue. I ended up turning on a listener to log System.Net traffic. The following article has directions on doing this: Using System.Net Tracing This output (found in the root folder in my project after running) showed that the default path was already /MyFiles/Folder1. This was already the path as soon as I connect to the base URI of sftp.domain.com, and I should have recognized this from the FTP client software, as I was directly taken to this folder location each time I connected. Therefore, when I fully qualified the URI, it actually resulted in looking for the following: ftp://sftp.domain.com/MyFiles/Folder1/MyFiles/Folder1/MyFile.txt This showed me that all I needed to do was just use the host + filename like the following which worked perfectly: ftp://sftp.domain.com/MyFile.txt

This code demonstrated how to download a file from a FTP server using SSL, but there are many other operations you can do as well (i.e. Uploading, file renaming, directory listing, etc.). Just modify the Enumeration value of 'Request.Method'.

Monday, October 11, 2010

How Much Better Is SQL2008 Full Text Index Performance vs. SQL2000?

I am on the cusp of finishing use of SQL2000 here shortly, but I still needed to set up a full text index for searching on a table. Upon running some of the 1st queries I was extremely disappointed with performance, almost to the point where the solution was not a viable option. I set up the same full text index on SQL2008 R2 and ran the identical query. The performance difference? Astounding. Take a look:

Full Text Index Search results:

SQL 2000: 431 Seconds (7minutes 11 seconds)



SQL 2008 R2: 2 seconds




Wow… (Rows #’s are different because of different database backups). I know SQL2000 relied on the Windows Search Service and this functionality and related catalogs became integrated once SQL2005 was released which helped immensely.

So this is probably not any big news for 90% of folks out there, but if you are still on SQL2000 and need something to look forward to, well here it is.

Full-Text Indexing Overview

Tuesday, October 5, 2010

Why Does My .NET Setup Package Project Not Create the Setup.exe and Only Makes a .msi Package?

I seem to run into these setup package configuration nuances often. It is probably because I keep moving along projects from version to version of VS.NET (now mostly working with VS.NET 2010) and servers keep changing versions too (i.e. 2008 R2). Recently I was having issue installing a .msi package On Windows Server 2008 R2, stating: "You do not have sufficient privileges to complete the installation for all users of the machine. Log on as administrator and then retry the installation." The typical workaround for this was to use the generated "Setup.exe" file instead for installation and right-click it to select "Run as Administrator". However, .msi Windows Installer packages do not have this option to directly run as an Administrator. I did find a work around for installing .msi packages on Server 2008 R2 here

However the problem I ran into before finding the work around was that my "Release" folder for my setup package project was only generating the .msi setup package and not the Setup.exe as well. This was strange to me as all of my other projects contained the Setup.exe package alongside the generated .msi package.

I figured out the reasoning why the Setup.exe package is (or is not) generated. It has to do with selecting the option in the setup project's properties to create a "Prerequisites" setup package as well. Selecting this option (I believe it is selected by default when adding a new setup and deployment project to your solution) is what triggers the creation of the Setup.exe package.

Here where the steps I took to find this option and have the Setup.exe package generated. 1st, select the setup project's "Properties" link after highlighting the setup project in Solution Explorer within VS.NET as displayed below:


2nd, take note of the "Configuration" dropdown value. In my case things were really messed up as I had the proper selections for "Debug" and it was "Release" mode that was incorrectly configured. Check both to make sure you have it correct.


3rd, press the "Prerequisites..." button displayed above in the property pages.

Lastly, check the box stating "Create setup program to install prerequisite components" as displayed below:


Take note of selecting the appropriate .NET Framework version or any other required prerequisites, and select "OK" and the "OK" again to close the dialog box. Rebuild the setup package and then navigate to its build directory (or use "Open Folder in Windows Explorer" feature by right-clicking project in VS.NET 2010: pictured below) to verify the Setup.exe package has indeed now been created.

Wednesday, September 1, 2010

Why Are My Lambda Functions Throwing An Error at Run Time With a System.MissingMemberException Exception?

Recently I was working in a VS.NET 2010 project and I was creating a multiline Lambda expression function within a method to use. It was pretty straight forward and was syntactically correct. I could build the application successfully, but when hitting that code at compile time I received the following error:

No default member found for type 'VB$AnonymousDelegate_0(Of String,String)'.
System.MissingMemberException occurred
Message=No default member found for type 'VB$AnonymousDelegate_0(Of String,String)'. Source=Microsoft.VisualBasic


The signature of (Of String,String) is not that important and if you are receiving this problem, it would reflect the lambda functions signature of your method. I took the code and placed it in a test harness VS.NET 2010 app, and it both compiled and ran successfully. One thing I immediately noticed is if I hovered over the Function variable declaration it resolved to: Dim MyFormatFunc As <Function(String) As String>. In my problematic application it resolved to nothing.

Well the problem lies in the project setting to allow 'Type Inference'. This was a VS.NET 2005 originating project that has been converted to 2008, then to 2010. If the project originated in 2008 or later, Type Inference is set to 'On' by default. My grandfathered application had it set to 'Off' which meant these anonymous types would be treated as type 'Object' instead of resolving to their appropriate type.

You can change this setting at the page, class, or project level which is what I prefer. To turn on this setting, right-click your project in Solution Explorer within VS.NET (or double click "My Project" to get the same result) which will bring up the project's properties. Click on the 'Compile' tab and change the dropdown for 'Option infer:' from 'Off' to 'On' (pictured below).


Recompile your app and run it again. Your lambda functions will work properly and you will not receive any errors (assuming you coded them correctly to begin with). You will also see the IDE resolves the type at design type if you hover over the declared lambda function variable.

Monday, August 30, 2010

Help! My VS.NET Solution Is Not Opening All Associated Projects

Recently while using VS.NET 2010 I had an unsettling feeling when I opened my solution file (.sln) that I have been working on for the past 3 years to find that only 1 of the 3 associated projects opened up. Well I immediately knew everything was ok because all of the directories, etc. still existed for the unopened projects, so I had to figure out how to fix the problem.

This particular problem occurred due to a corrupted .sln file. The .sln file associated with a VS.NET solution contains the meta data in text format of the associated project and source control information. To see this information, navigate to the .sln file in Windows Explorer and right click to select 'Open with' and select either Notepad or WordPad.

You will be able to see associated project information, along with the project GUID, source control information, and other meta data. For whatever unexplained reason, my .sln file only had the details for a single project in the solution, and therefore was being the only one opened up in VS.NET. I didn’t want to start adding projects manually via the IDE thinking that could cause a mess. The solution lies in correcting or replacing the .sln file.

I chose to go to my source control provider and get a 2nd most recent version of the .sln and placed it in a separate location from the real .sln file to inspect. Upon opening the version from source control, I found the details for all (3) projects as expected. There are (2) ways to fix the issue at hand now, and I recommend making a backup of everything before making any modifications to this file in case there is an issue.

I chose to copy everything out of the properly formatted .sln and paste it into the corrupted .sln file and then reopen the solution. The other option would be to simply replace the .sln file directly.

After reopening VS.NET, I didn't use any quick links on the home page and manually navigated back to the .sln file to open the solution. I received one warning/option box stating what was loaded was not the same as in Source Control (because I had modified the .sln manually), and told me it would notify me of issues or differences, which there were not any. I had to reset the startup project and startup .aspx page and all was back to normal.

Sunday, August 22, 2010

How To: Extract Selected Items From An ASP.NET ListBox Using LINQ

(Note: All code examples here use VS.NET 2010 so there is a lot of shortcutting. Just know you will need line continuation underscores "_" and long hand properties if using an earlier version of VS.NET)

If you have wanted to extract all of the 'Selected' items from a ListBox in the past, you probably created a For Each loop similar to the code below:

For Each li As ListItem In Me.ListBox1.Items
If li.Selected Then
'Code here to process selected item
End If
Next
However thank you to LINQ and being that the ListBox control is an enumerable type, you can run a LINQ query against the Listbox's Items to get back all of the selected items. One important note to keep in mind is how powerful LINQ is and how many different objects and types can be used in LINQ queries. This example is specific to an ASP.NET ListBox, but there are many other controls where you could write similar queries to extract out data based on a criteria.

So to begin let's create a simple ListBox with some colors:

<asp:ListBox ID="ListBox1" runat="server" SelectionMode="Multiple">
<asp:ListItem Value="1" Text="Green" />
<asp:ListItem Value="2" Text="Red" />
<asp:ListItem Value="3" Text="Blue" />
<asp:ListItem Value="4" Text="Orange" />
</asp:ListBox>
Now on the button click event we want to extract these colors into our class named 'ColorData'. It has (2) properties: ColorID and ColorDesc. So 1st let's just view the simple ColorData class:

Public Class ColorData

Public Property ColorID As Integer

Public Property ColorDesc As String

End Class
Like mentioned before, we would have iterated through each ListItem Object in the ListBox control, and manually added each set of values to a generic list. With the LINQ query we can do it all in 1 step. Below is the LINQ query that dumps into an Anonymous type named 'myColorData' the results of the query which places only the selected ListBox item values into the collection:

Protected Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click

'Extract the 'Selected' ListBox items into a generic list of type 'ColorData'
'Use LINQ to extract the items rather than iterating through manually with a For-Each loop

'Also notice the use of an Anonomous type: myColorData which at runtime is resolved to
'a generic list of type ColorData
Dim myColorData = (From li As ListItem In Me.ListBox1.Items
Where li.Selected = True
Select New ColorData With {
.ColorDesc = li.Text,
.ColorID = li.Value})

End Sub
That's it! Of course you could modify the values inline, check to make sure the values were of the correct type, or anything else required. Hopefully this will get you thinking about getting rid of those old For Each loops when accessing data from ASP.NET controls and using LINQ instead.

Tuesday, August 3, 2010

Visual Studio LightSwitch Announced

I don't normally just add links to other sites here, but this is a new tool that was announced at the VSLive! conference in Redmond today. Microsoft Visual Studio LightSwitch seems like a useful RAD tool reminiscent a tad of Microsoft Access with wizardry style ability to create data and forms, but with a managed code (.NET Framework) backend, and options for Application Type (desktop or web). Take a look at the link below to learn more:

Introducing Microsoft Visual Studio LightSwitch

Visual Studio LightSwitch 2010 Microsoft® Visual Studio®

Sunday, August 1, 2010

How To: Turn Off User Account Control (UAC) Prompts for Administrators on Windows Server 2008

There is more than meets the eye when it comes to the User Account Control (UAC) settings configured on the various Microsoft Operating System platforms, and I will not get into it all. I do think it is an important security feature to alert users when accessing an application that has prompted for elevated rights. When this occurs a UAC popup window appears prior to the application launching. This gives the user the chance to cancel any process that was not initiated by them directly.

With that said, if you are an administrator and work either directly or remotely with Windows Server 2008, you may find these pop-ups annoying every time you open IIS Manager, SQL Manager, or another application needing elevated privileges. If you have an understanding of UAC and wish to turn off the pop-ups for Administrators logged on you can do the following:

1. In Windows Server 2008, go to Start -> Administrative Tools -> Local Security Policy.


2. In the tree within the Local Security Policy MMC, expand Local Policies -> Security Options.


3. Scroll down in alphabetical order on the right-hand side. There are a block of settings for UAC. Again as mentioned before there is more than meets the eye with UAC, but for this operation we are only concerned with a single setting. Find the setting marked 'User Account Control: Behavior of the elevation prompt for Administrators in Admin Approval Mode'. Right-click the setting and select 'Properties'.


4. Change the dropdown setting to 'Elevate without prompting' and select 'Apply' to close the dialogue. You can also close the Local Policies MMC.


That's all there is to it; now when you open application that requests elevated permissions, you the administrator will not be prompted by any UAC pop-ups.

Tuesday, July 27, 2010

Solving a "The section is marked as being protected..." Error When Decrypting a .config With aspnet_regiis.exe tool

If you use the aspnet_regiis tool to encrypt sections of a .config file then you are ahead of the game is securing sensitive information with the apps configuration. If you have not used it before, I suggest trying it out sometime. However recently I came across an error while decrypting the 'appSettings' section that was as follows:

"The section is marked as being protected, but it does not have the correct format. It should contain only the <EncryptedData> child node."

It turns out that a new key that was auto generated in this particular project dealing with WCF was injected between the <EncryptedData> nodes (as shown below).

</CipherData>
<add key="ClientSettingsProvider.ServiceUri" value="" />
</EncryptedData>
Once I removed the new key and attempted the decryption, everything worked properly! So if you come across this error, see if any new keys have been added (or any other elements for that matter) between the <EncryptedData> nodes because it will cause this error.

Get a free MSDN Magazine Digital Subscription

Here is some good news: you can subscribe to get a digital edition of the MSDN Magazine for FREE! The MSDN Magazine (normally a $35 annual rate for paper copy) covers software development topics related to Microsoft technologies. Just click the link below and login with your Windows Live ID to sign up. It used to come with Dr. Dobbs Journal as well, but I read it ceased subscription in early 2010.

Free MSDN Digital Subscription




Monday, July 19, 2010

Help! My .NET Setup Package Is Throwing a System.BadImageFormatException Error 1001

Recently I had a WCF service install package that was upgraded from VS.NET 2008 to VS.NET 2010 fail with the following error message upon installing it locally on my machine:

"Product: MyWCFService -- Error 1001. Error 1001. Exception occurred while initializing the installation: System.BadImageFormatException: Could not load file or assembly 'file:///C:\Program Files\MyApps\WCFServices\MyWCFService.WinServiceHost.exe' or one of its dependencies. This assembly is built by a runtime newer than the currently loaded runtime and cannot be loaded.."


You might also see the following warning issues by the compiler:

"The target version of the .NET Framework in the project does not match the .NET Framework launch condition version '3.5.30729 '. Update the version of the .NET Framework launch condition to match the target version of the.NET Framework in the Advanced Compile Options Dialog Box (VB) or the Application Page (C#, F#)."

Well this was interesting to me because this was being displayed on a machine that has everything: .NET Framework 4.0, tools, you name it. There should be nothing missing. That's the 1st place to start obviously - make sure the .NET Framework is installed on the target machine.

However this was not the case in my scenario. It turns out even though all of my project comprising the setup package targeted the .NET Framework 4.0, and even the Setup package prerequisites indicated the .NET Framework 4.0, there was still 1 piece of the puzzle missing.

The problem: under the "Detected Dependencies" list for the setup project the 1st item listed was "Microsoft .NET Framework". Refreshing the dependencies does nothing for this value; it must be reconfigured manually. Double click the "Microsoft .NET Framework" item to bring up the "Launch Conditions" window and properties. In the 'Properties' window, change the 'Version' attribute to .NET Framework 4.0 (or whichever framework for your scenario). The Launch Conditions window is displayed below. After making the change, the installer package worked perfectly and installed without issues.



Thursday, July 15, 2010

How to Serialize a SyndicationFeed Object To Be Returned From WCF

Recently I have been working with the SyndicationFeed class in the System.ServiceModel.Syndication namespace. It allows us as developers to work nicely with Atom 1.0 and RSS 2.0 content. A problem arose when I needed to return this object from RSS to a consuming Silverlight client. The problem was that the raw SyndicationFeed class is not serializable, so I needed a solution to serialize the Feed and return it.

This was one time I was happy to find that no custom serialization methods, or mimicking all property values in my own DataContract to return was going to have to be the solution. Instead there are 2 nice classes in the same namespace that do exactly this for us: the Atom10FeedFormatter & Rss20FeedFormatter classes.

In my case I was working with Atom 1.0 content so I was able to use that Serialization class. The solution was simple. Alter my WCF service to return a type of 'Atom10FeedFormatter' defined both on the OperationContract and implementing service method. Then the client can define the return type to receive, and place it right back into a SyndicationFeed object if desired. There are also Atom10FeedFormatter(Of TSyndicationFeed) & Rss20FeedFormatter(Of TSyndicationFeed) classes to serialize classes that derive from those types, and there are all the same classes for the 'SyndicationItem' object as well. Needless to say there is some flexibility in serializing this data to be returned.

So let’s take a look at the code; 1st the code to extract a SyndicationFeed (i.e. from an RSS link off the web).

'Make a call to extract RSS information from the web
Dim proxy As New WebClient()
'Load stream into a reader
xmlRdr = XmlReader.Create(proxy.OpenRead(New Uri("http://rss.cnn.com/rss/cnn_topstories.rss")))

'Load syndicated feed (Atom)
Dim feed As SyndicationFeed = SyndicationFeed.Load(xmlRdr)
Next the code to Return from the method the Atom10FeedFormatter. I do this inline in the Return statement as it is easiest:

Return New Atom10FeedFormatter(feed)
Lastly, the client code to receive the Atom10FeedFormatter and place it back into a SyndicationFeed object. You may wish to do this before binding to controls.

Dim wcfSrv As New MyWCFService.MyWCFServiceClient
'Create the Formatter which will be returned from the RSS call below
Dim MyRssSyndicationData As Atom10FeedFormatter
MyRssSyndicationData = wcfSrv.RssFeedData()

'Load the returned formatter back into a SyndicationFeed object if desired
Dim RssDataFeed As SyndicationFeed = MyRssSyndicationData.Feed
So that's it! If you want to learn more about these classes that make serializing SyndicationItem or SyndicationFeed classes so easy, take a look to the following link:

System.ServiceModel.Syndication Namespace

Wednesday, July 14, 2010

Microsoft Offers Free VB6 to VB.NET or C# Conversion Tool

Microsoft has released 'ArtinSoft’s Visual Basic Upgrade Companion (VBUC)' as a free downloadable tool to convert old VB6 code into .NET code (either VB.NET or C#). It appears to be good for converting up to 10,000 lines of code under the free license. See the link below for more details:

ArtinSoft’s Visual Basic Upgrade Companion (VBUC)

Sunday, July 11, 2010

Fixing the "File type isn't supported" Error When Working With Expression Encoder SDK

Recently I had trouble on a Windows 7 Ultimate Machine with Expression Encoder 4 installed with a "File type isn't supported" error being returned when programming against the SDK. Upon creating an instance of the MediaItem object, the exception was thrown. It turns out even with Win7 ultimate, EE4, and Windows Media Player 11 installed I was still missing some codecs. I downloaded a robust codec pack from the link below and it solved the issues. Microsoft also offers a codec package for older versions of Windows Media Player, but to make sure I had no more issues I installed the larger free package offered from CNet. Once the additional codecs were installed, the code continued to work perfectly. Remember as well, if this code is in a service or .dll, make sure those codecs are in place on machine or server where deployed as well, to prevent the "Well it works on my machine, but not the server..." kind of issue.

Media Player Codec Pack

Codec Installation Package for Windows Media Player 7.1 and later

How to Fix When a WCF Service Does Not Debug via the "WCF Test Client" Tool

If you have created a WCF service, then you have probably seen that upon debugging the service via VS.NET, the service is started automatically using the WcfTestClient.exe tool. This allows the service to actively run through the development server, so test harness applications can consume the service and have real time debugging capabilities. The image below shows the window that should display upon successfully starting (debugging) a WCF Service.

Recently I had a WCF service that upon debugging was opening a web page with the .svc details being shown. This was not what I wanted, and my other VS.NET WCF service started just fine using the WcfClientTest tool. I checked all of the project properties and couldn’t find any setting that triggered this tool to be started.

Upon doing some digging, these configuration attributes are in the .vbproj or .vbproj.user files (.csproj or .csproj.user for C# projects). In the .vbproj file the <EnableWcfTestClientForSVCDefaultValue> tag needs to be present and set to 'true'. This can also be done in the vbproj.user file, but it is the <EnableWcfTestClientForSVC> configuration and it would also need to be set to 'true'. Only the configuration in the main .vbproj file is needed for the WCF service to start using the WcfClientTest.exe tool. Sure enough an older service of mine was completely missing this configuration. The proper configuration element is displayed below.

<ProjectExtensions>
<VisualStudio>
<FlavorProperties GUID="{123c5851-25df-10da-9384-00011b846f00}">
<WebProjectProperties>
<EnableWcfTestClientForSVCDefaultValue>True</EnableWcfTestClientForSVCDefaultValue>
</WebProjectProperties>
</FlavorProperties>
</VisualStudio>
</ProjectExtensions>
You can open either the .vbproj or vbproj.user files by right-clicking in Windows Explorer and opening with notepad. Once the configuration is added and set to 'true' the service will launch using the WcfClientTest.exe tool as expected.

Wednesday, July 7, 2010

Setting Up the ClientBin Folder to Debug a Silverlight Control in a Test ASP.NET Application

If you create a new Silverlight control using either VS.NET 2010 or Expression Blend 4, you are already off on the right foot for setting up a test ASP.NET application for running and debugging a Silverlight control. The newer versions of these applications configure well the test project, creating the 'ClientBin' folder, and associated test .aspx page including preconfigured <object> tags referencing the control.

But what if you have an old or mis-configured project that manually references a .xap file, or had to be constantly updated in Explorer in the referenced path? It is especially important to have the configuration correct in a test app or debugging the .xap from the source Silverlight project is not possible. The following steps describe how to easily reconfigure your ASP.NET test harness to properly reference the .xap generated from the Silverlight control created within the same solution.

1. Right click the ASP.NET test project and select 'New Folder'. Name it 'ClientBin' (without the quotes). Only do this step if the folder does not already exist.

2. Right click the ASP.NET test project (this project should be in the same solution as the Silverlight Control being referenced) and select 'Properties' (or 'Property Pages' for website projects).

3. While still in the Properties, open the 'Web' (for web project types) or 'Start Options' (for website types) and make sure to scroll down and have the 'Silverlight' checkbox checked under 'Debuggers'.

4. Select the 'Silverlight Applications' tab or selection on the left hand pane.

5. Click the 'Add' button to bring up the 'Add Silverlight Application' dialog box.

6. Make sure the radio button selection for 'Use an existing Silverlight project in the solution' is selected, and select the Silverlight control to reference.

7. In the 'Destination folder:' field, make sure 'ClientBin' is in the textbox. This will most likely be pre-populated.

8. Configure the remaining options to your liking. I suggest leaving 'Add a test page that references the control' checked as this will save you from having to do this from scratch.

At this point try starting debugging the Silverlight code by placing a breakpoint in the source. Obviously make sure the ASP.NET test app is set as the default project, and set the newly created page to be the startup page. If you configured it correctly, your code will properly place any new builds of the .xap into the ClientBin folder of the test app, and should allow for proper debugging.

Tuesday, July 6, 2010

Fixing the "attempting to access a service in a cross-domain way..." error when consuming a WCF service running via a VS.NET 'localhost' binding

There must be 1000 good blog posts about setting up the clientaccesspolicy.xml and crossdomain.xml files for a WCF service being accessed by a Silverlight application or control. This consists of dropping these files in the root of the site on the server where the site is hosted. But where do these guys go if you are actively running your WCF service locally (through VS.NET) and still want to consume the service?

I recently had my WCF service running through VS.NET so I could consume and debug it through an ASP.NET test harness. This is a really powerful way to iron out any WCF service issues. Upon launching the .aspx page that contained the .xap Silverlight control that had consumed the localhost version of the WCF service running (not a locally installed WCF service to reiterate; just actively running through VS.NET) I began to get the following exception:

"An error occurred while trying to make a request to URI 'http://localhost:6000/MyWCFService.svc'. This could be due to attempting to access a service in a cross-domain way without a proper cross-domain policy in place, or a policy that is unsuitable for SOAP services. You may need to contact the owner of the service to publish a cross-domain policy file and to ensure it allows SOAP-related HTTP headers to be sent."

What - I thought I fixed this a long time ago?? I realized I had never configured the clientaccesspolicy and crossdomain .xml files just for a service running locally. I started plopping those (2) files everywhere: wwwroot, project, etc. I finally found where they should be placed in order for an ASP.NET app containing a Silverlight control that has consumed a locally running WCF service is supposed to go: in the root of the project folder alongside the .svc and .vbproj files. Once I placed the (2) files into that directory containing the raw VS.NET files, the SL control began working properly.

Makes 100% sense too. If running through VS.NET Cassini local server, those files should be located with the actual service itself.

Again, this is only 1 of many, many ways to solve the original issue at hand. This is only a solution for Silverlight controls that consume a locally running (not installed) WCF service. Hopefully this saves someone else a few minutes.

Monday, July 5, 2010

Updating Microsoft Charting Controls for .NET from VS.NET 2008 to VS.NET 2010

If you recently did an upgrade of a project from VS.NET 2008 to 2010 that contained the Microsoft Charting controls for .NET, then you probably encountered some errors upon 1st run if you are now targeting the .NET Framework 4.0. The solution is to upgrade the charting references to the new .dll version throughout the application.

One possible exception you may have encountered immediately is the following:
"'Legend' is ambiguous in the namespace 'System.Web.UI.DataVisualization.Charting'."

This all points to the need to update the references to the 4.0.0.0 version of the System.Web.DataVisualization.dll used by the MS Chart controls. The following steps need to be completed to get your project current and working with the 4.0 version of the Chart controls.

1. Remove the reference to the System.Web.DataVisualization .dll in the project. If just converted it will still be referencing the 3.5.0.0 version of the .dll.

2. Add a reference to the new 4.0.0.0 version of the .dll from the '.NET' tab when adding a reference to your project. You can view the 'Version' column to make sure you have the correct reference.

3. In the web.config, update the version number in the 'Controls' tag from 3.5.0.0 to 4.0.0.0. Nothing else needs to be modified; the PublicKeyToken, etc. are all the same. Just change the version number. The corrected configuration is displayed below:

<pages>
<controls>
<add tagPrefix="asp" namespace="System.Web.UI.DataVisualization.Charting" assembly="System.Web.DataVisualization, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</controls>
</pages>
4. In the web.config, update the version number in the 'assembly' tag for the System.Web.DataVisualization reference from 3.5.0.0 to 4.0.0.0 if not already done. This may have been updated when adding the reference in Step #2, but if not go ahead and modify it now. The corrected configuration is displayed below:

<add assembly="System.Web.DataVisualization, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
5. In the web.config, update the 'httpHandlers' section for "ChartImg.axd" version once again from 3.5.0.0 to 4.0.0.0. The corrected configuration is displayed below:

<httpHandlers>
<add path="ChartImg.axd" verb="GET,HEAD" type="System.Web.UI.DataVisualization.Charting.ChartHttpHandler, System.Web.DataVisualization, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="false"/>
</httpHandlers>
6. In the markup of any page using the Chart controls, remove the page registration for the assembly; just delete it. The next step will add back in the proper registration.

7. From the 'ToolBox' under the 'Data' tab, drag a 'Chart' control onto your existing page. This simple method will create the proper registration at the top of the page. Make sure to modify the 'TagPrefix' to the proper value if you had changed it previously from the default 'asp' used. After the registration is added back to the page, you can delete the chart control dragged on the page; it was just used to create the proper registration (this happens whenever a new control is dragged onto a page). Repeat this step for any pages that have a chart control on them. The proper page registration is shown below:

<%@ Register Assembly="System.Web.DataVisualization, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
Namespace="System.Web.UI.DataVisualization.Charting" TagPrefix="asp" %>
8. Rebuild your solution and run it. Your MS Chart controls should now be displayed properly!

As an additional reference, the MS Chart samples for .NET have been updated for the .NET Framework 4.0. If you have any additional problems, download the samples project and view the code or markup. You can find the samples at the following link:

Samples Environment for .NET Framework 4 Chart Controls Released!

Friday, June 4, 2010

Converting the Twitter API "created_at" string into a .NET Date Object

If you have worked with the Twitter API in .NET, you will receive a string date representation back corresponding to a tweet in the following format:

"Tue Apr 07 22:52:51 +0000 2009"

If you are like me, you may prefer to have this converted into a .NET Date or DateTime object. This is easy enough to do with the .ParseExact method on a Date object. We just have to provide it a date format for which we are inputting. The magic date formula for the Twitter "created_at" date is as follows: "ddd MMM dd HH:mm:ss zzz yyyy"

The following code shows how to convert this string into a valid date:

Dim TwitterStringDate As String = "Tue Apr 07 22:52:51 +0000 2009"
Dim TwitterDate As Date = Date.ParseExact(TwitterStringDate, "ddd MMM dd HH:mm:ss zzz yyyy", CultureInfo.InvariantCulture)
You might actually do this as part of a LINQ query, and in that case, you might have something within your query that looks similar to the code below:


.PublishDate = If(xe..Value IsNot Nothing, Date.ParseExact(xe..Value, "ddd MMM dd HH:mm:ss zzz yyyy", CultureInfo.InvariantCulture), Date.MinValue)

Tuesday, June 1, 2010

Fixing the WCF 'The remote server returned an error: (415) Unsupported Media Type.HTTP GET' Error

Recently I was building a typical WCF service hosted in IIS on Windows7 using VS.NET 2010 (I have done this several times). Every time I either tried to start the service within VS.NET locally, or actually consume the installed IIS instance on my local machine I would get the following error:

"Error: Cannot obtain Metadata from http://localhost:3509/MyNetworkingService.svc The remote server returned an error: (415) Unsupported Media Type.HTTP GET Error"

I thought I had tried everything with the configuration; had all my "i" 's dotted and my "t" 's crossed. Plus the (415) Unsupported Media Type portion of the message was really throwing me off the proper path to fix the issue. The problem and the fix? A spelling mistake in the name of the service within the .config file. Fixing it was as easy as copying the properly assigned service name from the .svc Markup and into the .config file.

I had the word 'Networking' in my service name, and I spelled it 'Netwoking'. They were so close visually that it didn’t present itself immediately. As many services as I have created and still made a rookie mistake. A nice "Your service name and configuration service name don't match" message would have been nicer than a (415) Unsupported Media Type error, but no big deal it was my mistake.

The real lesson learned for the future - just be safe and copy the name used in the markup of the .svc file (you can get to this by right-clicking the .svc file in Solution Explorer and selecting "View Markup") and paste it into the .config. Below is a working template for a barebones WCF service hosted in IIS using "basicHttpBinding":


<system.serviceModel>
<services>
<service name="MyNetworkingService.NetworkingService"
behaviorConfiguration="ServiceBehavior">
<endpoint address=""
binding="basicHttpBinding"
contract="MyNetworkingService.INetworkingService" />

<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
<!--When hosting a WCF service in IIS, you don't need to specify a base address nor an endpoint,
because the Endpoint will simply be the Virtual Path that your service points to.-->

</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<!-- To avoid disclosing metadata information, set the value below
to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes,
set the value below to true. Set to false before deployment to
avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>

Using Auto-Properties and Traditional Properties with VB.NET in VS.NET 2010

Once you being using VS.NET 2010 as a VB.NET user, you will soon see the neat feature of Auto-Properties which C# users have enjoyed since VS.NET 2008. I will not go too much into them as the feature has been covered exhaustively across blogs and sites. In a nutshell the Auto-Property feature provides a short hand way of defining properties without having to fully write out the Get and Set code, along with the backing variable.

But what if you do need a more advanced property, where the data is manipulated? How do I get back to the more explicit long-hand format of a property if I need it? At least in VS.NET 2008, after finishing the definition of the property and its type and pressing <enter> the template was generated for me, so how do I recreate this in VS.NET2010?

The answer is to type in 'prop' (without quotes) which will show an Intellisense menu of built in snippets including the needed 'Property' code (shown below):


Type in 'prop' and hit the tab key twice to produce the long-hand explicitly defined version of the Property. In fact even this is 1 up from VS.NET 2008 as it creates the backing variable right on top of the property. All you need to do is tab through the fields to modify the values as needed. The long-hand version of the property is displayed below:


Wednesday, May 26, 2010

Add SourceSafe Add-In to VS.NET 2010

Ok for those out there that are still way behind on moving to Team Foundation Server and still use SourceSafe, you will find adding solutions to it in VS.NET 2010 is not enabled by default. To switch source control providers back to SourceSafe do the following: Goto 'Tools' -> 'Options' and select "Show all settings". Expand the "Source Control" node and then select "Plug-in Selection". On the right hand side change the value in the "Current source control plug-in:" to "Microsoft Visual SourceSafe" (pictured below).


Then sometime soon consider moving to Team Foundation Server, which has a "Basic" version that allows a route into a new provider for those intimidated by switching away from SourceSafe.

Monday, May 24, 2010

Using Caching on an Object Data Source and Making it Unique Per User

I am a fan of the Object Data Source control especially when binding objects to controls such as a GridView for ASP.NET web forms. Not a 'huge' fan, but a fan none the less. I find its ability to organize binding methods and events consistently makes it a decent option for binding, along with its ability to present the objects wired up in a strongly typed manner during configuration.

One of the nicest features of the ODS is its ability to implement caching. The ODS control will call it’s wired up '.Select()' method on the object upon binding. For (2) main reasons I can see how caching can be useful.

1. Use ODS caching to prevent multiple users pulling the 'exact' same static data. Caching is an application level store, so once ODS data is stored in the Page's cache, it can be shared to all client's. Why make 20 calls to the database amongst 20 clients for the identical data? This is a good candidate for caching.

2. Use ODS caching when the user is pulling back several records (i.e. 500-1000) records to be displayed in a Gridview, but don’t want the .Select()method called every time the Grid is paged. Essentially getting all 1000 records on each paging command, but yet only displaying maybe 10 at a time on the screen. With caching, all 1000 are pulled back the 1st time, but subsequent paging calls in the bound GridView only have to access the cache for the data and not call the database again. Another good candidate for caching.

If you only need caching for the purpose of the situation described in #1 above then you can stop and are done. This is the way it appears Microsoft designed the caching in the ODS, and really the only thing you need to do is set 'EnableCaching="True"' on the ODS control. It was well designed for the scope of sharing the same data via the cache for multiple clients running the ASP.NET web application.

But what about #2 above. The tricky part is the Cache object is an Application level store shared amongst all clients. What if you still wanted to implement caching for an individual user that requested the 1000 records via some search criteria, but yet don't want that same 1000 record pulled for a different user attempting to pull data based on another criteria. This is exactly what will happen with the default behavior. User2 would get User1's records because the cache is shared by the same ODS between applications. This is easy to test by opening 2 browsers on the same or separate machines. Have User1 do a custom search that populates a GridView, and then have User2 do the same but with different criteria. Upon User1 paging the GridView after User2's results are displayed, User1 will see User2's results. It sure would be nice if there was a "MakeCachingVaryByUser" True/False option on the ODS but it does not exist.

So we need a work around so that we can still leverage caching but make it unique per user. Initially I thought this might be as easy as assigning a unique value to the "CacheKeyDependency" value at runtime server side and hoping data stored in the cache would be based on that unique key for the user, but that did not work. Upon doing testing of this scenario I came to find out the ODS control has some undocumented idiosyncrasies that make it cumbersome to work with at times. The 1st of these was determining that no matter what value I tried to set for the 'CacheKeyDependency' property (or any ODS cache related property) at runtime say in Page_Load() would not stick. Only the value assigned in the page's source was used by the ODS and continually referred to. I tested this by placing a label on a page and writing to it the value of the "CacheKeyDependency" and "CacheDuration". On Page_Load these values would display whatever was assigned in the Page's source. Upon overwriting them, you could verify for a moment the new values were indeed assigned, but upon any subsequent postback to the server, the ODS reverted back to its default values assigned in the source. Well then I thought, I will just store the values in ViewState or in a HiddenField and continually overwrite the ODS properties on all postbacks, right?? Nice try, but still didn’t work. It kept pointing me to the fact that whatever was assigned in the source is what the ODS was married to, and wasn’t going to allow any changes.

So my next step was to determine, "How can I somehow assign a dynamic value to those properties in the page's source, so that a unique cache allocation will be created per user?" This is where the solution comes into play. I must also stop and mention what combination of properties make a 'unique' combination for the ODS according to the MSDN documentation. Let's take a look:

"A unique cache entry is created for every combination of the CacheDuration, CacheExpirationPolicy, TypeName, SelectMethod, and SelectParameters properties."

Ok, between my users CacheExpirationPolicy, TypeName, SelectMethod, and SelectParameters (the actual parameters not the values of the parameters) are always going to be the same. The one that can change... 'CacheDuration'. This specifies a time in seconds before the cache is expired and the ODS will call the select method again. Since caching to me is a 'helpful' attribute of the app, and not a requirement, I do not care too much about this value as long as something reasonable is assigned. And wait! Is the "CacheKeyDependency" value not used to create a unique combination?? That's correct it is not. But we are still going to dynamically create the value as we are the "CacheDuration" to ensure a unique client combination. And assigning the "CacheKeyDependency" to a unique value actually bypassed some odd behavior of calling the .Select() method each time a separate client made a new search, so the combination of both being dynamically created makes up the solution.

Now to the code. Let's begin with the source behind the controls. We need to use inline server tags that actually are used for DataBound controls. We can specify some TimeSpan attributes like 'TimeOfDay' or 'Ticks' to generate some unique values. We use the String.Format function with a placeholder, passing in the TimeSpan method chosen. Foe the "CacheKeyDependency" that actual type is string, so it can be long and we don’t have to worry too much about size. We will use Now.Ticks() for this. The cache duration is an Integer value in seconds so we don’t want it to surpass the bounds; for this we will use Now.TimeOfDay.Milliseconds().

EnableCaching="true"
CacheExpirationPolicy="Sliding"
CacheKeyDependency='<%# String.Format("{0}", Now.Ticks()) %>'
CacheDuration='<%# String.Format("{0}", Now.TimeOfDay.Milliseconds()) %>'
Now in order for the above to resolve to their actual values, we must call the .DataBind() method on the ODS control. This actually ends up serving a dual purpose as it will ensure the .Select() method is called each time the page initially loads, and it will resolve our server code into usable values. If we didn't call .DataBind, we would end up showing the string literal from the property assignment which is no good. Here is the code that needs to be placed in the Page_Load method:

If Not IsPostBack Then

'Call .DataBind() on the Page so the Properties on the ODS below that have serverside
'code associated to the 'CacheKeyDependency' & 'CacheDuration' properties will resolve.
Me.odsItems.DataBind()

'Expire the cache entry programmatically by expiring the key. The key can be expired by using the
'Cache.Remove method with the current CacheKeyDependency value as the parameter.
'When the cache item is removed, all the cached data that is dependent on the key is expired.
'This will force a fresh pull by calling the ODS's wired up Select() method again.
Cache.Remove(Me.odsItems.CacheKeyDependency)
'It's imperative that the ODS 'CacheKeyDependency' value exists in the Page's cache,
'otherwise the data cached by the ODS control will be immediately evicted from the data cache
'each time its added. It is added implicitly in the line below if it does not already exist.

'Using the key value reference of the ODS in the Page's cache, assign an arbritarty new value.
'This process expires the current cache assosiated with the ODS which will force new data to be pulled.
'If this is not done, subsiquient databound control events or page interactions will pull data
'from cache to populate the bound control as opposed to requesting new data.
Cache(Me.odsItems.CacheKeyDependency) = DateTime.Now

'Store both values in hidden field controls so subsiquient server calls can reload the same values.
'This will need to be done, because the inline server code property attributes only resolve when Page.DataBind() is called.
'This way we do it once above, and then just reload the values later.
Me.hfODSCacheKey.Value = Me.odsItems.CacheKeyDependency
Me.hfODSCacheDuration.Value = Me.odsItems.CacheDuration.ToString()

End If


'The 'CacheKeyDependency' & 'CacheDuration' values MUST be reset on all server calls so the default
'databound value will be used as assigned on the ODS directly. Since the Page is only
'databound in initial PageLoad, these values can just be reassigned from their corresponding HiddenField values.
'This MUST be done or the cache would revert to the default literal value from the control which will not
'match because it uses inline server-side code to generate dynamic values so the cache essentially is user specefic.
Me.odsItems.CacheDuration = Me.hfODSCacheDuration.Value
Me.odsItems.CacheKeyDependency = Me.hfODSCacheKey.Value
After the databind, we evict the cache using the newly assigned values to make sure that one it exists in the Page's cache, and two that there is nothing assigned (or actually an arbitrary DateTime value). Next we assign (2) hidden field controls (or ViewState - just needs to be page specific; don't use Session for these values) the values of the generated "CacheKeyDependency" and the "CacheDuration". This will be pulled on subsequent post backs and reassigned to the ODS. But wait a minute??? I know what you are thinking - you said reassigning the ODS properties with custom values didn't work, right? Yes, but these values match the values initially assigned in the source to the ODS, as opposed to overwriting the values with newly created ones. This is that undocumented feature that we are attempting to adhere to.

Lastly, notice the code that will reassign the "CacheKeyDependency" & "CacheDuration" properties on the ODS on each postback. This ensures that the ODS is placing data in the proper cache location, and it must be done or the literal value from the server tags would be used and we will lose reference to our data. The important piece here is that the initial value assigned in source to the ODS is continually reassigned.

At this point that's all the code you need! I recommend if doing this on several pages that you refactor the code above into a 'Shared' Utility UI method. You may end up needing to pass in the Page, Cache, ODS, and ViewState objects in order to refactor to a centralized method outside the page. You can do this later after everything is working well and tested.

The last piece of code will allow you to forcibly evict the cache. Why would you want to do this? If you had a GridView based on search criteria, and a new search was made. In this case you don’t want the default behavior of grabbing data from the cache; you want new data pulled. There are several other reasons you may want to force a fresh data pull, so inject the code below where needed.

'Expire the current cache assosiated with the ODS which will force new data to be pulled.
'If this is not done, this process would not yield new results as the ODS would
'continue to pull existing data from the cache; in our case we want new data.
PageCache.Remove(Me.odsItems.CacheKeyDependency)
PageCache(Me.odsItems.CacheKeyDependency) = DateTime.Now
The (2) lines above are another good candidate for code refactoring into a centralized Shared UI method (i.e. Utilities.ClearODSCache, etc.). Once again you might need to pass in the ODS control and PageCache objects in order to refactor outside the page.

So in just a few lines of code we have modified the caching offered by the IDS control to be user specific. This way we get the performance and caching attributes provided by caching, but the low level scope we needed. There are other 'big picture' ways to probably solve this same issue. For example, don't use the IDS at all, and just persist the Object source for DataBinding in Session and constantly access it there as opposed to going to the Database. Another option might be to siphon 'e.Result' in the 'Completed' event of an ODS and store that in Session. Then on subsequent calls to the '.Selecting()' method, check to see if an object exists as the DataSource already and cancel the operation. There are probably others too, but the ones mentioned here have pitfalls within, by using larger scale techniques to solve a specific issue. I prefer the method of this post because it is specific to addressing how to modify the caching ability to be unique per user. If you would like to learn more about how native caching works for the ODS control, please view the link below.

Caching Data with the ObjectDataSource

Tuesday, May 18, 2010

Methods for Comparing Lists of Objects Based on a Single Property

Recently I had the need to get a new list of objects that results in a new list with all of the items in 'MyList1' that don't already exist in 'MyList2'. At 1st glace I thought I could use the Enumerable.Except function to accomplish this. However I soon came to realize that if the class representing this list has 10 properties, all 10 properties are checked to find the difference. This does make sense and works as intended, but in my case the results were not what I wanted because one of the properties was a timestamp, and even though an object in MyList1 had the same 'ID' as an object from 'MyList2', its timestamp property was different thus retuning that object into the new list as well. What I needed to do was base the returned list results on a single property: 'ID'. This is all that I cared about, so the default overload of the .Except method of an IEnumberable type was not going to work.

A few methods presented themselves on how to solve this issue. If you are reading this through and you do not need to discriminate differences of objects for a single or few properties and it is a strict 1:1 comparison, you are done! Just use the .Except method as shown below:

Dim MyList1 As New List(Of Customer)
Dim MyList2 As New List(Of Customer)
'...populate the above lists with data
Dim ItemsInList1NotInList2 As New List(Of Customer)
'Get all of the items in MyList1 that do not already exist in MyList2 using the .Except() method
ItemsInList1NotInList2 = MyList1.Except(MyList2)
The 1st method in solving this issue is to use the second overload of the .Except method to define the IEqualityComparer(Of T) to compare values. This involves implementing the IEqualityComparer on the class being compared and defining the methods required by the interface for a custom comparer. Now in my case the comparison was for a specific case and I wanted a solution that was more inline. I wouldn't want to define a compare method for the class unless it was definite that this was always how the class was to be compared. In my situation this was not true so I did not implement the interface, but check out the link below for an explanation and code example on implementing the IEqualityComparer interface:

Enumerable.Except(Of TSource) Method

The 1st of (2) methods that worked for a more localized inline solution where to use the methods exposed on an IQueryable source, such as the .Where() and .Select() methods in the System.Linq namespace. By calling the .Where method as an instance method on MyList1 we can define a predicate that will test for a condition and return the resulting values. In our case we want to pass into the Where() method a Lambda expression that will use anonymous functions to test for a condition and return the resulting values. Let's look at the code:

Dim MyList1 As New List(Of Customer)
Dim MyList2 As New List(Of Customer)
'...populate the above lists with data
Dim ItemsInList1NotInList2 As New List(Of Customer)
'Get all of the items in MyList1 that do not already exist in MyList2 using the .Where() method
ItemsInList1NotInList2 = MyList1.Where(Function(i) (MyList2.Select(Function(i2) i2.ID).Contains(i.ID) = False)).ToList()
So the above code can loosely be read from the inside out as "Select all IDs from MyList2 that do not exist in MyList1 and return them into a list of type MyList1". The .Where() method returns the elements from source that satisfy the condition specified by predicate. A predicate is a function that will test each element for a condition returning a Boolean value. In our case the Boolean value returned is True/False if the .ID in MyList2 exists in MyList1. Also notice the .Select() method called on MyList2 to project over the sequence of .ID values and use the index of each element in the projected form. Both IQuerable methods define an Anonymous method in VB.NET using the 'Function()' keyword accepting parameter of the type to be used. In our case the .Where() method takes the anonymous type 'i' which is of type MyList1 and the .Select() method takes an anonymous type 'i2' of type MyList2. If we were to decompile the above code we should see all of the values in MyList2 being iterated over to determine if the .ID value already exists in MyList1 and if not adding it to a new list of type MyList1 that is the result. Just remember that Lambda expression are just syntax sugar. In our case you could write the same code long hand by defining a delegate method that takes in the list types and iterates through them to get the same result. The Lambda expressions make our life as developers much easier by not having to write out so much code.

To me I probably prefer the method above to solve this issue because it is the most concise. However, those not familiar with anonymous types and Lambda expressions may not read the code above so well and want something a bit more explicit in definition. The 2nd method to solve our issue is to define a simple LINQ query dumping the result into a an anonymous type and then converting it into a list. I think the advantage of this 2nd method is it is much more readable. Let's take a look at the code:

Dim MyList1 As New List(Of Customer)
Dim MyList2 As New List(Of Customer)
'...populate the above lists with data
Dim ItemsInList1NotInList2 As New List(Of Customer)
'Get all of the items in MyList1 that do not already exist in MyList2 using a LINQ query
Dim query = From ItemsIn1 In MyList1 _
Where Not (From ItemsIn2 In MyList2 _
Select ItemsIn2.ID).Contains(ItemsIn1.ID) _
Select ItemsIn1
'Convert the LINQ query results into a list of objects
ItemsInList1NotInList2 = query.ToList()
Even though LINQ should not be confused with TSQL, the above query does have attributes of a typical SQL query. It is essentially a 'NOT' clause with LINQ sprinkled in. It reads loosely as follows: "Select all of the items in MyList2 that are also in Mylist1, and exclude these items (using NOT) from everything that is in MyList1; finally Select the results. I think another advantage of this is it is faster to modify if you needed to make the comparison based on (2) properties as opposed to (1) as in my examples. Both solutions work identically and will produce the same results.

So to review, we discussed (3) methods for comparing (2) lists to get only the items in the 1st list that don't exist in the 2nd list: the Enumerable.Except() method, the Queryable.Where() method, and a LINQ query. Each has their place, but all will help to quickly make a comparison that otherwise may have required a long had For-Each loop with a flag set for comparison differences in order to get the same result.