Wednesday, April 02, 2008

Bill Burrows has created a series of videos on MVC and is now working on a new series based on Scott Guthrie's MVC tutorial posts (which are all in C#) but using VB instead. I was surprised to find a pointer to my recent MVC post as a "rare example" of MVC with VB (and it's only one little post so I found that to be sad) so I'm happy Bill is doing these. It gives a leg up to VB developers who find it hard to try to learn something that is VERY new and convert the C# syntax in their brain at the same time.

Here's the list of topics covered

  An Overview of the MVC Pattern
  Setting up the MVC Preview Environment
  URL Routing
  Setting up New Pages
  MVC Controller Actions 
  Creating HTML in Views 
  MVC - Putting it all together

Thanks to Beth for the heads up.

ASP.NET | VB
Wednesday, April 02, 2008 8:19:32 AM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 
 Wednesday, February 27, 2008

While I will always promote the value of being able to read C# and mentally convert it to VB  and being able to read VB and mentally convert it to C# is a skill I think all VB and C# developers should try to have, admittedly, having to do it with a whole book does sometimes get tiresome.

If you are a VB programmer and constantly plagued with having to translate from C# when you are reading advanced programming books, this list is for you.

Chris Williams has just added a page to his I LOVE VB.net website called Serious VB Booklist.

These are not books filled with Hello World samples.

He is just building up the list now, so it's light. That doesn't mean there aren't very many published, just not very many on his list yet. Let him know if you have any additions.

VB
Wednesday, February 27, 2008 3:24:57 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 
 Thursday, January 31, 2008

I followed the great walkthrough on Brad's blog showing how to use MVC together with Entity Framework. But I did it my own way - in VB, using a different database and trying to write more effective EF queries. I have a simple solution that renders these views from the AdventureWorksLT database:

Start with a list of customers who have orders in the system (that's less than 10% of the full customer list)

Then I can click on a customer and see a list of their orders

 

Then drill in to see the order details. (The Edit button is not implemented yet, in case you were wondering!)

 

So here is what's significantly different from Brad's walkthrough.

  • My EDM is created from AdventureWorksLT.
  • The relationship from AW's SalesOrderHeaders to Customer is the same as the relationship from Northwind's Products to Category. Therefore, where he use Products, I use SalesOrderHeaders and  where he uses Categories, I use Customers.
  • One of the keys for getting data easily to a view is that we need to send ONE object (and not an anoymous type) to the view. Yet what we really desire in the case of the List of Order (which also has the Customer Name) and the list of details (which also has data from the order and the customer) is an object graph.

So on the 2nd page, I need to pass in an set of orders with their related Customer entity so that I can have access to the customer name.

On the 3rd page, I need to pass in a set of order details with their related Order entiteis AND the order's related customer.

Brad achieves this by starting with the desired entity and then using entity references and some scary looking LINQ to Entities queries.

List<Product> products =TheProducts.Where(c => c.Category.CategoryName == category).ToList();
//prepare the view by explicitly loading the categories  
products.FindAll(p => p.Category == null).ForEach(p => p.CategoryReference.Load()); 

Only because I've spent a lot of time with LINQ to Entities, do I happen to know a little trick.

If I start with the "parent", i.e. category and query it's property collection (i.e. products), when I return the property collection, the "parent" entity is still attached.

So taking Brad's query, I can get the same effect with this query (still in C#):

     var _prod = Northwind.Categories.
              Where(c => c.CategoryName == id).
              OrderBy(c => c.CategoryName).
              Select(c => c.Products).
              First().ToList(); 

(Update: turns out this only works because of a bug which will get fixed - see THIS POST for an even better way!)

This returns a list of products that belong to the category. However I do not have to do any extra loading to get to Product.Category.CategoryName. Beasue my query began with the Category, it's already there. (I learned this by trial and error by the way.)

Therefore, in my SalesOrderController (my versoin of his productController), the List ControllerAction code is a little different.

I use the same type of querying to get the order details.

Another thing that I spent some time thinking about and asking about was about the ObjectContext. In a web app you want an ojbectcontext to be as short-lived as possible. I notice that Brad was instantiating in the class level declarations. This is okay because in the background, MVC  instantiates  the class for each ControllerAction and then disposes it when it's finished. It doesn't hang around waiting for another method call. (This is one of the key premises of MVC. As Kevin Hoffman explained to me, it works in "short bursts" long enough to get something out to the browser. I have much to learn!)

Brad uses the CategoryName as the basis for the view creation so that he gets a pretty URL http://host/products/list/Beverages.

I'm still seeing if there is a way around this, but I don't like querying on a string like this. I like my keys! So I'm passing in CustomerID and SalesOrderID and my urls aren't as pretty.

Here is what my controller looks like and you can see my queries that populate the Customer List, the customer's order list and the order's detail list.

Imports System.Web.Mvc
Imports System.Linq
Imports MvcApplication.awModel
Namespace MvcApplication.Controllers 

  Public Class SalesOrdersController
    Inherits System.Web.Mvc.Controller
    <ControllerAction()> _
    Sub Index()
      REM Add Action Logic here
    End Sub
    'example URL:http://localhost:xxxx/SalesOrders/Customers
    <ControllerAction()> _
    Public Sub Customers()
      Using aw = New awEntities
        Dim _customers = aw.Customers. _
Where(Function(c) c.SalesOrderHeaders.Any). _
OrderBy(Function(c) c.CompanyName).ToList RenderView("Customers", _customers) End Using End Sub
    'example URL:http://localhost:xxxx/SalesOrders/List/[CustomerID]
    <ControllerAction()> _
    Public Sub List(ByVal id As String)
      Using aw = New awEntities
        Dim _salesorders = (From cust In aw.Customers _
                          Where cust.CustomerID = id _
                          Select cust.SalesOrderHeaders).FirstorDefault.ToList
        RenderView("SalesOrdersbyCustomer", _salesorders)
      End Using
    End Sub
    'example URL:http://localhost:xxxx/SalesOrders/List/#### (order number)
    <ControllerAction()> _
    Public Sub Detail(ByVal id As String)
      Using aw = New awEntities
        Dim _order = (From ord In aw.SalesOrderHeaders.Include("SalesOrderDetails.Product") _
                     Where ord.SalesOrderID = id _
                     Select ord).First
        Dim _order2 = (From cust In aw.Customers.Include("SalesOrderHeaders.SalesOrderDetails.Product") _
                   Where cust.SalesOrderHeaders.Any(Function(so As SalesOrderHeader) so.SalesOrderID = id) _
                   Select cust.SalesOrderHeaders).First.ToList.First
        RenderView("SalesOrder", _order2)
      End Using
    End Sub 

  End Class
End Namespace 

The markup is not really different from Brad's since I can drill from the ViewData into my references (Customer, Product, SalesOrder) thanks to my queries (which make me feel so clever!)

The last page just uses tables to do the trick.

<asp:Content ID="Content1" ContentPlaceHolderID="MainContentPlaceHolder" runat="server">
<h2><%=ViewData.Customer.CompanyName%></h2>
<h3>
<%=String.Format("Sales Order #: {0}", ViewData.SalesOrderNumber)%>
<%=String.Format("Order Date: {0:d}", ViewData.OrderDate)%>
<%=String.Format("Order Total: {0:C2}", ViewData.TotalDue)%>
</h3>
 <table>
 <tr><td style="width: 225px">Product</td><td style="width: 154px">Quantity</td>
   <td style="width: 256px">Line Item Total</td></tr>
            <% For Each detail As awModel.SalesOrderDetail In ViewData.SalesOrderDetails%>
            <tr>
               <td style="width: 225px"><%=detail.Product.Name%></td>  
               <td align="center" style="width: 154px"><%=detail.OrderQty%></td>
               <td style="width: 256px"> <%=String.Format("{0:C2}", detail.LineTotal)%></td>
            </tr>
        <% Next%>
    </table>
</asp:Content>

As an aside, this was my first time really playing with client side code in VS2008 and I am enamored of all the intellisense in there!

Thursday, January 31, 2008 3:03:52 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [2]  | 
 Wednesday, January 23, 2008

Bill McCarthy explained this to me in an email.

He wrote about it in this excellent article in Visual Studio Magazine "Drill Down on Anonymous Types".

But it still bit me in the ass!

So I am stamping this into my memory:

in a query results in
...select cust.CustomerID.cust.CompanyName...  Immutable anonymous type
...select New With {
                                .CustomerID=cust.CustomerID,
                                .CompanyName=cust.CompanyName} ...
Mutable anonymous type
...select New With {
                             Key .CustomerID=cust.CustomerID,
                             Key.CompanyName=cust.CompanyName} ...
Immutable anonymous type
Direct creation of object  
New With {
                 .CustomerID=cust.CustomerID,
                 .CompanyName=cust.CompanyName}
Mutable anonymous type
New With {
                  Key .CustomerID=cust.CustomerID,
                  Key .CompanyName=cust.CompanyName}
Immutable anonymous type

And just in case someone who really needs this stumbles on this post, two clarifications:

  1. All anonymous types are immutable in C#.
  2. Mutable means changeable, editable. Immutable means written in stone.
VB
Wednesday, January 23, 2008 4:48:31 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [3]  | 
 Sunday, December 30, 2007

While there are lots of awesome new features in VB9 and C#3.0, everything has taken a back seat to LINQ. I am, however, growing more and more fond of extension methods. Read more...

[A new DevLife post]

dotNET | VB
Sunday, December 30, 2007 4:50:10 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 
 Sunday, December 23, 2007

Last spring, I ran into Don Box in the hallways at MIX07.

I hadn't seen him in quite a while and asked what he had been up to lately in his wizard's tower.

Though he couldn't really talk about what he was doing, he did say that he was working on some cool new stuff and having fun.

I had a quick fantasy about suggesting in a blog post that perhaps Don was working on the next version of Visual Basic, which would have been, of course, a completely insane idea. Don has a bit of a history with VB and I have a bit of a history of calling him out for public jibes at VB (which were likely more innocent than they sounded....).

On the other hand, it was definitely painful to sit in a WCF session a number of years ago where he had been clearly coerced to code in VB (after he was swayed to actually use Visual Studio rather than emacs in public ;-)), though the audience was composed of about 95% C# programmers. I felt badly for him, though he managed to have a little fun with it.

Again, it was no more than a silly idea that crossed my mind, provide me with a momentary evil grin and then it was forgotten, because it could so easily be taken out of context.

Who knew my silly little idea was closer to the truth than I could ever have dreamed? Not to suggest that he is, in fact, working on VBX, but he certainly is whistling (or is that singing) a different tune when it comes to VB these days.

Check out this Channel 9 video with Don Box, Chris Anderson and Amanda Silver!

You can read more of Don's new-found VB love over on his blog (and look for some posts about the fact that his team is hiring!)

VB
Sunday, December 23, 2007 12:26:35 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 
 Thursday, October 04, 2007

There is a lot of potential for using nullables incorrectly in VS2008 in Visual Basic without even realizing there's a problem, or encountering a problem that makes no sense.

Check Bill McCarthy's article in the October issue of visual Studio Magazing (not online yet but watch this space) is really helpful for pointing out gotchas.

I had a conversation with Bill recently where i was asking some of these questions and he was happy to be able to say "actually, I just wrote an article about that and it will be out soon."

Ahh, serendipity.

VB
Thursday, October 04, 2007 11:22:56 AM (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]  | 
 Tuesday, September 18, 2007

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]  | 
 Sunday, September 16, 2007

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]  | 

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]  | 
 Tuesday, September 04, 2007

It took me a while to figure out what caused this problem, so I thought I'd share it here.

I was trying to write a query in VB to grab customers who have orders placed after July 1, 2007.

The query looks like this:

Dim q = From cust In nwentities.Customers _
  Where (cust.Orders.Any(Function(o) _
     o.OrderDate > New DateTime(2007, 7, 1)))

But it would not compile. I have Option Strict On and the error was

Option Strict On disallows implicit conversions from 'Boolean?' and 'Boolean'.

What the heck was this "Boolean?". Not google-able, that's for sure!

Then I noticed that OrderDate was being defined as a "Date?".

What I'm seeing is the shortcut for Nullables in VB. It's really hard to google for that. But if you look it's everywhere! Such as in this post by Bill McCarthy. (See Bill? I found it all by myself! :-))

OrderDate is nullable. "Date?" means nullable date.

And the VB's compiler is casting the entire expression to a nullable Boolean then telling me "Yo! A Nullable boolean is NOT the same as a boolean! Sorry".

Note that the designer showed Nullable as false but the actual database has Nullable=true.

So, while I can write a regular query with this model, such as:

  Dim ords = From ord In nwentities.Orders Where ord.OrderDate > New DateTime(2007, 7, 1)

and I can write my exact query in C# with no worries:

   var q=from cust in nw.Customers where cust.Orders.Any(o=>o.OrderDate>new DateTime(2007,7,1)) select cust;

I believe that in my scenario, I just need to rethink my query. But later...

Tuesday, September 04, 2007 5:09:37 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [1]  | 
 Friday, August 31, 2007

Well, sonny is me, actually.

I spent quite a long time trying to understand why my LINQ average function wasn't working.

Paring down to a simple example, I wanted to check the average of a filtered subset of data.

Say my data is an array if integers:

int[] nums = { 84, 123, 101, 94, 238 };

I can get the average of these by calling:

var numavg=nums.Average();  --returns 128.0

I then wanted to get the average of only those numbers that were greater than 100.

But wait, I really started this in VB! So I was writing this function:

Dim avgnums = mynums.Average(Function(n) n > 100)

Which compiled and ran, but returned -0.333333333333.

I looked at it inside and out, over and under and could not figure out what was going on.

so then I tried it in C# and got this compiler error:

Cannot convert lambda expression to delegate type 'System.Func<int,int>' because some of the return types in the block are not implicitly convertible to the delegate return type 

Finally I noticed the other error listed in the Errors window:

Cannot implicitly convert type 'bool' to 'int'

And realized the error of my ways.

I was thinking that my lambda was for filtering, but all I was doing was an eval which was returning trues and falses depending on whether the value was >100 or not. And the average function was the same as Average(0,0,0,0,-1,-1) which is -2/6. So -0.333333333 was correct!

[note - Option Strict was off which imacted this - see additional comments at the end of this post]

So what is the correct way to do what I wanted? I need a filtering method, duh!

in C#:

var numavg3 = nums.Where(q => q > 100).Average();

in VB

Dim avgnums2 = mynums.Where(Function(n) n > 100).Average(Function(n) n)

About VB's Option Strict in this example

As Bill McCarthy points out, Option Strict=On would have caught the attempt to use a boolean in the Average function, forcing me to specifiy the types in the array, which I *had* in fact done in the C# code, (because it forced me to). Option Strict is one of those things you need to change to "ON" after you install Visual Studio. I had missed it in this installation and because I was focused on what I was doing wrong with the lambdas, it hadn't even occurred to me.

Dim mynums() As Integer = {10, 20, 40, 100, 120, 140}

Otherwise, he compiler tells you: "Option Strict on requires all variable declarations to have an 'As' clause." Which is a lie. There are cases where it is required and cases where it is not. Note my VB code for calculating the average is still valid and avgnums2 is recognized as a Double at compile time.

On the other hand, VB's implicit casting is still in play as is demonstrated by then taking that double and concatenating it with a string.

 Dim x = avgnums2 & "xya"

VB
Friday, August 31, 2007 4:54:05 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 
 Sunday, August 26, 2007

I was looking at a LINQ Query that I wrote when I was first exploring LINQ in depth at the beginning of the year.

This example was to see how to create a named anonymous type like this:

Dim query= From s In nw.Suppliers _
    Order By s.CompanyName _
    Select Supp = New With {s.CompanyName, s.Country, s.Products}

Looking at it today though I could not see what creating a name (Supp) for the new anonymous type was buying me in this particular case.

Its results were no different than

Dim query = From s In nw.Suppliers _
 Order By s.CompanyName _
 Select s.CompanyName, s.Country, s.Products

And whether I iterate through the results or bind them to a data control, there's no still no difference.

So I wanted to make sure i was clear on when naming the anonymous type was beneficial.

One great example is in the (dog-eared by now) LINQ article that Anders Hejlsberg and Don Box wrote in February where the LINQ query results in objects with child objects. I've used the idea behind their couple/wife/husband example to create my own query where I combine data into new types.

For example, this query lets me create a type from some product data with a new sub-type (anonymous) of SupplierHighlights. The sub-type has only the properties Name and Country.

Dim MyQuery = From p In db.Products _
Select p.ProductName, p.QuantityPerUnit, _
SupplierHighlights = New With {.Name = p.Supplier.CompanyName, .Country = p.Supplier.Country}

Then I can access the new type and sub-type data because the subtype has a name.

For Each q In MyQuery
     Console.WriteLine("Prod Name: {0},Quantity Per Unit: {1}, Supplier: {2}, Country: {3}", q.ProductName, q.QuantityPerUnit, q.SupplierHighlights.Name, q.SupplierHighlights.Country)
    Next

Sure, I could have just created properties called SupplierName and SupplierCountry, but having the child type SupplierHighlights not only makes me more organized, but gives me the ability to do further queries if I wanted to get all of the supplier info out of the product query. This is why I am considering SupplierHighlights to be a type rather than just a property within a complex type and I should point out that I could be incorrect in calling it a type, but will stick to it until I find better evidence that it's not. Because I have everything already grouped into a supplier type, I don't have to list each of the fields (SupplierName, SupplierCountry), I can just query :

   Dim nextQuery = From prod In query Select prod.SupplierHighlights
    For Each s In nextQuery
     Debug.WriteLine(s.Name, s.Country)
    Next

So, here creating the named type (I really want to call it a named anonymous type. I wonder if that's accurate?), it is really useful and necessary. Try creating the sub type without a name, and you'll see what I mean!

Another place to leverage the named type SupplierHighlights is in databinding. I can databind directly to prod.SupplierHighlights and automatically get all of the columns. In databinding to the properties of the base query results (ProductName, QuantityPerUnit), I have to bind to "MyQuery", so even if I had named that type, I don't use that name anyway.

Also,  take a look at this post by Paul Vick about mutable and immutable anonmyous types in VB9.

(The italicized text are a result of feedback from Bill McCarthy, who is constantly (and generously) attempting to drag me down into the depths of complexity of VB.)

Sunday, August 26, 2007 6:08:14 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 
 Wednesday, August 22, 2007

At first glance, the new ImpliciltyTyped Local Variables in C#3.0 and VB9 might look familiar to VB programmers.

Earlier (that would be "current" as well) versions of VB can do a lot of implicit stuff. This has been a point of aggravation to many non-VB programmers, but I guess they have a new perspective. Next thing you know they'll be using declarative languages! Eek!

In VB2005, we can create an integer implicitly and do integer-like things with it.

  Dim ii = 5
  Console.WriteLine(ii + 2)

Or we can do the same with something more complex like a FileInfo class. I'm not explicitly declaring files, but VB infers it from what is returned by New DirectoryInfo.GetFiles (an array of FileInfo objects).

   Dim files = New IO.DirectoryInfo("C:\").GetFiles

We can even go a bit further with an iteration, using f to represent each FileInfo object without explicitly defining it.

   For Each f In files
       Console.WriteLine(f.FullName)
   Next

All of this runs just fine.

But, if you were actually typing this code, you would notice that the compiler does not comprehend the types at design time.

That's because it can't really identify the type at design time. All that's happening here is that we are getting late binding.

The undeclared f (FileInfo) doesn't even give you this much help with late binding. No method or property suggestions and it's just an object.

So here is where VB9 is definitely different. We are truly getting strong typing, not just the intelligence of late binding.

When using the implicit variables in VB9, the compiler is smarter and much more helpful!

In the For Each, f is now recognized as a FileInfo and gives intellisense as well.

Note that all of this is using the default VS2008 properties for Compiler Options. Jim Wooley has an interesting blog post about Option Infer (a new option) which allows the strong typing to occur.

VB
Wednesday, August 22, 2007 3:57:19 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 
 Sunday, July 08, 2007

I wanted to see some of the new Beta2 (in the June CTP bits) functionality of Entity Framework so I've been poking around. You can see a nice list of what to look for on Danny Simmons' blog.


One thing that was a huge source of frustration was the inability to support database constraints where a column was both a primary key and also a foreign key of another table. When using the EF Wizard to import database schemas, this would be part of a list of errors that were generated during the model creation and would result in a problem like this:

Notice that you can't get back to the ORDER that owns the Order_Detail.

With the new CTP, that problem goes away.

You can't traverse over Order_details because it is a  collection property, but you can get at properties like Count, etc...

Dim od = From o In NWEntities.Orders Where o.ShipCountry = "Spain" _
Select o.Customers.CompanyName, o.Order_Details.Count
    


Another thing, of course was the difficulty of working with existing views and stored procedures.

Here's a before and after of the wizard:

 

      

But the sproc and view support goes much deeper.

Unfortunately, I don't see the wizard carrying the views and stored procs all the way through. If you look at the SSDL, you will see that the views are described as any other table. The stored procs are <function> elements.

While I can't figure out how to surface the query function, there are also functions to let you use your own sprocs for insert, updates and deletes and tie them into EF's SaveChanges function. Shyam Pather wrote a post about this a while ago on the ADONET Team blog and now that stuff is in this version along with documentation examples.

Another SPROC goody is the ability to define your own stored procedures in your model without  needing it in the database. Check the DefiningQuery element that goes in the SSDL to achieve this.


Poking around in the CSDL, I see a few changes which tie back to the references.

A Key used to be an attribute of an Entity Type. Now it is an element.

Beta1:   <EntityType Name="Employees" Key="EmployeeID">

CTP:   <EntityType Name="Employees">
    <Key>
      <PropertyRef Name="EmployeeID" />
    </Key>


So that they could do this:

 <EntityType Name="Order_Details">
    <Key>
      <PropertyRef Name="OrderID" />
      <PropertyRef Name="ProductID" />
    </Key>

Which then enables us to do this:

  <Association Name="FK_Order_Details_Orders">
    <End Role="Orders" Type="Model.Orders" Multiplicity="1" />
    <End Role="Order_Details" Type="Model.Order_Details" Multiplicity="*" />
    <ReferentialConstraint>
      <Principal Role="Orders">
        <PropertyRef Name="OrderID" />
      </Principal>
      <Dependent Role="Order_Details">
        <PropertyRef Name="OrderID" />
      </Dependent>
    </ReferentialConstraint>
  </Association>
  <Association Name="FK_Order_Details_Products">
    <End Role="Products" Type="Model.Products" Multiplicity="1" />
    <End Role="Order_Details" Type="Model.Order_Details" Multiplicity="*" />
    <ReferentialConstraint>
      <Principal Role="Products">
        <PropertyRef Name="ProductID" />
      </Principal>
      <Dependent Role="Order_Details">
        <PropertyRef Name="ProductID" />
      </Dependent>
    </ReferentialConstraint>
  </Association>

Which is describing this from the database:


 Speaking of the database, there was mention of the fact that it's now easier to get at the underlying datastore. I haven't figured out how though, yet. I looked to see if you could now cast an EntityConnetion to a SqlConnection but that's not it. I'll keep an eye out for that. While I was looking though I noticed a bunch of new methods in the ObjectContext such as the pieces that go along with attaching and detaching such as "Addto" methods for each of the entities in the objectContext.


 The last thing I'll shove into this already long post is that Entity constructors are no longer auto-generated. This will make customization a lot easier (you can read about that in this post from Danny Simmons).

So where we had this in Beta 1:

  Partial Public Class Employees
        Inherits Global.System.Data.Objects.DataClasses.Entity
        '''<summary>
        '''Initialize a new Employees object.
        '''</summary>
        Public Sub New()
            MyBase.New
            'Call DoFinalConstruction if this is the most-derived type.
            If (CType(Me,Object).GetType Is GetType(Global.NorthwindModel.Employees)) Then
                Me.DoFinalConstruction
            End If
        End Sub
        '''<summary>
        '''Initialize a new Employees object.
        '''</summary>
        '''<param name="employeeID">Initial value of EmployeeID.</param>
        '''<param name="lastName">Initial value of LastName.</param>
        '''<param name="firstName">Initial value of FirstName.</param>
        Public Sub New(ByVal employeeID As Integer, ByVal lastName As String, ByVal firstName As String)
            MyBase.New
            Me.EmployeeID = employeeID

            Me.LastName = lastName

            Me.FirstName = firstName

            'Call DoFinalConstruction if this is the most-derived type.
            If (CType(Me,Object).GetType Is GetType(Global.NorthwindModel.Employees)) Then
                Me.DoFinalConstruction
            End If
        End Sub

Although the constructor is gone (along with the acrobatics that would require creating your own constructors) there is a new method for creating each entity:

     Public Shared Function CreateEmployees(ByVal employeeID As Integer, ByVal lastName As String, ByVal firstName As String) As Employees
            Dim employees As Employees = New Employees
            employees.EmployeeID = employeeID
            employees.LastName = lastName
            employees.FirstName = firstName
            Return employees
        End Function

Sunday, July 08, 2007 4:48:59 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 
 Sunday, July 01, 2007

My first MVP award was on July 1, 2003. I received an email this morning that I have been awarded an MVP (Developer Visual Basic) again. This is my 5th. I am always a little surprised since I never really know year to year what the criteria is. I just do what I do and if that makes Microsoft happy, I find out on July 1st.

I was smart this year to spend every last dollar of the past year's Microsoft bucks before the end of June. When I was down to $5, I found these cute little "beanie baby" type MSN butterflies for $2.50 each. They will be a fun thing to give to someone's kid. Not bad for a non-shopper like myself. My nephews got some pc games and Office 2007 Home & Student edition. I was able to get copies of Office for my sister as she expands her business. It's nice that I can share this benefit with my family, whom I steal time to go to conferences, run my user group, etc. Before the airlines got more strict with their carry-on rules, my husband's benefit was that I brought home a 6-pack of local brew in my backpack when I did INETA speaker trips to user groups around the country.

Sunday, July 01, 2007 12:50:29 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [7]  | 
 Thursday, June 21, 2007

I recently whipped together a small tool for a client and because it was so small (and for only one person to use), I used some simple drag n' drop wizardry for the form. I created a datasource pointing to a table in the SQL Server 2005 database on my develpment machine then dragged that datasource onto a form to create a navigation toolbar and a screenfull of text boxes and checkboxes.

When I finished it up and pointed it to the live SQL Server 2000 database I was having some strange issues with inserts and updates. Specifically, SQL Server was throwing this error: "Error converting data type varchar to bit."

A little digging showed me that the TSQL being sent to the databse was passing "True" and "False" to the bit fields rather than 0 and 1.

SQL Server 2005 didn't seem to mind this, but SS2000 sure did!

I looked at the dataset that was created by the wizard and the bit fields were properly identified as booleans.

But when I looked at the parameters collections of the Insert and Update statements that the wizard had built, I could see that the properties of my boolean values was set to DbType=AnsiString.

So even though I prefered to know why I was seeing a difference between SS2005 and SS2000, I modified the auto-generated Insert & Update statements to make the DbType "Boolean" which passed 0's and 1's up to the database and everyone was happy. Changint the DbType to Boolean automatically changed the ProviderType to "Bit". 

I have seen a few threads where people pointed out this problem but never saw any suggest my solution or provide a reason for SS2000 rejecting it. If this wizard is meant to be used by newbies, I don't know how they would deal with a problem like this.

 

dotNET | VB
Thursday, June 21, 2007 1:07:18 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  |