Wednesday, January 23, 2008

LINQ To SQL Attach

The Attach method allows updating with a disconnected object that has been updated. The easiest version to use is:

Public Sub UpdateCustomer(updatedCustomer As Customer) 
  Dim dc As New MyDataContext() 
  dc.Customers.Attach(updatedCustomer, True) 
  dc.SubmitChanges
End Sub

The only problem is that this generates a SQL statement that updates all columns rather than just the on that has changed. In other words, we used a little extra bandwidth.

The reason this happens is because when we disconnected the object, we lost all the built in change tracking provided by LINQ (for now).

Option 2 is to get a new copy of the object and update it.

Public Sub UpdateCustomer(updatedCustomer as customer)

  Dim dc as New MyDataContext
  Dim originalCustomer as Customer = from c in
    dc.Customers Where c.PrimaryKey =
    updatedCustomer.PrimaryKey

  originalCustomer.CustomerName =
    updatedCustomer.CustomerName
  originalCustomer.CustomerAddress =
    updatedCustomer.CustomerAddress
  originalCustomer.CustomerCity=
    updatedCustomer.CustomerCity
  originalCustomer.CustomerState=
    updatedCustomer.CustomerState
  originalCustomer.CustomerPostalCode=
    updatedCustomer.CustomerPostalCode

  dc.SubmitChanges
End Sub

If you're wondering how this changes anything since it looks like we're changing all of the fields, look at the code LINQ generates for the properties. You'll notice that it checks to see if the value has changed and if so fires all of the update events. If it hasn't, it basically ignores the change.

The problem here is that we have to code all of the updates ourselves. We also have a round trip to the DB to get the object and then another one to update the object.

A slight modification will reduce the code we write but doesn't reduce the number of DB trips.

Public Sub UpdateCustomer(updatedCustomer as customer)
  Dim originalDC as New MyDataContext
  Dim originalCustomer as Customer = from c in
    dc.Customers Where c.PrimaryKey =
    updatedCustomer.PrimaryKey

  'We have to disconnect the original object
  ' from it's datacontext
  originalDC = Nothing

  Dim newDC as New MyDataContext
  newDC.Customers.Attach(updatedCustomer,
    originalCustomer)
  newDC.SubmitChanges

End Sub

This version of Attach uses the original version to compare against the new version for changes.

If we want to send only the changes we have made and avoid the extra round trip, we need to store a copy of the object before changes are made.

This example is very basic so that you can get the idea.

Public Class Customers

Private Const CustomerCacheKey As
  String = "Customer{0}"

Public Function GetCustomer(customerKey as integer)
  As Customer

  Dim CacheKey As String = String.Format
    (CustomerCacheKey, customerKey.ToString)

  Dim dc As New MyDataContext

  GetCustomer = (From c In dc.Customers Where 
    c.CustomerKey = customerKey Select
    c).SingleOrDefault
  HttpContext.Current.Cache.Add(CacheKey, GetCustomer,
    Nothing, DateTime.Now.AddSeconds(60),
    Cache.NoSlidingExpiration, CacheItemPriority.High,
    Nothing)
End Function

Public Sub UpdateCustomer(updatedCustomer as Customer)

  Dim CacheKey As String = String.Format
    (CustomerCacheKey,
    updatedCustomer.CustomerKey.ToString)

  Dim dc As New MyDataContext
  Dim originalCustomer As Customer =
    HttpContext.Current.Cache(CacheKey)

  dc.Customers.Attach(updatedCustomer, 
    originalCustomer)
  dc.SubmitChanges
End Sub

End Class

The real problem -- you've got to code all over everywhere to handle keeping a copy. If you're in a GridView or other list control, you don't know which one they picked and do you really want to cache a copy of every object. The above example is very weak because the cache is shared by everyone and can become stale very quickly. Someone may update the copy stored in the cache by calling your business layer method (of course you could use the cached copy instead of getting an updated version). I think you get the idea -- we're increasing the complexity in a different layer of our application. Can you say maintenance nightmare?!

So, when is it appropriate to use the different mechanisms? Here's some guidelines (remember, only you know your situation and can determine what's best).

1. Use the basic Attach(MyObject, True) in most cases. It presents the simplest code and is the most easily maintained. The only real cost is the extra data that goes over the wire.

2. Use option 2 where concurrency concerns out weigh the cost of the extra round trip. This allows a better level of control and flexibility and is just plain easier to code and maintain.

3. The last option is best if the payload of the generated SQL update statement above is too large. I would ask if the update payload is so large, doesn't that mean that your cache copy is really large also and now you have two copies?!

Just remember whatever you decide, do it because you have reviewed the options and made a decision as to what fits your environment best.

Oh, what about the basic Attach(MyObject) method? Well, all you're doing here is attaching the object and telling LINQ that it is in the original state. So, when you SubmitChanges nothing will go back to the DB because as far as LINQ is concerned nothing has changed.

Tuesday, January 22, 2008

Visual Studio 2008 & SPAN tags

Visual Studio doesn't seem to like SPAN tags. I've already reported a bug where it adds duplicates SPAN tags to templated fields in a GridView or DetailsView that is within a span tags. Now, it's moving my span tags around.

I had two span tags to define left and right columns with inline-block. They were contained within the same div. Somehow, VS decided that the left span belonged to the div above and moved it all on it's own. If it keeps happening, I'll see if I can narrow the circumstances down and get a bug report filed.

Thursday, January 17, 2008

VS 2008 GridView Rendering Bug

Found an annoying bug in VS today. If you have a GridView contained in a Span tag on a ContentPage and edit the fields, VS will add span tags around the fields you change. Of course, when your done making your changes and click OK, you'll get a rendering error. Simply edit the source and remove the spurious span tags.

LINQ To SQL OnValidate doesn't fire

Technorati Tags:

I spent half an hour tracking this down today and couldn't find it based a google search of the blog title. So.....If your OnValidate code isn't firing in your LINQ to SQL, your probably working with an out-of-date signature. Somewhere between Beta 2 and RTM, they snuck in a slight change by adding a parameter to indicate what kicked off the method.

Make your signature similar to:

Private Sub OnValidate(ByVal action as ChangeAction)

The ChangeAction enumeration values are Insert, Update, Delete and None. So if you need special code for the insert versus the update, you can code an IF statement appropriately.

You can find all of the breaking changes here.

Tuesday, January 15, 2008

Steelers out, rooting for anybody but the Pats

They just have gotten so arrogant. They're good but I don't they're that good. They did beat some good teams but usually when those teams were struggling (SD in particular). I might feel differently if they were a little humble about it.

On the other hand, I'd love to Favre get another ring. He's been a great player and stayed with the team/town that made him. Can he beat NE? If they played in the frozen tundra, definitely yes. Neutral field, maybe.

Sunday, January 13, 2008

LINQ To SQL, Business Layer and LinqDataSource (Round 2)

Played around with the LDS and can really only see using the LDS for really thin apps where you don't want a lot of structure (maintenance tools, prototypes, etc.) Of course, this is the same time I get my Visual Studio magazine and it covers the topic very well. Read Roger Jennings article on what's missing in VS 2008, the LINQ to SQL section talks about this issue in depth.

Meanwhile, I found this great blog from Rick Strahl on returning IQueryable that will do a lot of what I want and still get the benefit of only pulling back what I want.

Monday, January 7, 2008

LINQ To SQL, Business Layer and LinqDataSource

In playing with LTS I keep coming back to the question "Should the DataContext be exposed to the presentation so that you can use the LinqDataSource?" Does this violate the separation of layers by moving business (domain) logic into the presentation tier?

Dinesh and Scott Guthrie seem to advocate exposure but I could be interpreting them wrong. On the flip side, Rick Strahl posted some stuff that would seem to say no. Note: I do see that he is more concerned with a disconnected state than with the datacontext).

My current thoughts are that this is a six of one/half dozen issue. Seems like I get the business rules (validation, etc) I want separated from the data. Should be maintainable. So for now, I'm going to use it on some legacy applications that are well defined where this would be a definite time saver. We'll see....