advanced web statistics

External URLs and Phone Numbers in WP7 WebBrowser Control

The Windows Phone 7 WebBrowser is garbage.  F**king horrible. If you want to display basic HTML it's great.  If you want to display HTML that users can interact with (i.e. clicking hyperlinks, mailto: links, or dial phone numbers) it's basically useless.  Don't even get me started on opacity and background images.  In developing a Windows Phone 7 application recently there was a need to link to a mobile-enabled website. In said website there were pages with phone numbers, email addresses, etc... A business requirement for the application was to open up the mail client, dial a phone number, or open a hyperlink when a user clicked on these links.  No duh right?  I thought the WebBrowser would at least be able to parse out links with href and mailto and since it's built for a PHONE it should be smart enough to detect tel.  Fail.

At this point we had a couple of options (Mango is NOT an option at this point):
  1. Recreate the external site in XAML with event handlers for the "hyperlinks" which would end up being styled textblock controls
  2. Take a screenshot of the mobile site and place rectangles over the hyperlinks and wire those up to events
  3. Download the HTML from the site asynchronously, parse HTML and find head or title tags, inject JavaScript to find and enumerate all a tags and wire up an onclick event to raise the ScriptNotify event [via window.external.Notify], call the WebBrowser's NavigateToString method and pass in this newly-constructed HTML, figure out what kind of link was clicked and then fire an appropriate task
Before I talk about which option I chose, take a minute to soak in the absurdity of the aforementioned solutions (FYI - I chose 3). Take this simple example site here:

simple webbrowser example 1

And here's the markup (unadulterated)

simple webbrowser example 2

Simple enough right? You can clearly see that the links are sporting the correct syntax. Time to start hacking.

The first thing we need to do is add JavaScript to get all of our a tags. Once we have this we simply need to enumerate and attach an onclick event. I'm not a JavaScript expert by any means but think this fairly unobtrusive. This JavaScript is going to remain constant so we should use the const string:

simple webbrowser example 3

The next thing we need to do is wire up the WebBrowser control. The most important part is to enable scripting via the IsScriptEnabled property. This is set to false by default and resulted in many f-bombs being dropped until I figured this out. We will also need to wire up the ScriptNotify event. This is how the WebBrowser will communicate with our application.

simple webbrowser example 4

In the LoadedComplete method we perform an asynchronous request via WebClient object to get the source HTML from the server. We then create a StringBuilder instance and insert our JavaScript just before the </title> tag.

Now that the ScriptNotify event is wired up we can set a breakpoint in our code, run the application, and determine which link was clicked when we tap the link.

simple webbrowser example 5

In the case above, we'll want to launch the EmailComposeTask.

simple webbrowser example 6

Running this code in the emulator and we get the following:

simple webbrowser example 7

I don't have a mail account setup in the emulator (is this possible?) so we see the fail message. But if I did in fact have an email account setup it would display appropriately. For other scenarios you would use the appropriate phone task.

Easy enough. This code isn't production-ready by any means. I just wanted to quickly share with others how to accomplish a task that should be a native experience (IMO). I will clean this up and add logic for things such as detecting if there is actually a title tag, confirming that a task is about to be shown, cleaning up the ScriptNotify event handler, etc... This would take about 3 seconds using Interface Builder in Mac OS (iOS development). iOS developers have the luxury of checking boxes for detecting various links in text controls.

Original inspiration: Integrated Links and Styling for Windows Phone 7 WebBrowser Control

Tags: C#, Silverlight, Windows Phone

Json.NET and Generic HttpWebRequest Responses FTW

Working on a Windows Phone 7 mobile app recently, I found myself writing a bunch of similar / redundant code for HttpWebRequest response handling. By "a bunch" I mean in the neighborhood of 3 methods.... Hello Generics!!!

If I find myself writing the same handler more than once and the only difference is the type of object I am returning I ALWAYS (at least try to) write a generic method to take care of the heavy lifting.  There are a plethora of reasons to use Generics when the opportunity presents itself and I'm not going to start listing them.  Those of you that understand .NET Generics will get it.  If you are a .NET developer and currently developing applications targeting version 2.0 or greater and aren't familiar with Generics, then I highly suggest you educate yourself: Generics in the .NET Framework.

If you are still with me and are familiar with the System.Net namespace and the HttpWebRequest class (as it pertains to Silverlight and/or Windows Phone 7), I'd like to share the following response handler for dealing with Asynchronous requests:

private static T ProcessFamiliarRequestResponse<T>(IAsyncResult asyncResult)
{
    HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;
    HttpWebResponse httpWebResponse = (HttpWebResponse)request.EndGetResponse(asyncResult);

    using (StreamReader streamReader = new StreamReader(httpWebResponse.GetResponseStream()))
    {
        return JsonWorker.TooEasyToDeserializeJsonObject<T>(streamReader.ReadToEnd());
    }
}

If this doesn't make sense to you I highly suggest reading up on WebClient and HttpWebRequest usage.  If you are familiar, I hope you can appreciate that this is one point of entry into handling an infinite number of requests (within reason).

It gets cleaner: Enter Json.NET.  Personally, I like to create clean POCO's to return data to the consumer.  I have seen way too many examples creating objects that conform EXACTLY to the Json format returned from any given request.  For instance:

public class Employee
{
    public string employee_first_name { get; set; }
    public string employee_last_name { get; set; }
    public string employee_job_description { get; set; }
}

Gross (imo).  .NET is so much better than this.  I take pride in the code that I deliver and personally believe the following class is much cleaner (and not to mention, conforms to .NET naming conventions):

public class Employee
{
    [JsonProperty("employee_first_name")]
    public string FirstName { get; set; }

    [JsonProperty("employee_last_name")]
    public string LastName { get; set; }

    [JsonProperty("employee_job_description")]
    public string JobDescription { get; set; }
}

This way you can map your neatly-defined POCO properties to their disgusting origins.  The following generic method for deserializing your Json results works fairly well (pre-supposing you decorated your POCO(s) with the JsonProperty attribute OR left it repulsive and/or primitive):

public static class JsonWorker
{
    public static T DeserializeJsonObject<T>(string result)
    {
        return JsonConvert.DeserializeObject<T>(result);
    }
}

If you want to take it a step further, you could add the following method to your JsonWorker class to serialize your objects. Think storing application state in IsolatedStorage or updating your light-weight, client-side database (via Sterling (which is AWESOME!!!)):

public static string WayEasierToSerializeObjectToJson(object randomPOCO)
{
    return JsonConvert.SerializeObject(randomPOCO);
}

Easy.

Tags: C#, Code, Windows Phone

Set Silverlight Startup Page

In sandboxing Windows Phone 7 recently I came across the need to change my startup page.  The reasoning for this is simple: I have a core of the work completed and I want to sandbox a data call before I implement in the actual application.  With conventional web projects this is as easy as right-clicking the file you want to and click 'Set As Start Page'. With the Windows Phone 7 SDK this luxury does not exist.

There is however a way to accomplish this with a little bit of code.  Simply open up your projects App.xaml.cs and implement an Application_Startup method.

private void Application_Startup(object sender, StartupEventArgs e)
{
    RootVisual = new SandboxPage();
}

SandboxPage is the name of my XAML page in this example. Here we simply set the RootVisual to an instance of our new page.

The second piece to this is registering this startup method in the App constructor a la:

public App()
{
    Startup += Application_Startup;
}

This doesn't necessarily apply to Windows Phone 7.  General Silverlight.

Easy.

Tags: C#, Silverlight, Windows Phone

Microsoft Office Interop Outlook & C# For Outlook Searches

I had a need yesterday to query a bunch of Outlook e-mails: 44,743 to be exact.  This was the result of a clients mailing list account being setup incorrectly and we just found out about it!  An e-mail account was setup strictly for the mailing list and it was never properly setup as an Outlook account locally.  No big deal.  When the hosting provider contacted us to clean it up I noticed that there were a BUNCH of bad e-mail addresses (Return to sender, bad account, etc...).  At first I started to click on individual e-mails, copy the bad e-mail address to a .txt file, then search that folder for the same e-mail address and delete the results.  That takes FOOOOORRREEEEVVVVEEEEERRRRRRR.  Then I realized I could dork around with Outlook via a C# console application and harvest the e-mails and output them in a sorted Excel spreadsheet.

Enter Microsoft.Office.Core and Microsoft.Office.Interop.Outlook namespaces. I decided it would be best to create a little C# console application utilizing these namespaces to search the e-mail, write a little regular expression pattern (by write I mean copy & paste one from the interwebs) to parse out e-mail addresses, and add e-mail addresses that didn't have the host name or the reply e-mail account in it to a List<string> (if it didn't already contain it!).  It was pretty easy.

First:

using Outlook = Microsoft.Office.Interop.Outlook;
using Microsoft.Office.Interop.Outlook;

I'm not going to bore you with the entire application unless you really want it.  I'll just get you to the point where you can start to do stuff with your e-mails.

Now you need to instantiate your Outlook object.

Outlook.Application application = new Application();
Outlook.NameSpace nameSpace = application.GetNamespace("MAPI");
Outlook.MAPIFolder mapiFolder = nameSpace.GetDefaultFolder(OlDefaultFolders.olFolderJunk);

It might be worth mentioning the olFolderJunk business you see there.  In the interest of time I didn't want to mess around with trying to navigate to the folder that I actually stored these e-mails in since I have a bunch of subfolders in Outlook due to using multiple accounts and a handful of rules and what-not.  So in the interest of time I moved all the e-mails to the junk folder since I could easily access it.  I think the correct syntax would be something like:

nameSpace.Folders["FOLDER"].Folders["SUB"].Folders["YOU_GET_IT"];

Moving on.  Once you finally get access to your designated folder you need to start being able to do stuff with the items in that folder.  It's as easy as enumerating the, you guessed it; MailItem objects a la:

foreach (MailItem mailItem in mapiFolder.Items)
{
   // do stuff
}

Easy.  The properties are fairly braindead as well.  MailItem Object Members

Have fun.  I'll probably use this in the future as well.

Tags: C#, Code

Silverlight ObservableCollection Bug Will Be the Death of Me

BUG: Items are sorted correctly sometimes, other times not so much.

FREQUENCY: Intermittent, never when debugging and stepping through everything, always seems to happen when I'm not.

RATING: Probably the most painful bug I've worked on in some time.

So I've got an ItemsControl in a custom XAML user control.  All this does is display an IEnumerable<T> of video items.  Simple.  To provide a better user experience we implemented an ObservableCollection<T> as the source of the ItemsControl with a Collection_Changed event so we can add one item at a time to the collection so as to not wait for the entire collection to populate before displaying to the user.  Now we're able to display the first 2 items and the scrollbar height shrinks as items are being added.  It's a nice user-experience.

Anyway, so for the implementation.  When the VideoRetrieveComplete event fires (after making a web service request for the data) we call the UpdateVideoCollection method.

UIThread.Run(delegate
{
   videoListControl.UpdateVideos(args.VideoItems);
   videoListControl.itemsControlVideoListing.DataContext = new VideoItem();
}

Notice that we're doing this in the UI Thread.  The code above is located in our Controller class.  The args.VideoItems is an IEnumerable<VideoItem> and they are sorted in the order they need to be in.  I have verified this time and time again with painful debugging.

Now time for VideoList controls properties, objects, and methods:

public int CurrentPageIndex { get; set; }
public ObservableCollection<VideoItem> ObservableCollection { get; set; }
private List<VideoItem> VideoItems = new List<VideoItem>();

Constructor where everything is initialized.  Probably important to include this.

public VideoList()
{
   CurrentPageIndex = 0;
   InitializeComponent();
   ObservableCollection = new ObservableCollection<VideoItem>();
   itemsControlVideoListing.ItemsSource = ObservableCollection;
   ObservableCollection.CollectionChanged += CollectionChanged;
}

public void UpdateVideos(List<VideoItem> videoItems)
{
   UIThread.Run(delegate
         
   {
               VideoItems.AddRange(videoItems);
               ObservableCollection.Clear();

               if (VideoItems != null && VideoItems.Count > 0)
               {
                  VideoItem videoItem = VideoItems[0];
                  VideoItems.RemoveAt(0);
                  ObservableCollection.Add(videoItem);
               }
            });
}

void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
   if (VideoItems.Count > 0)
      UIThread.Run(delegate
               
{
                  VideoItem videoItem = VideoItems[0];
                  VideoItems.RemoveAt(0);
                  ObservableCollection.Add(videoItem);
               });
}

The reason for removing an item, and then re-adding it is that this is the only way we can get our Converters to fire off, otherwise the collection hasn't changed and UI elements don't update.  For example, switching between tabs the user can add videos to their playlist.  Each time the tabs are selected and the VideoListing populates the VideoItems within that listing needs to show either an ADD / DELETE FROM PLAYLIST button depending on whether or not the item is in the playlist.  If there's another way to get those Converters to fire off I'm all ears.

So the problem is that more often than not the videos, which come in sorted correctly, are not always displayed that way.  For example, if I have 5 videos with dates 10/1/2008, 10/1/2008, 9/30/2008, 9/29/2008, 9/28/2008 sometimes the sort will be:

10/1/2008
9/30/2008
9/29/2008
9/28/2008
10/1/2008

or:

9/30/2008
9/29/2008
10/1/2008
9/28/2008
10/1/2008

You get the idea.  Could it be that we are not implementing this correctly?  Do we need to rebind the ItemsControl or is this correct?

I've been working on this bug for a day and a half now and have nothing.  I'm sure it's probably something stupid.

I'm all ears if you have anything. ANYTHING!

Oh, should probably add that if I page to the next listing of videos and then back it'll remedy itself!

Tags: C#, Silverlight

No Generic List FindAll Method in Silverlight 2 Beta 2

Came across a need this morning to use the FindAll method. To my dismay it wasn't there.  I know that Silverlight uses a subset of the .NET Framework but the System.Collections.Generic namespace is definitely there.

System.Collections.Generic namespace in Silverlight 2 Beta 2

You can clearly see that I'm using the namespace in the above image.

ReSharper List.FindAll Error Silverlight 2 Beta 2

Here you can see that ReSharper's background compiling caught it right away. For a second there I thought: "You know, maybe ReSharper is full of shit. This can't be right." I tried to compile to make sure (which was the case with the version 4 nightly builds).

'System.Collections.Generic.List' does not contain a definition for 'FindAll' and no extension method 'FindAll' accepting a first argument of type 'System.Collections.Generic.List' could be found (are you missing a using directive or an assembly reference?)

Weird.

Tags: C#, Silverlight

JSON Serialization with Silverlight Isolated Storage For the Win

I'm going to post this to help others who are killing themselves trying to figure out IsolatedStorage in Silverlight 2 Beta 2.  Ordinarily I'd post stuff like this for my own reference but I have had it beaten into my head for the past 12 straight hours.

The IsolatedStorageFile class abstracts the virtual file system for isolated storage.  It can be used to store stuff such as application settings or user login information.  The application settings being persisted on a user's machine were exactly what we needed to accomplish so that led me down this road.  I read numerous blogs, watched countless videos, and did endless searching and finally crafted my solution as a hodge-podge of all of them!

First, what didn't work.

Channel 9 had a video on "how easy it is to persist user application settings" using IsolatedStorage.  Their solution consisted of the following:

public class LocalStorageHelper
{
    private const string KEY = "FOO";

    public static object RetrieveObjectFromLocalStorage()
    {
        if (IsolatedStorageSettings.ApplicationSettings.Contains(KEY))
            return IsolatedStorageSettings.ApplicationSettings[KEY];

        return null;
    }

    public static void UpdateLocalStorageObject(object value)
    {
        if (IsolatedStorageSettings.ApplicationSettings.Contains(KEY))
            IsolatedStorageSettings.ApplicationSettings[KEY] = value;
        else
           
IsolatedStorageSettings.ApplicationSettings.Add(KEY, value);
    }
}

Simple enough.  The video presenter did all sorts of cool things like shut down the browser, open it back up, press F5 repeatedly and the storage still contained the user data.  I'm guessing this solution worked flawlessly in Beta 1.  I also had to add the checks for whether or not the ApplicationSettings contained the specified key.  Needless to say this implementation isn't very reliable.

It was then that I started looking into JSON and other storage options.  I remember in .NET 2.0 I was used to serializing objects into XML and thought I would take this route.  This was easier said than done because Silverlight is using a subset of the .NET Framework and the XML Serialization namespace didn't make it.  (NOTE: I could've used Linq XML but it was an after thought!).

Enter the DataContractJsonSerializer class.  Since we are only given a default storage of 1MB (more can be requested but the end-user has the option of shutting you down) JSON is a likely candidate since it isn't as verbose as XML.  A simple string written in a text file within the file store is more than sufficient enough to give us an instance of the object which holds the properties we want to persist.

Some code for converting an object to JSON string and vice-versa.

public static string ConvertObjectToJsonString(object input)
{
   try
   
{
      using (MemoryStream memoryStream = new MemoryStream())
      {
         DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(input.GetType());
         dataContractJsonSerializer.WriteObject(memoryStream, input);
         memoryStream.Position = 0;

         using (StreamReader streamReader = new StreamReader(memoryStream))
            return streamReader.ReadToEnd();
      }
   }
   catch (InvalidDataContractException)
   {
      return string.Empty;
   }
}

public static T ConvertJsonStringToObject<T>(string json)
{
   try
   
{
      using (MemoryStream memoryStream = new MemoryStream())
      {
         byte[] bytes = Encoding.Unicode.GetBytes(json);
         memoryStream.Write(bytes, 0, bytes.Length);
         memoryStream.Position = 0;

         DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(typeof(T));

         return (T)dataContractJsonSerializer.ReadObject(memoryStream);
      }
   }
   catch (SerializationException)
   {
      return default(T);
   }
}

You might notice the InvalidDataContractException I am catching above.  Like with XML Serialization you want to mark a class & properties as Serializable.  If you don't you won't be able to serialize it.  Hence the catch block.  Here's an example of a serializable class:

using System.Runtime.Serialization;

namespace LocalStorage.Objects
{
   [DataContract]
   public class UserAccount
   
{
      [DataMember] public string EmailAddress { get; set; }
      [DataMember] public string Password { get; set; }
   }
}

I'll show an example of how to store the user account in a second.  First how to open / create this in isolated storage.

public static void SaveDataToLocalStorage(string data, string filename)
{
   try
   
{
      using (IsolatedStorageFile isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication())
      {
         if (!isolatedStorageFile.FileExists(filename))
         {
            using (IsolatedStorageFileStream isolatedStorageFileStream = new IsolatedStorageFileStream(filename, FileMode.Create, isolatedStorageFile))
            using (StreamWriter streamWriter = new StreamWriter(isolatedStorageFileStream))
               streamWriter.Write(data);
         }
         else
         
{
            using (IsolatedStorageFileStream isolatedStorageFileStream = new IsolatedStorageFileStream(filename, FileMode.Open, isolatedStorageFile))
            using (StreamWriter streamWriter = new StreamWriter(isolatedStorageFileStream))
               streamWriter.Write(data);
         }
      }
   }
   catch (IsolatedStorageException)
   {      
   }
}

public static string RetrieveDataFromLocalStorage(string filename)
{
   try
   
{
      using (IsolatedStorageFile isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication())
      {
         if (!isolatedStorageFile.FileExists(filename))|
            return string.Empty;

         using (IsolatedStorageFileStream isolatedStorageFileStream = new IsolatedStorageFileStream(filename, FileMode.Open, isolatedStorageFile))
         using (StreamReader streamReader = new StreamReader(isolatedStorageFileStream))
            return streamReader.ReadToEnd();
      }
   }
   catch (IsolatedStorageException)
   {
      return string.Empty;
   }
}

I apologize for the mess but those are some pretty lengthy class names and descriptive variable names 8^)

So now how to use:

// instantiate our user
UserAccount user = new UserAccount
                              
{
                                 EmailAddress = "asdf@asdf.asdf"
                                 Password = "stupid"
                              
};

// convert to JSON string
string json = ConvertObjectToJsonString(user);
SaveDataToLocalStorage(json, "UserAccount.txt");

// convert back to UserAccount
string json = RetrieveDataFromLocalStorage("UserAccount.txt");

if (!string.IsNullOrEmpty(json))
{
   UserAccount account = ConvertJsonStringToObject<UserAccount>(json);
   // do stuff with userAccount here...
}

Now the user can be persisted on each site visit.  If you wanted to store the email address and password you could log your user in when they visit the application.  Think "remember me" checkbox or something similar.  Another good use would be to serialize the RSS categories and only display those articles from within your silverlight application.

The possibilities are endless.  Remember that this information is stored with the client in their AppData folder so don't put anything too private in there ;)

Easy.

UPDATE
If this is a little too cluttered for you to read might I suggest viewing the RSS feed.

Tags: C#, Silverlight

Silverlight Databinding and Interaction with Controls

Busy, busy with Silverlight lately.  I'm starting to really enjoy it and am learning a ton.  It's still so new to me that I don't know if I'm doing everything correctly but I am getting stuff to work ;)

The biggest issue I've had with Silverlight lately is databinding and interacting with the bound data.  For example, with ASP.NET you can bind a List<Foo> to a Repeater and implement a method for ItemDataBound event that wires up each of the hyperlinks, buttons, etc... to perform certain actions when each Repeater item is databound.  In Silverlight you do not have this luxury.  The more I work with Silverlight the more I realize that you don't need this.

The way I have been working with Silverlight is the same as working with ASP.NET in that I am a big fan of encapsulation of functionality.  This means modularizing a XAML control with a C# code-behind much like an ASCX in ASP.NET.  Let's say you have 2 controls: Article, and ArticleList.  ArticleList is simply an ItemsControl of Article's. Simple.  Here is the XAML:

ArticleListing.xaml

<StackPanel x:Name="stackPanelArticleListing">
   <ItemsControl x:Name="itemsControlArticleListing">
      <ItemsControl.ItemTemplate>
         <DataTemplate>
            <Controls:Article x:Name="article" />
         </DataTemplate>
      </ItemsControl.ItemTemplate>
   </ItemsControl>
</StackPanel> 

Simple enough. All the binding syntax is in the Article.xaml.  In the Article.xaml I have a TextBox called "textArticle" that displays the entire article.

What if I have a button in my Article control that, when clicked, displays the full article in an ArticleViewer control?  It's actually pretty easy if you set the DataContext of the ItemsControl control.  I've got another Silverlight Class Library as part of my solution and I've got a class called Article.  Article looks like this:

public class Article
{
   public Guid ArticleId { get; set; }
   public string Author { get; set; }
   public string Body { get; set; }
   public string Introduction { get; set; }
   public DateTime PublishDate { get; set; }
   public string PublishDateShort { get; set; }
   public string AuthorUrl { get; set; }
   public string AuthorImageUrl { get; set; }

So when I go to bind my List<Article> to the ItemsControl I would do this:

itemsControlArticleListing.DataContext = new ArticleLibrary.Article();

var results = ArticleLibrary.Article.RetrieveAllArticles();
itemsControlArticleListing.ItemsSource = results; 

So now in my Article control I can get the current DataContext by way of the following:

TextBlock textBlock = sender as TextBlock;

if (textBlock != null)
{
   var article = textBlock.DataContext as ArticleLibrary.Article;

   if (article != null)
   {
      // set another control's text property to article stuff :D
   
}

So I set a breakpoint and you can see that my article object above won't be null:

Article != null

You can also see that I was able to get the exact object that I clicked on.  Well, you really can't see it because of the lack of screen grabs but take my word that this is the one that I clicked on.

Article object local information

Awesome right?

Sorry if this post is all over the place.  It's been a work in progress and I've been adding snippets here and there in between work!

Tags: C#, Silverlight

Elegant Way of Executing a Stored Procedure using LINQ?

I was working in a project today and I was trying to create a generic way of using a DataContext class to execute a stored procedure.  Of course there's the way of dragging a stored procedure onto the designer, etc... but I'm not interested in this at the moment.  TBQH, I haven't even been doing the ORM mapping in this manner lately after using the System.Data.Linq.Mapping namespace to map a POCO (Plain 'Ole CLR Object) to an actual SQL table (as well as using ColumnAttribute's to map the properties to columns.  This is extremely helpful for larger 2.0 projects that were recently converted to 3.5.   Some might argue that this isn't how LINQ was supposed to be used or that it makes it more complicated.   First of all, I don't think that there is any right or wrong way to use LINQ.  Secondly, I find it can be a pain to have to remove the table from the designer, reconnect via Server Explorer, refresh the database, re-drag onto designer over-and-over when making changes to tables, views, procedures, etc...  This is also problematic if you don't have access to a production database from your dev environment.  Most web servers I experience don't have Visual Studio 2008 installed so it's not like I can quickly generate a newer set of LINQ-to-SQL classes.  If a table column name changes, I simply refactor the corresponding property in my library and I'm good to go.

[Table(Name = "dbo.FooTable")]
public class Foo
{
   #region Table Columns / Properties

   [Column]
   public long FooId { get; set; }

   [Column]
   public string FooName { get; set; }

   [Column]
   public string FooDescription { get; set; }

   #endregion Table Columns / Properties

Easy.

public class Database<TDataContext, TEntity>
{
   // ...   
}

Take the previous data access helper class.  Let's say I want to create just a generic function to execute a stored procedure against my database.  Let's call it "ExecuteCommand".

public static void ExecuteCommand(string command, object[] parameters)
{
   if (string.IsNullOrEmpty(command))
      throw new ArgumentNullException("command");

   using (var database = new TDataContext())
      database.ExecuteCommand(command, parameters);

Basic.  The implementation is pretty mindless as well.

Database<MyDataContext, TFoo>.ExecuteCommand("CommandName", null); 

This sucks when you have parameters because I haven't found any documentation on how to execute a procedure elegantly using this approach.  It appears that the DataContext.ExecuteCommand approach is best-suited for on-the-fly dynamic SQL.... gross!  I also read (need to find the link again) that some super-sleuthing uncovered that parameter substitution is silently ignored.  (I'll have to check back on that)

I did find a way to implement using a stored procedure....

string command = string.Format("exec Command '{0}', {1}", "string", 1);
Database<MyDataContext, TFoo>.ExecuteCommand(command, null); 

That is disgusting.  Not a huge fan.  What if you have a procedure that has 5, 10, 20 parameters? Ouch.  What if your input parameters contain ' 's? You would then have to replace all ' with a ''.

Any suggestions?  I'm hoping that there is a much more elegant / succinct way of doing this that I haven't found yet.  I've tried to "RTFM" as they say but a couple thousand pages of LINQ reference books and I've got nothing.

Here's a little 2.0 throwback of what I'm trying to accomplish.  Like I said before, there has to bge a more elegant way of doing this with the 3.5 Framework / LINQ / DataContext that doesn't involve dragging-and-dropping procedures onto the designer.

public static int ExecuteProcedure(List<SqlParameter> parameters, string command)
{
   try
   
{
      using (DBManager manager = ...)
      {
         manager.Open();
         manager.CreateParameters(parameters.Count);

         for (int i = 0; i < parameters.Count; ++i)
            manager.AddParameters(i, parameters[i].ParameterName, parameters[i].Value);

         return (int) manager.ExecuteScalar(CommandType.StoredProcedure, command);
      }
   }
   catch (SqlException)
   {
      return 0;
   }

Help me from the following please!!

StringBuilder query = new StringBuilder("exec Command ");
query.AppendFormat("{0},", foo.FooProperty1);
query.AppendFormat("'{0}',", foo.FooProperty2.Replace("'", "''"));
query.AppendFormat("'{0}',", foo.FooProperty3.Replace("'", "''"));
query.AppendFormat("'{0}',", foo.FooProperty4.Replace("'", "''"));
query.AppendFormat("'{0}',", foo.FooProperty5.Replace("'", "''"));
query.AppendFormat("'{0}',", foo.FooProperty6.Replace("'", "''"));
query.AppendFormat("'{0}'", foo.FooProperty7.Replace("'", "''"));

Database<MyDataContext, Foo>.ExecuteCommand(query.ToString(), null); 

Tags: C#, LINQ

LINQ and Generic GetRecord Method

Lately I've been abstracting some LINQ functionality into a Generic Data Access Class so that it can be reused across multiple projects without having to write out functionality over-and-over (i.e. creating a RetrieveProductsByProductId method, AddNewProduct, DeleteProduct, etc...).

CRUD'ing is a braindead process using LINQ & Generics.  I've found that Update is / can be a huge pain in the ass.  I currently have 2 versions of an update method (working on a 3rd) but just thought I'd kick-off the next couple of posts by posing the following question:

Question:  How well do you think the following would scale?

public T GetRecord<T>(int primaryKey) where T : class
{
   using (var database = new TData())
      return database.ExecuteQuery<T>(string.Format("SELECT * FROM {0} WHERE ID={1}", typeof(T).Name, primaryKey)).Single();

I found this little beauty during my travels to find examples of generic update methods.  This is obviously not an update method but definitely worthy of posting by me.  This seems a little too specific to the database implemented so it begs the question of why one would even try to make it generic.

Here's how I (and Dwight) would implement:

public static T GetRecord<T>(Expression<Func<T, bool>> predicate)
{
   if (predicate == null)
      throw new ArgumentNullException("predicate", "lulz");

   return new TData().GetTable<T>().Where(predicate).SingleOrDefault();
}

Not Shakespeare but it sure is a lot more practical than the previous example IMHO.

var foo = DataAccess<FooDB>.GetRecord<Foo>(f => f.Name == "Stihl 066");

Easy.  I'll be prepping some more entries related to Generic updating of SQL data soon.  I could really use some feedback from some more seasoned LINQ'ers.

Tags: C#, LINQ
<< Newer Entries Older Entries >>