Monday, September 24, 2007

One of the very difficult to solve problems in .NET client & web service solutions is sharing custom objects between the web service and the client.

Here's the scenario.

You have a business layer with some serializable objects. Let's say the namespace is "MyLovelyBAL" and the object is "MyLovelyObject".

You have an operation in your web service that returns one of those objects.

Now you build a client, say a Windows Forms app ("MyWinApp") that will call into that Web Service, and you are not savvy enough to use a tool like thinktecture's WSCF. :-) Therefore you "Add Web Reference" and point to the web service you built. In doing so, you are asked to give a name to the proxy. Let's call that "MyWSProxy".

In your WIndows Forms app, you also have a reference to the assembly that contains MyLovelyBAL.MyLovelyObject.

Now it's time to write some code.

You instantiate the proxy and request a lovelyobject.

Dim myproxy = New MyWSProxy.Service
Dim MyLovelyBAL.MyLovelyObject=myproxy.GetMyLovelyOBject

But it fails telling you that you can't cast a MyWinApp.MyWSProxy.MyLovelyObject to a MyLovelyBAL.MyLovelyObject.

And then you will begin on a journey to hell, which might include reading this article on Schema Providers which is not for the feint of heart.

So just forget it. It's 2007! We have WCF.

You can go into your project where you define the MyLovelyObject class and give it a DataContract attribute and give it's properties (that you want to expose) DataMember attributes. Your interface operation (and any operations that implement it) will return a MyLovelyBAL.MyLovelyObject object.

And when you Add Service Reference to your windows forms app, there is no namespace replacement. A "MyLovelyObject" is always coming from MyLovelyBAL and this is oh, so lovely. The way it oughtta be!

You know I learned this the hard way. And I will be seeing Michele Leroux Bustamante tomorrow who will first bop me on the head for waiting so long to use WCF and the sit me down and have me read her fabulous book Learning WCF, (which I had in my hands long enough to raffle off at VTdotNET last week BOO HOO) cover to cover. It's about time!

Monday, September 24, 2007 2:23:08 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 
 Sunday, September 23, 2007

As we plug away on the prototype, the Astoria team is building brand new code for the real product and right now they are trying to finalize the URI query syntax and looking for your input. Read more....

[A New DevLife Post]

Sunday, September 23, 2007 7:21:55 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 

I ran into what felt like a brick wall today trying to install Vista onto a VPC and finally found a way to accomplish it. Read more...

[A New DevLife Post]

Sunday, September 23, 2007 7:18:44 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 
 Saturday, September 22, 2007

Boy am I excited about this.

Timothy Ng is on the VB team and wrote the recent MSDN Magazine article on Lambda Expressions for VB developers. I understood lambdas better than ever after reading the article ... finally something from the VB perspective.

His two talks are:

LINQ-ing your data
In this session, I will explore how LINQ will change the way you think about data in your applications. In particular, I will explore how LINQ provides a higher abstraction for accessing your data, provides a common abstraction across data domains, and allows for simplified data transformations. I will focus on LINQ to XML as the transformation technology, and the various XML features that are available in VB.

LINQ from the Ground Up
In this session, I will explore the fundamentals of LINQ by examining how the various language features (such as extension methods, type inference, lambda expressions, and anonymous types) form the foundation for LINQ. I will share some of the experiences that the VB compiler team had while building LINQ, in hopes that it will help you build your applications on LINQ. Examples will be in VB, but this talk applies to C# folks who are interested in learning how the fundamentals work as well. All language concepts apply to both VB and C#.

I sure wish I was seeing these before I do my LINQ talk in San Diego on Tuesday! :-)

Richard Hale Shaw will be doing some pretty cool LINQ talks too which will be a C# focus.

Before I learned that Tim was coming, I was already excited about the Code Camp speaker roster. This will be the first time that Jesse Liberty and Fritz Onion (who are "local" in about the same way that I am local... well, I guess Fritz's drive is even longer than mine). Jesse recently started working for Microsoft (see? I managed not to say "swallowed the red pill"!) and he is "da man" at Microsoft on Silverlight. Fritz is a true ASP.NET plumber who amazed us all with his book Essential ASP.NET in the .NET 1.0 days and again with the updated version for ASP.NET 2.0, because it explained HOW ASP.NET actually worked and he continues to spelunk on our behalves.

There are already almost 40 sessions listed with tons of amazing expertise on display.

I'll be doing a talk on Astoria, a session on using Entity Framework in multi-tiered apps (and getting a little nervous that nobody seems to be doing an Intro to entity framework session) and a session on ASP.NET Databinding with LINQ.

Saturday, September 22, 2007 9:33:52 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 

I don't like not knowing how something works, so I just had to dig further into how Astoria services see requests for queries versus Updates, Deletes and Inserts.

In my previous post, I dug into the assembly with reflector and could see that a PUT was used for updates, DELETE for deletes and POST for inserts.

I turned tracing on for the Astoria service and pushed the trace info to a log file. You can see how to do that at the end of this post.

Looking at the messages, you can see the METHOD and the query operators but not the main call. :-(

For example if I made a call to service.svc/Customers the message looks like this. I've stripped out WebHeader information from the message.

<HttpRequest xmlns="http://schemas.microsoft.com/2004/06/ServiceModel/Management/MessageTrace">
<
Method>GET</Method>
<
QueryString></QueryString>
</HttpRequest>

I don't see that I called the Customers "operation".

If I request service.svc/Customers[52], it's the same. I don't see the Customers and I don't see the ID i've passed in.

If I use a query operator as in service.svc/Customers[City eq Seattle], it's still not exposed. Now I'm getting frustrated. I know I'll have to dig deeper at some point, or bribe Pablo to show me someday.

But for now here's a little gratification. If I create my own Service Operation which takes a query string, now there is something to see. My service operation is CustomersbyCity. the call looks like this

service.svc//CustomersByCity?city=London

The message looks like this

<HttpRequest xmlns="http://schemas.microsoft.com/2004/06/ServiceModel/Management/MessageTrace">
<
Method>GET</Method>
<
QueryString>city=London</QueryString>
</HttpRequest>

When I use my little console app as a client and do some inserting and deleting, I can see the methods coming up the pipe, remember, the Astoria.Client.dll translates the function (insert/update/delete) into a Method.

<HttpRequest xmlns="http://schemas.microsoft.com/2004/06/ServiceModel/Management/MessageTrace">
<
Method>DELETE</Method>
<
QueryString></QueryString>
</HttpRequest>

So, only MILDLY interesting to me (because I can't get in deep enough yet) and probably COMPLETELY uninteresting to most everyone else. But it was fun digging around and also learning how to use some of the WCF tool. Here by the way is how I got to see the messages.

Modify the web.config to "Turn On" tracing and messaging. You can do it manually or with a tool.

    <system.diagnostics>
        <sources>
            <source name="System.ServiceModel.MessageLogging">
                <listeners>
                    <add name="messages"
                    type="System.Diagnostics.XmlWriterTraceListener"
                    initializeData="c:\logs\messages.svclog" />
                </listeners>
            </source>
        </sources>
    </system.diagnostics>

    <system.serviceModel>
        <diagnostics>
            
<messageLogging logEntireMessage="true" logMessagesAtServiceLevel="true"
               logMessagesAtTransportLevel="true" />
        </diagnostics>
    </system.serviceModel>

 

You can use the WCF Config tool in Visual Studio 2008 to configure this. Right click on the web.config and choose Edit WCF Configuration. Here are screenshots of the settings that created the above in my web.config.

If you are using the Web Dev server, you need to stop that server so that the trace actually gets written to the file before you can look at it.

Once the file is created, you can look at it using the Service Trace Viewer which you can find in the SDK Tools (available from Start/Program Files/Microsoft Windows SDK for Visual Studio).

Saturday, September 22, 2007 3:54:12 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 

I got bored with querying Astoria in the browser and and wanted to see how to use it in an application, so I opened up the sample console app that comes with the bits and started poking around and was astonished at what I saw.

You can create an new WebDataContext based on the Astoria data service.

WebDataContext is like an ObjectContext. Here's what it exposes:

namespace Microsoft.Astoria.Client
{
public class WebDataContext : IDisposable
{
public WebDataContext(string uri);

public string BaseUri { get; }
public ICredentials Credentials { get; set; }

public void Add(object obj, string entitySet);
public void Attach(object obj, string entityKey, ObjectState state);
public WebDataQuery<T> CreateQuery<T>(string queryString);
public WebDataQuery<T> CreateQuery<T>(string queryString, QueryOption queryOptions);
public void Delete(object obj);
public string[] GetEntitySets();
public void MarkModified(object obj);
public void SaveChanges();
}
}

So, you can create a WebDataContext

WebDataContext ctx = new WebDataContext("http://localhost:50000/northwind.svc");

then query it

Category beverages =
ctx.CreateQuery<
Category>("/Categories[CategoryName eq 'Beverages']"
)
.FirstOrDefault();

add a new object to the ObjectContext

Category newcategory = new Category();
newcategory.CategoryName =
"Sample"
;
ctx.Add(newcategory,
"Categories");

And then (this is the part that is sick and I want to rip open the covers and see how they are pulling this off!), call SaveChanges on the context!

ctx.SaveChanges();

Of course, you can update and delete, too.

So, I wanted to see what's going on here. It's REALLY important to understand that we're looking at prototype bits. THe production code that they are working on now will be different, though the concepts and use will be relative to what we can play with today.

You can't get any hints by adding ?WSDL to the end of the uri. Rembmer withi a default asotira service, there are no operations. It's all based on interpreting the URI. So I opened up Reflector and pointed to the key assembly.

SaveChanges calls a method called SendHttpEntityUpdate which sends up the object and the key and based on the operation calls a request method. So if you are adding, it sends a POST. If you are deleting, it sends DELETE and if you are modifying it sends PUT. The dll used by the service checks which method you pushed up (there's an enum for ReceiveEntityOperations) and processes the incoming data accordingly.

Neat!

So of course there will be the usual million concerns about this. There are definitely credentials built into these operations and all kinds of ways to limit what is available thorugh the web service.  And there's so much I don't know yet, so don't judge solely on what you are seeing here.

If you don't have a .NET Astoria client, you can just construct the POST, PUT and DELETE URIs. I can't say how to do that quite yet though. ;-) (Not that it's a secret. I just don't know how yet.)

But it's still darned cool. Nice job folks (and don't forget Alex who was key in getting Astoria going before he left Microsoft! )

Saturday, September 22, 2007 11:34:01 AM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 

I find it helpful to use the browser to visualize immediate cause and effect of queries to an Astoria web data service. THere's so much more that you can do when you are really using this in applications, though.

In the documentation there's a list of query operators:

  • callback (specific to JSON for AJAX calls)
  • expand - pull related data using EDM associations
  • format - define format of the response (eg xml, json, rdf)
  • keyset- only return the entityKeys
  • orderby (you know what this is)
  • skip (same as in LINQ)
  • top

The doc explains each of these in more detail.

Here for example, I can get a specific customer and all of their sales order headers. (Make sure you have MultipleActiveResultSets=true in your connection string in order to do this. That allows the db to return more than one result on the same connection string .. a feature added in that we first acquired with .NET 2.0 and SS2005.)

I can drill even further to return the SalesOrderDetail data form the SalesOrderHeader using this URI:

http://localhost:55976/WebDataService1.svc/Customer[6]?$expand=SalesOrderHeader.SalesOrderDetail

Here's an example of using skip

Looking at the non-xml formats is tough in Internet Explorer, at least with JSON as IE doesn't know how to display the response. There are a few ways around this.

1) (bit of a hack :-))

Add this into the appSettings seciton of web.config

  <add key="EnableAstoriaJSONP" value="true"/>

and then browse your service in Firefox, Firefox will ask you waht you want to view the response in, choose notepad and you can see the json response. Read the caveats about JSONP in the Astoria documentation.

2) Use the helper tool which is an HTML page here [ http://astoria.sandbox.live.com/tools/raw.htm ]. You can't use this online (I know 'cause I tried in my impatience!). You need to download it and add it into your project. It will then properly parse and display results.

So all this is just about querying, the "R" (read) in CRUD. Wait till you see the C, U & D. Watch for another post.

Saturday, September 22, 2007 11:23:57 AM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 
 Friday, September 21, 2007

Here's a quick start for checking out astoria. See my  blog post "what the heck is astoria" if necessary. :-)

Requirements:
VS2008 Beta 2
Entity Framework Beta 2
Entity Framework Tools August CTP
Astoria CTP September refresh (which works with VS2008 Beta 2)

Note that I had to do this in XP. I'm having a wierd problem in Vista around the ASPNETCompatibilityEnabled setting in web.config. You can follow the discussion of that if you are interested in this forum thread. The thread is accidentally a combination of Kevin Hoffman's issues with IIS and my issue with Cassini. They don't seem to be related problems.

1. Start a new web application project (not website) in VS2008.

2. Add an ADO.NET Entity Data Model using the projects Add New Item dialog.
If you haven't used the wizard before, you can see this blog post.
The basic steps are

  • Generate from Database
  • SElect a database (same as doing this for other data UIs in Visual Studio)
  • Just leave default names for things for this quickstart. (I pointed to Northwind in SQL Server and it defaulted at NorthwindModel and NorthwindEntities, etc)
  • Select all database objects (this is the default). This brings in tables, sprocs and views.
  • Then finish and you will see the EDMX file in the solution. You can double click on that to see the conceptual model (one of three parts of the model) in the designer. Note that this is just a simple way of getting a model from a database, the most basic type of conceptual model.

Add a Web Data Service.
Add New Item, choose Web Data Service. It's in the main section (Visual Basic/ C#, not categorized into Data or Web templates) down on the bottom, alphabetical. :-)

The services code view should open up. Note the TODO for the class. All you need to do is replace the stub [class name]

(VB)

(C#)

with the name of your entity wrapper class created in the model. THe model code gens a class file.
 

You can open up the model in teh designer and then see it's Namespace and Entity Container Name in the properties.
 

That's it! Now you can browse the web service directly (eg. right click on svc file in solution explorer and View in Browser) and start looking at how the data is exposed through the EDM.

My What the Heck is Astoria post walks through some quick query scenarios to give you an idea of what you can do.

This is just the tip of the iceberg of course. See the Astoria Team blog for more info.

Friday, September 21, 2007 10:36:45 AM (Eastern Standard Time, UTC-05:00)  #     |  Comments [4]  | 

I'll be doing a session on Astoria at REMIX Boston 07 (10/8-10/9) and at the New England Code Camp (9/29-9/30).

What is Astoria?

Astoria exposes data as web services through an Entity Framework Data Model. Once exposed, you can use URI based syntax to query data through the conceptual layer of the entity data model. You can control what is and isn't exposed and you can also do inserts, updates and deletes. The results can be output in a number of formats including JSON and ATOM.

It's simplest to understand this by looking at some queries in a web browser. The Astoria CTP add-in for Visual Studio 2008 Beta 2 has a template for creating a Web Data Service that looks like a WCF service. It only needs one class to expose the whole kit n' caboodle and that class inherits from WebDataService, a generic class. You merely need to tell that class that the type it is dealing with is the Entities class exposed by an Entity Data Model that you have created. The class doesn't even have any code in it.

  public class northwind : WebDataService<NorthwindModel.NorthwindEntities>
    {
    }

Then you can just make calls to that service and start playing with the query syntax.

The base service exposes the list of entities in the model (http://mysite/myservice.svc). The payload is just a raw XML representation of the data. There will be payloads for different formats, such as JSON, ATOM and more. See this Astoria team blog post for more info.

Then you can start drilling in. Here for example, I have asked for the Categories entities. It will return all entities with their details.

Now you can start seeing the URIs that are displayed to show you how to drill further into data. When looking at it in the browser, you might want to click on those URIs, but remember, in a real app this is not how you will be working with the results of the service call. :-) Note that you can see how to get to the related Products data although it is not pulled in automatically. That's no different than how Entity Framework works by default - deferred loading of related data.

So let's try the Category uri for categoryID 2.

And then get the products for that category.

So you can see now the basic concept of what Astoria is doing. I am not forced to write a billion operations in my web service to expose my data every which way. I don't have to deal with building xmldocuments to return data that can be consumed by anyone, not just .NET clients that can deal with serialized datasets. With proper security in place and the possibility of customizing what is and isn't exposed (and how), consumers of my data can now have easy access to do all kinds of reporting, mash-ups and more. The fact that any consumer will know what to do with any data that is exposed via an astoria web data service is huge.

You can learn more about Astoria by following the Astoria Team blog.

Friday, September 21, 2007 9:14:28 AM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 
 Wednesday, September 19, 2007

When Vermont.NET does not have a sponsor to cover the cost of pizza & soda, the attendees pay for their own - $5 each. This was the case at Monday's meeting. Additionally, we were doing a fundrasing raffle for the first time ever, using a license to Infragistics NetAdvantage that was donated by Infragistics. The raffle was $5. There were about 25 people at the meeting.

Usually, collecting the money is a subtle thing that happens in the background and I have not even had to do it in years as another user group member has been grabbing the cash on my behalf. But I was doing it on Monday and wouldn't you know it, but everyone had 20's. Nobody seemed to have change. In over 5 years and 70 meetings, this has never happened before. It took a good 15 minutes or more to get it all sorted out. While I'm laughing about it now, it was totally chatoic and embarrassing at the time. We had a bunch of new attendees and our speaker, Mark Mullin (who helped come up with change for a 20) from New Hampshire. I could only imagine what they were thinking!

So my lesson? I guess from now on, I'll just have to add in "20's not accepted".

 

Wednesday, September 19, 2007 9:17:06 AM (Eastern Standard Time, UTC-05:00)  #     |  Comments [3]  | 
 Tuesday, September 18, 2007

Erick Thompson is at SQL PASS and will be doing a talk on Entity Framework for DBAs tomorrow (Wednesday) afternoon.

You can get a preview of what he will be talking about by reading his recent post on the topic.

Tuesday, September 18, 2007 7:35:55 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 

While there are many great LINQ resources for VB, I happened to read/watch/see these and wanted to be sure to point them out.

1) Lisa Feigenbaum's LINQ Best Practices webcast. Lisa is on the VB team. This webcast is full of great insights and tips.

2) Timothy Ng's Basic Instincts: Lambda Expressions article in the current (Sept 07) issue of MSDN Magazine. I've spent so much time trying to interpret lambda articles written in C#. Having one written specifically for VB devs is awesome and this article gently leads you into the complexities in a very easy to comprehend way.

3) 101 LINQ Samples for VB (yay!) was posted a few weeks ago. LINQ to Objects, to SQL, to XML and to DataSets. Don't go looking for LINQ to Entities yet though.

 

VB
Tuesday, September 18, 2007 12:24:42 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 

Inc's 5000 list was recently released and among the 9 Vermont companies on the list there are two software companies, both based in South Burlington.

 iTechUS ranked 113 on the whole list and #11 in IT companies. In New England it is ranked #1 in the IT Services companies. www.itechus.com

Dealer.com ranked 578 in the full list. While they are a software company, their industry is listed as advertising and marketing. In that category they are #55. www.dealer.com

Tuesday, September 18, 2007 10:04:51 AM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 

Just a quick pointer to Pablo Castro's announcement that a new CTP for Astoria is out that will work with vS2008 Beta2.

An important note, this is a refresh of the CTP, which is based on the early prototype. The actual production code that they are working on is a totally different set of bits. Hmm, for safetey's sake, I better just quote Pablo:

NOTE: let me stress that this is a refresh of the May CTP, which is based on the initial prototype of Astoria. All of the features/design options we’re discussing here in this blog (e.g. last post about payload formats) are in the context of the production version of Astoria which we are in the process of building. The production version incorporates a ton of feedback that we received over the last few months plus our own more detailed thinking of what should the system look like. So expect to see quite a few differences between the CTP and the production code once we start shipping the production version. The principles will all be the same, but the details will vary.

Tuesday, September 18, 2007 6:05:17 AM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 
 Monday, September 17, 2007

This a job with one of my mentoring clients, Co-Operative Insurance. I absolutely love going to their office and working with these people. It's a small team in a large company. Nice, friendly folks. And it's in Middlebury which is a great place to work.

Web Application Development

Excellent opportunity for a qualified programmer with web application design, development, and implementation experience!

Work on the complete product development lifecycle of our B2B extranet, including requirements gathering, technical design specification creation, coding, testing, deployment, post-production support, documentation and maintenance.

Must have 2-5 years’ development experience with Visual Studio 2003/2005 as well as JavaScript, (X)HTML, ASP.net, VB.net and XML. Pluses include insurance industry knowledge and experience with CSS and with version control software such as SourceSafe or Vault. Solid UI design skills get you multiple pluses.

To join our hard-working, fun IS team in producing quality apps, you must be an organized, self-motivating team player, an excellent communicator and attentive to detail. You must be willing and able to work both independently and with a team, depending on the assignment.

Co-operative Insurance Companies is a $50 million property and casualty insurer in VT and NH.  We offer a competitive benefits package and a schedule that allows for work/life balance.

For immediate consideration, please forward a copy of your resume to jobs@ciui.net.

Monday, September 17, 2007 12:20:58 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 

We are really looking forward to this talk by Mark Mullin tonight at VTdotNET!

We also have some great WCF books to give away: Learning WCF by Michele Leroux Bustamante (thanks OReilly and INETA) and Pro WCF: Practical Microsoft SOA Implementation from APress.

Thanks to Infragistics, we will be raffling off two licenses to NetAdvantage for .NET. One of the raffles will actually be a fund-raising raffle.

We also have shirts and swag courtesy of CodeZone and piles of CoDe Magazine and aspnetPRO to give away.

And thanks to Verio's sponsorship of INETA, I am able to offer Verio's free webhosting to Vermont .NET members who show up at the meeting tonight.

Since it's such a nice day (and I have just absolutely NO work to do ;-)), and Mark is being so generous with his time (driving up from New Hampshire) and cash (there's gas $ and hotel involved), I am going to take him on a 1.5 hour cruise on Lake Champlain aboard the Spirit of the Ethan Allen this afternoon. Hey, running a user group is HARD work, y'know?

Monday, September 17, 2007 11:15:14 AM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 
 Sunday, September 16, 2007

Loren Heiny has been working on a cool Silverlight annotation app to user Silverlight and a stylus to do Google and Technorati searches. It's called SearchTip and is live on TabletPC Post.

There is a lot that is really interesting about this. (And it's visually appealing too!) First of all, he has a YouTube video where he compares using this (earlier version) on a Mac to using Inkwell on the Mac (when you have a tablet ala Wacom attached to the Mac) and demonstrates the superiority of Microsoft's handwriting recognition. I was lucky enough to attend an SDR at Microsoft a few years ago where the man who ran the whole reco team explained to us how they made it all work. I can't imagine anybody being able to come close to their investment or acheivement with handwriting recognition, so it was great to see Loren being able to really compare apples to apples (no pun originally intended, but oh well!)

Next is the fact that Loren is doing handwriting recognition from the InkPresenter in Silverlight. Not an easy feat. Once the reco is done, the result can get passed to a google service and voila you get your results displayed.

Loren has done some amazing work with the Tablet APIs for a number of years and always thinks out of the box. So I love that he's taking his ideas and applying them to Silverlight.

 

Of course, you don't need a tabletpc to draw in silverlight, but as awesome as the recognition is, I don't think it's fair to ask it to recognize mouse drawn handwriting. But it's not bad. Reco returns a list of guesses, sorted by best to worst. Here I'm using an earlier version which does google searching and trying it with my mouse on a regular (non-tablet) desktop.

So you can click on the guess (I clicked on Steins) and it will show you the next guess on it's list. Heinz was next, then a few more clicks (just to see) got me off the track. oops. This is not a reflection of the reco or of Loren's app. I'm only playing around with the mouse for fun (and laziness since my tablet is upstairs.) So while the reco may not be great when you aren't using a stylus which has about 10 times the resolution as a mouse, Silverlight's ability to collect the ink data from the mouse is still pretty impressive.

You can see Loren doing this. and how impressive it truly is using a Tablet, in his YouTube demo video.

You can clear your writing with the x, and of course, when you have the correct reco displayed, click Search.

Sunday, September 16, 2007 9:48:13 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [1]  | 

I watched part 2 of Jim Wooley's ASP.NET Podcast show on LINQ and was really impressed with the creativity of his examples. Having dug deeply in order to write LINQ in Action along with Fabrice Marguerie and Steve Eichert, he's way past the how-to basics and able to see the bigger picture of leveraging LINQ.

In his demo, he starts with some simple querying of the file system - a good demonstration of using linq against objects, but by the time he gets to the end of the demo, he is using JOIN to build queries that combine file system info with data pulled from the database.

I knew I wanted to do something like that but I couldn't just copy him, no matter how flattering. So I thought about it for a while... what data is on my computer that I might want to extend with some database data? Then I thought of Outlook.

Thankfully, John Goalby had already written some posts on querying Outlook data with LINQ. So I was well on my way!

I created a few new email accounts for some employees of companies in AdventureWorksLT and sent emails to myself with their accounts. Then I created contact records for them in Outlook in my own account, making sure that I typed in the company names to match the database. Now I had some test data.

First I tested out a query where I joined MailItems from my inbox with ContactItems from my contact. (Note that I did this in VB since John's examples are in C#, so this gives a little more sample code for people to discover.)

Dim ol As Outlook._Application = New Outlook.Application
Dim inbox = ol.ActiveExplorer().Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox)

Dim contactfolder As Outlook.MAPIFolder = ol.ActiveExplorer.Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderContacts)

Dim emails = 
From email In inbox.Items.OfType(Of Outlook.MailItem)() Select email
Dim contacts = From contact In contactfolder.Items.OfType(Of Outlook.ContactItem)() Select contact

Dim
emailswithcompany = From email In emails Join contact In contacts _
    On email.SenderEmailAddress Equals contact.Email1Address _
    Select email, contact.CompanyName

For Each emailwithco In emailswithcompany
    Debug.Print(String.Format("{0} from {1}: {2}", _
       emailwithco.email.SenderName, emailwithco.CompanyName, emailwithco.email.Subject))
Next

This worked fine. I got a list of the sender and subject from the emails and company names from the contact records.

  • Katherine Harding from Sharp Bikes: Order 40 Shifters
  • John Harding from Sharp Bikes: modification to recent order
  • Keith Harris from Progressive Sports: vendor appreciation party

Then I queried the database and did a JOIN with the above results. It was funny to see how the types and subtypes kept growing as I built this up in layers. It's nice to have things organized, but if I were starting from scratch, I might do this a bit differently so that my resulting types aren't so complex*.

Dim awdc As New awlinqDataContext
Dim custSalesPerson = From cust In awdc.AWCustomers Select cust.CompanyName, cust.SalesPerson
Dim emailswithcompanysp = From emailco In emailswithcompany _
   Join cust In custSalesPerson _
   On cust.CompanyName Equals emailco.CompanyName _
   Select emailco, cust.SalesPerson

For Each emailwithcosalesp In emailswithcompanysp 
  Debug.Print(String.Format("{0} from {1}: {2}" & NewLine & "SalesPerson:{3}", _
    emailwithcosalesp.emailco.email.SenderName, _
    emailwithcosalesp.emailco.CompanyName, _
    emailwithcosalesp.emailco.email.Subject, _
    emailwithcosalesp.SalesPerson))
Next

And voila!

  • Katherine Harding from Sharp Bikes: Order 40 Shifters
    SalesPerson:adventure-works\josé1
  • John Harding from Sharp Bikes: modification to recent order
    SalesPerson:adventure-works\josé1
  • Keith Harris from Progressive Sports: vendor appreciation party
    SalesPerson:adventure-works\david8

Now I could write an app that can distribute email to the correct sales people when they come into a general mail box! Well, I supposed I could have done it prior to having LINQ (or maybe in Exchange which I know nothing about), just with a lot more effort!

*I couldn't resist streamlining the final solution.

Dim ol As Outlook._Application = New Outlook.Application
Dim inbox = ol.ActiveExplorer().Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderInbox)
Dim contactfolder As Outlook.MAPIFolder = ol.ActiveExplorer.Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderContacts)
Dim emails = From email In inbox.Items.OfType(Of Outlook.MailItem)() Select email
Dim contacts = From contact In contactfolder.Items.OfType(Of Outlook.ContactItem)() Select contact
Dim awdc As New awlinqDataContext
Dim custSalesPerson = From cust In awdc.AWCustomers Select cust.CompanyName, cust.SalesPerson

'now query across emails, contacts and custSalesPerson in one query

Dim emailcontactsp = From email In emails Join contact In contacts _
  On email.SenderEmailAddress Equals contact.Email1Address _
  Join custsalesp In custSalesPerson On contact.CompanyName Equals custsalesp.CompanyName _
  Select email, contact.CompanyName, custsalesp.SalesPerson

For Each ecsp In emailcontactsp
Debug.Print(String.Format("{0} from {1}: {2}" & NewLine & "SalesPerson:{3}", _
   ecsp.email.SenderName, _
   ecsp.CompanyName, _
   ecsp.email.Subject, _
   ecsp.SalesPerson))
Next

Sunday, September 16, 2007 8:17:37 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [2]  | 

There's been a lot of talk about role models in the various Women in Tech essays over on OReillyNet.

But I wanted to brag about my best role models!

My parents celebrated their 50th anniversary yesterday. :-)

Sunday, September 16, 2007 1:27:41 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [1]  | 

A million years ago, when Windows 2000 came out, many VB6 developers got calls from users that the scroll wheel stopped working in their VB6 applications.

It didn't take long for the fix (Install Intellimouse v4.0) to get shared on newsgroups and forums. (This was pre-blog days; do you even remember those?)

I have a client who has that Intellimouse install tucked away on the server to apply to new computers being set up with my old apps and it's worked charmingly for years.

But a few months ago, a user got a new p.c. and the fix didn't work. His job requires him to make heavy use of a function that has a big FlexGrid and not having the ability to use the scroll wheel definitely cramped his style and impeded the efficiency of his work.

I found a KB that offered a solution for the IDE, but not compiled exes. I dug into the mouse drivers on his compuer but there was no way to uninstall them.

Finally, on the VB newsgroups, MVP Ken Halter suggested a code fix that was on PlanetSourceCode.

Scroll Wheel Support: http://www.planetsourcecode.com/vb/scripts/ShowCode.asp?txtCodeId=48722&lngWId=1

It contains two modules that futz around with some Win32 stuff to get the scroll wheel to be noticed by VB6. You need to add those modules to your project and then make a call to a function passing in the window handler of your control when you fire up your form and then a detach function as you unload the form.

Simple as heck and worked like a charm!

(and generated this very happy note from the user: IT WORKS!!!  THANKS!!)

VB
Sunday, September 16, 2007 11:13:51 AM (Eastern Standard Time, UTC-05:00)  #     |  Comments [1]  |