|
|
Insights and observations regarding LINQ
-
On page 216 of LINQ in Action, I made a comment that unmapped properties in a mapped class cannot be used in a LINQ to SQL projection. This was true with the beta bits, but only partially true with the release bits. To begin, let's consider the Author table we have in the book samples.
The Author class has separate fields for the first and last name. Each of these is mapped to the corresponding fields in the Author table. In the book, we show how you can create a read only property in a partial class (so that it won't get clobbered when we regenerate our classes in the designer). The new property is trivial:
Partial Public Class Author Public ReadOnly Property FormattedName() As String Get Return Me.FirstName & " " & Me.LastName End Get End Property End Class
Notice here that there are no mapping attributes to this property. In part, that is because there is no corresponding field in the table. As we show in the book, you are free to query the author table and return Author objects. From there, you can display the FormattedName as follows:
Dim authors = From a In context.Authors _ Select a For Each a In authors Console.WriteLine(a.FormattedName & "; " & a.WebSite) Next
This works fine because we are projecting the complete Author type. However, in early builds, we couldn't project the unmapped properties into an anonymous type like this:
Dim authors = From a In context.Authors _ Select a.FormattedName, a.WebSite
If you tried to use this projection, you would get a runtime exception. In the RTM bits, the behavior was modified. Now, if you try to run the above query (sample 8.25 in the book samples for anyone following along). You will see that the query succeeds and the anonymous type is used. So how can they know how to populate the FormattedName when it is not mapped and doesn't exist in the table itself? No, the provider doesn't look inside the property, determine the mapped properties that are used, and fetch them. While that could work in our simple example, many unmapped properties would use significantly more resources, many of which may not be members of our class, or methods without direct translation in TSQL. If you look at the generated SQL that is issued when the query is consumed, you might be able to figure out what is happening in this case.
SELECT [t0].[ID], [t0].[LastName], [t0].[FirstName], [t0].[WebSite], [t0].[TimeStamp] FROM [dbo].[Author] AS [t0]
Notice here, our select clause to the database is not optimized to only return the fields we requested. Instead, all of the fields are returned. So what's going on? They discovered in evaluating the Select clause that there were unmapped properties. At that point, they just turned around and populated a full author object. Using this object, the provider turns around and generates the anonymous type from the object rather than directly from the underlying data store directly. It's a bit of smoke and mirrors at this point.
So the question that came up asks if the next printing of the book needs to be adjusted to remove the statement that you can't project an unmapped property. While you can indeed project these properties, you can't use them elsewhere in the query. Thus if you wanted to sort the data based on the unmapped property, the exception would be thrown. Consider the following query./p>
Dim authors = From a In context.Authors _ Order By a.FormattedName _ Select a.FormattedName, a.WebSite
In this case when we try to run it, we get the following error:
"System.NotSupportedException: The member 'LinqInAction.LinqBooks.Common.VB.SampleClasses.Ch8.Author.FormattedName' has no supported translation to SQL."
Because of this, I plan to leave the note in the chapter warning you of using the unmapped property in your query. Unfortunately, I don't have enough space in the book to insert this complete explanation at this time. I hope this explanation helps some of you who are confused at this point.
Crossposted from ThinqLinq.com.
|
-
I had a blast on the Geek Speak today. If you missed it, they will have it available on demand from their blog at http://blogs.msdn.com/geekspeak/. You can even subscribe to the audio feed using your the Zune podcasting functionality from their rss feed. One of the things I love about these events is the variety of the questions that attendees bring.
As I mentioned, the slide deck I used for the session is available in the Files section here as Linq Migration Strategies.
Below are some of the questions that came up and some links to back up my answer for your reading edification.
Q: How do you import schema into VB at the top of the file, if schema is in project it will give you intellisense?
Beth Massi and Avner Aharoni demonstrate this in the webcast available at http://blogs.msdn.com/bethmassi/archive/2008/01/18/channel-9-interview-xml-properties-and-enabling-intellisense.aspx. They also demonstrate generating the schema from a sample XML document.
Q: How would you replace an existing data layer with LINQ to SQL?
I started showing how to take the Personal Web Starter Kit and begin to LINQ enable it. The completed project is available at http://code.msdn.microsoft.com/LinqPersonalWeb.
Q: How does LINQ perform as compared to the other alternatives?
Any time you add a level of indirection there will be some performance penalty. With LINQ to Objects, they basically leverage the iterator pattern the same way the C# 2.0 Yield method works. For LINQ to SQL, the best series on performance is from Rico Mariani starting with http://blogs.msdn.com/ricom/archive/2007/06/22/dlinq-linq-to-sql-performance-part-1.aspx
Q: Where can we find patterns and practices guidance on LINQ
This post is more along the lines of the Framework Design Guidelines book, but it is good information anyway: http://blogs.msdn.com/mirceat/archive/2008/03/13/linq-framework-design-guidelines.aspx
There is also a Live from Redmond VB9 webcast discussing best practices at http://msevents.microsoft.com/CUI/WebCastEventDetails.aspx?EventID=1032337466
Additional "How do I" videos are available at http://msdn2.microsoft.com/en-us/vbasic/bb466226.aspx
Q: What advice do you have for passing data across tiers? If we can’t pass context across tiers, can we pass resultant objects?
There isn't really a single difinitive post on this one. Searching for "DataContext" and "Short lived" or "Unit of work" should supply a number of examples, including the following:
http://msdn2.microsoft.com/en-us/library/system.data.linq.datacontext.aspx
This forum post includes responses by Keith Farmer and Matt Warren who were both intimately involved with creating LINQ to SQL: http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2485909&SiteID=1. Matt states, "You'll most often want a new context for every interesting unit of work. DataContext's should be short lived if they can be. The only scenario where you'd keep a DataContext around for a long time would be if you were bulding the equivalent of a 2-tier UI based data-entry/editting appliction."
Dino Esposito notes: http://weblogs.asp.net/despos/archive/2008/03/19/more-on-datacontext-in-hopefully-a-realistic-world.aspx
Some additional resources mentioned:
Rick Strahl: http://www.west-wind.com/weblog/default.aspx
Beth Massi: http://blogs.msdn.com/bethmassi/
MSDN LINQ forum: http://forums.microsoft.com/MSDN/ShowForum.aspx?ForumID=123&SiteID=1
Q: Regarding Linq to Entities, I hear that the entities will not use the same change tracking model to permit emitting across tiers, by making the EntitySets serializable and have the changes tracked on a set by set basis, can you confirm deny? Info on this?
I don't know enough to comment on this. I would recommend asking the question on the ADO.NET Entity Framework forum at http://forums.microsoft.com/MSDN/ShowForum.aspx?ForumID=533&SiteID=1.
Crossposted from ThinqLinq.com
|
-
Tomorrow, Wednesday 4/2/2008, I will be the guest speaker on the Geek Speak webcast. We will be discussing strategies for beginning to incorporate LINQ into your existing application infrastructure. In many cases, that does not mean replacing your entire data stack, but rather using pieces of LINQ to add functionality and in new components. Please join us. The Geek Speak webcasts are often driven by attendee questions. The more questions, the better the event.
When: Wednesday, April 02, 2008 12:00 -1:00 PM (GMT-08:00) Pacific Time (US & Canada) or 3:00 - 4:00 PM Eastern Time.
Where: Live Meeting
Registration URL:
|
-
I love it when projects take a life of their own. A while back, I posted my LINQ enabled Personal Web Starter Kit in VB and received several requests to provide a C# port. Thankfully, one brave soul stepped up and did the port for me. Thanks go to Stephen Murray for undertaking the challenge. As is often the case, one of the best ways to learn a technology is to use it.
If you're interested in this sample, you can check out the project at the MSDN code center. Specifically, you can access the original VB version or Stephen's C# Port. You can read more about this project on the Thinq Linq web site. As always, let us know what you Thinq.
|
-
At long last, the process of doing my first book is coming to a close. I started this project last March. Through the process we had to revisit our work numerous times, including each time a new CTP or Beta drop came. For me, 10 months, and Fabrice 2 years later, we found out this week that the book is going to press.
What does this mean for you, if you purchased the eBook, the final version is available now. Additionally, the samples are available online in both C# and VB. We are also making three chapters available for free if you are considering the book, but not sure yet.
If you purchase the hard copy from Manning, we understand that it should be in around the first of February. This should mean that it will be shipping from the online outlets, like Amazon by the middle of February.
I hope you find the book as rewarding to read as it did for us to write.
crossposted from www.thinqlinq.com
Crossposted from http://devauthority.com/blogs/jwooley/default.aspx
|
-
You may have seen me present it at a speaking engagement. You may have watched the podcasts. You may have even downloaded the sample application. Now you can see it in action.
ThinqLinq.com is now live.
The site was designed completely in VB with LINQ as the data access mechanism. The base application was built in 2 hours from not knowing RSS to being able to import a RSS feed, displaying it on a form and producing a new feed from the imported data. The site is a testimate to the power of LINQ and the RAD capabilities that it brings. Head on over to the site and check it out.
Crossposted from http://devauthority.com/blogs/jwooley/default.aspx
|
-
-
Among the new cool features in Visual Studio 2008, one of the best may be the XML Literal support with VB 9 and LINQ. In my last post, I mentioned some changing features from the Beta to RTM. One that could easily be overlooked is a change to the way LINQ to SQL can now directly project into XML literals.
Through the Beta cycle, there was an issue with projecting XML elements directly from a LINQ to SQL query. If you haven't seen LINQ to SQL with XML, here's a code sample that explains what I'm referring to:
Dim dc As New LinqBlogDataContext 'Formulate the Query to get the last 10 blog posts Dim query = (From p In dc.PostItems _ Order By p.PublicationDate Descending _ Take 10 _ Select p).ToArray
'Create a root Site node with 10 child "item" nodes. 'Each node will be filled in through a LINQ query Dim fooShort = <site> <%= From p In query _ Select <item> <title><%= p.Title %></title> <link>http://ThinqLinq.com/Default.aspx?Postid=<%= p.Id %></link> <pubDate><%= p.PublicationDate %></pubDate> <guid isPermaLink="false">42f563c8-34ea-4d01-bfe1-2047c2222a74:<% p.Id %></guid> <description><%= p.Description %></description> </item> %> </site>
In this code, I'm performing two queries. The first one sets up the LINQ to SQL query and pre-fetches the results into an Array. In the beta builds, if we didn't include the pre-fetching ToArray, the second query which projects the results of the first into individual <item> nodes. What is the difference between these queries? The first query uses LINQ to SQL and projects results directly from the database. Because we pre-fetch the results into an array of objects, the resulting query only uses LINQ to Objects rather than the direct LINQ to SQL implementation.
With the final RTM of Visual Studio, we no longer need to pre-fetch the results from the query. Instead, we can directly project our desired XML from the select statement without needing the intermediary step. Here is the revised code. Notice, we can now perform the same result with a single LINQ query rather than two.
Dim fooNew = <site> <%= From p In dc.PostItems _ Order By p.PublicationDate Descending _ Take 10 _ Select <item> <title><%= p.Title %></title> <link>http://ThinqLinq.com/Default.aspx?Postid=<%= p.Id %></link> <pubDate><%= p.PublicationDate %></pubDate> <guid isPermaLink="false">42f563c8-34ea-4d01-bfe1-2047c2222a74:<%= p.Id %></guid> <description><%= p.Description %></description> </item> %> </site>
The result is more concise. You may find you want to continue separating your query definition from your XML creation in order to improve maintainability. If this is the case, simply keep the first code sample and remove the call to .ToArray. Because LINQ to SQL is composable, you can separate the queries into two code sets. When the query is evaluated, the two expressions will be combined into a single query to the database and the projection will continue to work.
Enjoy working with VB 9 and XML. In my opinion it is one of the killer features of Visual Studio 2008. If you give it a try, I think you might find the same.
Technorati tags: linq, VB Visual Studio XML
Crossposted from http://devauthority.com/blogs/jwooley/default.aspx
|
-
I've just updated the ThinqLinq proof of concept site for the Visual Studio 2008 release that came out today. If you're following the sample application, or are looking for a sample VB 9 implementation of LINQ in a web site, check out the download at http://devauthority.com/files/13/jwooley/entry101097.aspx. In case you are interested, here are the changes that were necessary to move from Beta 2 to the RTM. (The first two items are repeats from my post earlier today).
- Open the DBML file as XML and change the UTF encoding from 16 to 8. Otherwise the compiler will complain about the DBML file's format.
- Replace the Add methods with InsertOnSubmit. Likewise, change Remove to DeleteOnSubmit. You may be able to do a global search and replace on this, but be careful not to make changes to any IList object implementations, only LINQ to SQL ones.
- SyndicationFeed.Load removed the option to pass in a URI. Instead, I used SyndicationFeed.Load(New System.Xml.XmlTextReader(UrlString)).
- The SyndicationFeed's Item PublishDate property is changed to the new DateTimeOffset type rather than the simpler DateTime. As a result, get the date from the PublishDate.Date property.
- When projecting XML elements as part of a LINQ to SQL query, you no longer need to pre-fetch the results into an array. I'll make a separate post on this item.
That's all I've found so far. I've already updated both the ThinqLinq site and the samples for the upcoming LINQ in Action book. I'm sure I've missed something, but so far, the upgrade is relatively easy this time around. The IDE does appear to be running faster as well.
Technorati tags: linq, orcas Visual Studio
Crossposted from http://devauthority.com/blogs/jwooley/default.aspx
|
-
It's offical, Soma annouced on his blog that the 3.5 .Net Framework along with Visual Studio 2008 have shipped. I have been excited by the things coming with this release since PDC 2005 and am glad to finally work the the official bits. I am aware of a couple breaking changes between the Beta 2 and RTM, and will try to keep you updated as I find more items.
The biggest changes to be aware of in regards to LINQ to SQL are the following:
- Open the DBML file as XML and change the UTF encoding from 16 to 8. Otherwise the compiler will complain about the DBML file's format.
- Replace the Add methods with InsertOnSubmit. Likewise, change Remove to DeleteOnSubmit. You may be able to do a global search and replace on this, but be careful not to make changes to any IList object implementations, only LINQ to SQL ones.
There are a couple other minor updates that may catch you off-guard. I'll update you once I've had a chance to play with the final bits.
Crossposted from http://devauthority.com/blogs/jwooley/default.aspx
|
-
I have set-up several feedburner syndication options for my postings for your enjoyment. Feel free to move your aggregator over to those versions of the feeds and show me how much you digg what I have to say. Here's the links for you:
Wooley's Wonderings: My main feed Jim's Samples and Presentations: My downloads and sample applications from my presentations. Subscribe here to get updates whenever I add new samples.
I have a bunch of things I want to discuss from my DevConnections trip, so stay tuned.
Crossposted from http://devauthority.com/blogs/jwooley/default.aspx
|
-
If you downloaded the VPC image version of Visual Studio 2008 beta 2, make sure to back-up your data and move anything off of the Team Foundation Server before November 1, 2007. I was just informed that the images will be time bombing on November 1 2007 rather than the originally intended March 15, 2008 date. If you don't download it now, you may not be able to retrieve your projects. More information is available at the VS 2008 beta 2 download site. If you installed the stand-alone version, you should be ok.
Crossposted from http://devauthority.com/blogs/jwooley/default.aspx
|
-
When I originally started the ThinqLinq project I began by loading the RSS feed from my DevAuthority blog, and iterating over the results adding them to the PostItems table in the data context. With LINQ this is relatively easy. Loading the XML from the feed is done with a single line:
Dim Feed As System.Xml.Linq.XDocument = XDocument.Load( "http://devauthority.com/blogs/jwooley/rss.aspx")
The xml document consists some basic informational elements which are not terribly important in this instance as we are only pulling from a single blog. Following the initial elements, the document contains a series of "item" elements that contain the actual post information. We can easily query the document and return an IEnumerable(Of XElement) object that we can iterate over and create new post items. Below is an excerpt from my original implementation.
For Each post As XElement In Feed...<item> Dim DataPost As New LinqBlog.BO.PostItem DataPost.Author = "Jim Wooley" DataPost.Description = post.Element("description").Value DataPost.PublicationDate = CDate(post.Element("pubDate").Value) DataPost.Title = post.Element("title").Value dc.PostItems.Add(DataPost) Next
Once the records are added to the table, we can commit them to the database with a single call to SubmitChanges as follows:
dc.SubmitChanges()
Ok, so that is easy enough. There are a couple of things to mention before we continue on. The .Add method will be changed to .InsertOnSubmit when LINQ and the .NET 3.5 Framework is released. This will be a breaking change for anyone who is currently working with the beta builds.
Another item of note: this implementation does not bother importing the various sub-collections, including attachments, comments, categories, etc. We will address some of those in a future update.
In looking back at the code and being more familiar with LINQ, it is about time to update this code taking advantage of some of LINQ's more interesting features. First VB 9 allows us to eliminate some of the functional construction syntax. Instead of weakly accessing the post.Element("description").Value, we can refer to post.<description>.Value. If we import the namespace, we will even get intellisense on the xml document.
In addition, rather than iterating over the item elements explicitly, we can use a LINQ query to create an IEnumerable(Of PostItem) list using the object initializers in the select projection. We then fill the entire collection using the table's AddAll (changing to InsertAllOnSubmit with RTM). With this change, we eliminate the entire for each loop. Below is the revised code:
Dim Feed As System.Xml.Linq.XDocument = XDocument.Load("http://devauthority.com/blogs/jwooley/rss.aspx")
Dim dc As New LinqBlogDataContext()
dc.PostItems.AddAll(From post In Feed...<item> _ Select New PostItem With { _ .Author = "Jim Wooley", _ .Description = post.<description>.Value, _ .PublicationDate = CDate(post.<pubDate>.Value), _ .Title = post.<title>.Value})
dc.SubmitChanges()
That's it. We've read the feed from the site, filled the object collection and saved the resulting objects to the database. Clean and simple.
But hold on... The title of this post refers to the System.ServiceModel.Syndication.SundicationFeed object. This is a new object as part of the WCF enhancements in the .NET 3.5 Framework. To use it, add a reference to the System.ServiceModel.Web library. This object lets you create and consume feeds in both RSS and ATOM formats and use a single object model against both options. It also gives easy access to a number of child object structures, including Authors, Categories, Contributors, and Links. Additionally it strongly types the results so that we don't need to explicitly cast the values ourselves (for example with the PublicationDate). Here is the complete code to load the feed using the SyndicationFeed.Load method, fill the PostItem collection and submit them to the database.
Dim feed As SyndicationFeed = SyndicationFeed.Load(New System.Uri("http://devauthority.com/blogs/jwooley/rss.aspx"))
Dim dc As New LinqBlogDataContext()
dc.PostItems.AddAll(From p In feed.Items _ Select New PostItem With _ {.Author = If(p.Authors.Count > 0, p.Authors(0).Name, "Jim Wooley"), _ .Description = p.Summary.Text, _ .PublicationDate = p.PublishDate, _ .Title = p.Title.Text})
dc.SubmitChanges()
The code is substantially the same as the revised version using the XML Literals above. The advantage of using the Syndication services implementation is that it abstracts the feed structure (RSS/ATOM), giving direct and strongly typed access to the contents.
Now that we've set this up, maybe I can work on using the SyndicationFeed to generate the feeds in ThinqLinq and present that in a future post. Stay tuned.
Technorati tags: linq, orcas, vb, xml
Crossposted from http://devauthority.com/blogs/jwooley/default.aspx
|
-
In my VB 9 language enhancements talks, I do them withalmost all coding on the fly as I find people often can comprehend the code. I start by building a quick class that is used throughout the demos. To assist, I do use the snippet functionality in VB. For example, if you type "property" and then tab twice, the designer will generate a private field with public property accessors. The if you change the highlighted values, any associated names will be changed as well.
Private newPropertyValue As String Public Property NewProperty() As String Get Return newPropertyValue End Get Set(ByVal value As String) newPropertyValue = value End Set End Property
Personally, if you are not doing anything within your properties, there isn't much that you buy in using properties as compared to just exposing the field publically. I want a bit more functionality built into my properties. In the very least, I want to be able to include some change tracking. Once nice feature of the snippets is the fact that they are quite easy to modify and create your own.
To begin, we need to find where the supplied snippets are located on your disk. We can find this by clicking "Tools" and then the "Code Snippets Manager". We can find the location by drilling into the "Code Patterns" then "Properties, Procedures, Events" and find the "Define a Property" snippet. The location window will show you where this one is located. In the default install, it will be in your c:\Program Files\Microsoft Visual Studio 9.0\Vb\Snippets\1033\common code patterns\properties and procedures\ folder. Navigate to this folder and copy the DefineAProperty.snippet. Paste it as a new file and name it whatever you want keeping the .snippet extension.
The snippet file is just a XML document. Open it with visual studio to edit it. The top is a Header which includes the description information that will show up in the snippet manager. One change you will need to make is to alter the "Shortcut" tag so that it will use the key combination you want to use to invoke your custom snippet. In my demos, I use "propertycc", thus I change the header as follows:
< Title>Define a Property with PropertyChanging</Title> <Author>Jim Wooley</Author> <Description>Defines a Property with a backing field.</Description> <Shortcut>Propertycc</Shortcut>
The key is to change the info in the Snippet node. In my case, I like to use a convention where the private field is the same name as the public property with the exception that it is prepended by the underscore. Thus, my field may be called _Foo and the property is called Foo. Due to this, I can eliminate the PrivateVariable node and just keep the PropertyName and PropertyType nodes.
With these changes in place, we can actually define our new generated code. This can be found in the CDATA section in the Code node. I use the following in my snippet declaration:
<![CDATA[ Private _$PropertyName$ As $PropertyType$ Public Property $PropertyName$() As $PropertyType$ Get Return _$PropertyName$ End Get Set(ByVal value As $PropertyType$) If Not _$PropertyName$.Equals(value) Then _$PropertyName$ = value OnPropertyChanged("$PropertyName$") End If End Set End Property]]>
With this definition, any time we change the starting PropertyName, all associated values will be changed for each snippet. When we save our changed snippet and open a class libarry, we can start to use our new snippet by typing "propertycc" and the following code will be generated for us:
Private _NewProperty As String Public Property NewProperty() As String Get Return _NewProperty End Get Set(ByVal value As String) If Not _NewProperty.Equals(value) Then _NewProperty = value OnPropertyChanged("NewProperty") End If End Set End Property
Now our property has a detection as values change and we can then do something with it. In this case, we will call an OnPropertyChanged(propertyName as string) method, assuming we have defined one in our class. If we don't have one defined, we won't be able to compile our application. We have several options to provide the OnPropertyChanged method. The class could inherit from a base class implementtation. The additional complexity level may not necessary in many cases. Additionally, we could implement a concrete method in our class. This will mean a slight performance hit if we don't actually do anything in the method.
As an alternative, we can use the new partial methods in VS 2008. The great thing about partial methods is that if they are not implemented they are compiled away. Additionally, we can place the partial stub in a partial class for generated code and then put the implementing method in the other half of a partial class which is isolated to the custom business functionality. With this architecture in mind, we can define our partial method in the class with the rest of our generated code properties:
Partial Private Sub OnPropertyChanged(ByVal propertyName As String)
End Sub
In the other half of our partial class pair of files, we can implement the method as follows:
Private Sub OnPropertyChanged(ByVal propertyName As String)
RaiseEvent PropertyChanged(Me, New System.ComponentModel.PropertyChangedEventArgs(propertyName))
End Sub
Public Event PropertyChanged(ByVal sender As Object, ByVal e As System.ComponentModel.PropertyChangedEventArgs) Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
If we don't need the implementation, the OnPropertyChanged method call in the property setters will be compiled away, otherwise we already have the stubs in place with our snippet in order to handle the functionality as necessary.
If you are interested in trying out this snippet, I'm attaching it to this post. Simply unzip it to your snippets directory and try it out. Technorati tags: orcas, vb
Crossposted from http://devauthority.com/blogs/jwooley/default.aspx
|
-
|
|
|