July 2007 Entries



Managed to get the new http://www.TheWPFDirectory.com site launched today!  Does not have a lot of content yet, but hopefully soon, takes time.

Now we can add that to the others that recently launched:

http://www.TheWPFDirectory.com

http://www.TheSilverlightDirectory.com

http://www.SilverlightCIty.com

Yes, they all seem to have a similar look :)

Hopefully this fall we will get http://www.DevsForHire.com up and running!




Just launched http://www.TheSilverlightDirectory.com site today!  It has been a bit of work, but the site appears to be fully functional. If you happen to run into any issues, but sure to let me know!

Now the real works begins, adding all the content.  Looks like a busy time for a couple of weeks

If you happen to have or know of an article, product, tutorial, demo or even websites and blogs that are focused on Silverlight, adding them to the listing would be appreciated!

 BTW, the http://www.TheWPFDirectory.com site will launch on July 30th.  It will have the same looks and features as the Silverlight Direcotry, but will focus only on WPF.  If you happen to have a blog about WPF or products, artcles, etc., be sure to check it out on the 30th.  The RSS feed for the WPF Directory will not be active utnil the 30th, but you can add it by using the URL http://www.TheWPFDirectory.com/Rss.aspx and be ready when it does launch!




I have heard a number of developers fear that LINQ 2 SQL would cause less mature developers to employ bad practices.  That is a very likely thing to happen as this new technology makes it easy for novices to build database access.  We just do not need to throw the baby out with the bath water, in professional environments, management will need to keep an eye on issues.   If managed properly this should not be an issue, but unchecked, you could end up with a mess!

Today, I was playing around with navigating related tables.  I built three simple tables and populated them as:


Categories
{
    Category_ID
    Title
}

1, Business
2, Games
3, Utilities
4, Development

Posts
{
  Post_ID
  Subject
}

1, Zork for Business
2, Visual Studio
3, Notepad
4, File Manager

PostsCategoryListItem
{
  Category_ID
  Post_ID
}

1,1
2,1
1,2
4,2
3,2
3,3
3,4


Okay, that is all simply enough, it is two tables with a cross index table .  Now, handling this in LINQ 2 SQL via VS Orcas Beta 1 is a simple enough task.  I built this as a Windows Form project that had a button to run the query and a TextBox to display the results.  Here is the sample code:


DataClasses1DataContext db = new DataClasses1DataContext();

var result = from p in db.Posts
                     select p;

DisplayOutput.Text = "Results:\r\n\r\n";

foreach (Post post in result)
{
  string categories = "";

  foreach (PostCategoryListItem li in post.PostCategoryListItems)
  {
    categories += li.Category.Title + " ";
  }

  DisplayOutput.Text += string.Format("Post: {0} - {1}\r\n", post.Subect, categories);
}


That does not seem like much and makes it really easy to navigate these three tables.  The goal here is to simply list out the subject of each post followed by the list of categories to which it belongs.  You run it and you get just the results you would expect:

Results:

Post: Zork for Business - Business Games
Post: Visual Studio - Business Utilities Development
Post: Notepad - Utilities
Post: Filemanager - Utilities

Now, what exactly happened in this query?  It may not be obvious, but it did not just query with a join as some might think.  With the delayed execution built into LINQ 2 SQL, the first query is when you iterate the posts in the first foreach.  That sends the query:

SELECT t0.Post_ID, t0.Subect
FROM Posts AS t0

Next, when we iterate the PostCategoryListItems collection in the next foreach, it send a query on the PostCategoryListItem table for all items with the first Post.Post_ID:

exec sp_executesql N'SELECT t0.Category_ID, t0.Post_ID
FROM PostCategoryList AS t0
WHERE t0.Post_ID = @p0',N'@p0 int',@p0=1

After that it will send a query to get the Category information for each of the results of that query:

exec sp_executesql N'SELECT t0.Category_ID, t0.Title
FROM Categories AS t0
WHERE t0.Category_ID = @p0',N'@p0 int',@p0=1

exec sp_executesql N'SELECT t0.Category_ID, t0.Title
FROM Categories AS t0
WHERE t0.Category_ID = @p0',N'@p0 int',@p0=2

That is the first iteration of a post.  For the next post, the iteration of the PostCategoryListItems will send a query out to get all the entries for the post as it did the first time:

exec sp_executesql N'SELECT t0.Category_ID, t0.Post_ID
FROM PostCategoryList AS t0
WHERE t0.Post_ID = @p0',N'@p0 int',@p0=2

But this time, it will not send out queries for any of the categories that it already knows about.  In the previous iteration, it send out queries for categories “1:Business” and “2:Games”.  For this post, it does not need to send a query for the Category information for “1:Business” since it already has it, so it just sends queries for “3:Utilities” and “4:Development”:

exec sp_executesql N'SELECT t0.Category_ID, t0.Title
FROM Categories AS t0
WHERE t0.Category_ID = @p0',N'@p0 int',@p0=3

exec sp_executesql N'SELECT t0.Category_ID, t0.Title
FROM Categories AS t0
WHERE t0.Category_ID = @p0',N'@p0 int',@p0=4

After this iteration, it will have all the categories in memory so that it does not need to send queries to obtain that information in subsequent iterations.  For the last two iterations of PostCategoryListItems for the remaining two posts, it will send out the query to retrieve the PostCategoryListItems, for each post as:

Next Iteration:

exec sp_executesql N'SELECT t0.Category_ID, t0.Post_ID
FROM PostCategoryList AS t0
WHERE t0.Post_ID = @p0',N'@p0 int',@p0=3

And last Iteration:

exec sp_executesql N'SELECT t0.Category_ID, t0.Post_ID
FROM PostCategoryList AS t0
WHERE t0.Post_ID = @p0',N'@p0 int',@p0=4

Notice, no queries for the “3:Ultities” category since we already have it in memory.

If the category list was large say thousands and each post was tagged with say thirty or forty categories, this innocent little chunk of code could cost thousands or even tens of thousands of queries on the database.

The moral of the story is to take care “how” you right your queries and make SQL Server Profiler your best friend

 

 .



Just spent a number of hours trying to figure out why my Linq was not updating my foreign key when adding a record with a one-to-one relationship.  Do not know if it is an issue or by design, but it took me a while to try the obvious....

Here is the simple tables I was using in this test:

 


 

Table Contacts
(
   Contact_ID [int] IDENTITY(1,1) NOT NULL,
   CreatedAt] [datetime] NOT NUL,
   FullName [nvarchar](128) NOT NULL,

CONSTRAINT [PK_Contacts] PRIMARY KEY CLUSTERED
(
   Contact_ID ASC
)

Table Comments
(
   Comment_ID [int] IDENTITY(1,1) NOT NULL,
   Contact_ID [int] NOT NULL,
   LastEditedAt [datetime] NULL,
   Comment  [nvarchar](1024) NULL,
 CONSTRAINT [PK_Comments] PRIMARY KEY CLUSTERED
(
   [Comment_ID] ASC,
   [Contact_ID] ASC
)
 

Table AdditionalInfo
(
   Contact_ID [int] NOT NULL,
   LastUpdatedAt [datetime] NULL,
   AdditionalInfo [nvarchar](1024) NULL,
 CONSTRAINT [PK_ContactAdditionalInfo] PRIMARY KEY CLUSTERED
(
  Contact_ID ASC
)

 


 

The table "Comments" has a one-to-many relationship with "Contacts" and the "AdditionalInfo" table has a one-to-one relationship to "Contacts". 

When these tables are pulled into the designer in VS 2008 beta 1, it seems to generate the correct code for them.  Adding a new contact and a comment is easy enough and works correctly (I prefixed all the table entities with "Our":

 


 

ContactClassesDataContext db = new ContactClassesDataContext();

OurContact contact = new OurContact
{
   FullName = "Ben Here Too",
   CreatedAt = DateTime.Now,
   Comments =
  
{ new OurComment
      { LastEditedAt = DateTime.Now, 
        Comment = "Typical Comment" } }
 };

db.OurContacts.Add(contact);
db.SubmitChanges();


 

So, since there is a one-to-one relationship to the AdditionalInfo table, it seemed we only had to set the property to a new instance of the OurAdditionalInfo and it would be good to go, like:

 


 

ContactClassesDataContext db = new ContactClassesDataContext();

OurContact contact = new OurContact
{
   FullName = "Ben There Too",
   CreatedAt = DateTime.Now,
   Comments =
   { new OurComment
      { LastEditedAt = DateTime.Now, 
        Comment = "Still no Joy" } }

  AdditionalInfo = new OurAdditionalInfo
   {
          LastUpdatedAt = DateTime.Now,
          AdditionalInfo = "Some new Stuff",
    } ;

 };

db.OurContacts.Add(contact);
db.SubmitChanges();

 


 

No such luck, it kept blowing errors on the primary key (contact_ID) as Linq was setting it to zero.  So, something was different between the one-to-one and the one-to-many relationships.  After much banging around with the code, I finally decided to try setting the "contact" property on the new OurAdditionalInfo object such as:

 


 

 ContactClassesDataContext db = new ContactClassesDataContext();

 OurContact contact = new OurContact
  {
        FullName = "Ben Here Finally"
        CreatedAt = DateTime.Now,
        Comments =
       {  new OurComment
           { LastEditedAt = DateTime.Now, 
             Comment = "Simple, answer" } }
 };

 contact.AdditionalInfo = new OurAdditionalInfo
  {
      LastUpdatedAt = DateTime.Now,
     AdditionalInfo = "Some new Stuff",
    Contact = contact
 } ;

 db.OurContacts.Add(contact);
 db.SubmitChanges();


That seemed to do the trick!  Like I said, not sure if this is by design, but it would make more sense to me to not have to set the contact the same as you can do with the comment entries.  Just glad I found that and can move on .