Wednesday, October 10, 2007

I promise not to say anything along the lines of "it's about time". I promise! I won't!

(And you can blame Cicero for that little trick I learned in Latin class many years ago...)

Wednesday, October 10, 2007 3:11:47 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 

I glanced at this headline:

Train Derails In Ohio; Explosion Heard

and thought it said

Train Derails In Ohio; Expression Blend

I know that your brain definitely fills in the blanks for you a lot which is why the SHAPE of words plays a huge role in being able to speed read. But I'd say this was a little bit out of context.

Wednesday, October 10, 2007 1:41:01 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [2]  | 

I learned a harsh lesson about Visual Studio and saving code snippets in the toolbox yesterday. Knowing that I am not great at coding Entity SQL on the fly, I stuck my code for custom operations in an Astoria Web data service into the VS2008 toolbox. I had two instances of VS open when I did this, so when I closed the instance where I had created the snippets, the instance that was still open is the one that saved the state and they were lost. Note to self: always have a text file with all snippets available when demoing. Oddly, I never used to trust the snippets and only used text files.  I guess I need to get back to a level of cautious (though not rampant) mistrust.

I did surprise myself this morning when I rewrote these from memory and only made one mistake... inserting an "@" where I didn't need to. I'd call that an A-. It wasn't a typo, though and it did take a few minute to discover the error which is something I don't like doing during a conference session, so I just skipped the pain.  (Still only a C+ in my book :-( ).

So here, in place of that particular demo, is a post on how to write operations in data services.

The default service provides for all of the capabilities of drilling into, traversing and querying your entities as laid out in this post and in more detail in the Using Astoria document that comes with the downloads and is also on the Astoria site. (Start at http://astoria.mslivelabs.com to find all resources.)

However you can customize your service by providing explicit query operations and also blocking specific query operations. Don't forget that you can also build views and sprocs into your model. Views are surfaced as EntitySets so they will be no different than other entities (though read-only) in your web service. Stored Procs are a little different. I have to explore how both of those are currently surfaced in Astoria, but would like to wait until we have bits that are not just prototype.

To create a custom operation you need to use the WebGet attribute. Here's an operation that finds all customers based on the first name of the contact. (I'm using the AW LT database, and am just lazily querying for something that is a direct property of the customer table.)

<WebGet()> _
Public Shared Function CustbyContact(ByVal aw As AdventureWorksLTEntities, ByVal firstname As String) As ObjectQuery(Of Customer)
  If String.IsNullOrEmpty(firstname) Then
  'don't expect to see this nice error if you are testing
  'your services in a browser
    Throw New ArgumentNullException("firstname", _
     "You must provide a name to search on")
  End If

  Try
    Return aw.Customer.Where("it.firstname=@fname", New ObjectParameter("fname", firstname))
  Catch ex As ArgumentException
  'this is an opportunity to catch problems with your Entity SQL query when debugging
    Throw ex
  End Try
  End Function

Note that I'm using Entity SQL in this query. While the query

http://localhost:50000/WebDataService1.svc/CustbyContact?firstname=Linda

will work if I had used LINQ to Entities for the query, I found that further operators or options such as this example where I additionally sort the response data

http://localhost:50000/WebDataService1.svc/CustbyContact?firstname=Linda&$orderby=CompanyName

do not work unless I have used Entity SQL.

While I have used a WebGet attribute to add additional queries to my service, you can use OnSendEntity and OnReceiveEntity to trap calls to the web service.

This custom operation in my data service prevents any one from querying directly for SalesOrderHeader entities using http://localhost:50000/WebDataService1.svc/SalesOrderHeader.

<OnSendEntity("SalesOrderHeader")> _
Public Shared Function RequestOrders(ByVal aw As AdventureWorksLTEntities, ByVal order As SalesOrderHeader) As Boolean
   Return False
End Function

I can still get at SalesOrderHeaders, for example by requesting customers with the expand option for SalesOrderHeader entities.

The using astoria doc details the key components for creating a method that traps requests and also one that traps POST, PUT and DELETE operations.

Wednesday, October 10, 2007 12:06:41 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 
 Monday, October 08, 2007

I'm hopping in the car to drive the 4 hours to Boston forthe sold-out REMIX which started this morning. I'll be there all day tomorrow and will be giving a talk on Astoria tomorrow afternoon.

For you locals, the Vermont.NET user group is having a special type of meeting tonight  - a PubClub! We'll (without me  - so You'll) be meeting at RiRa's in downtown Burlington. Check the website www.vtdotnet.org for details.

Thanks to Laura Blood, Tom Cooley and Rob Hale for coordinating and leading the three discussion topics.

Monday, October 08, 2007 12:21:35 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 

Two job openings at Competitive Computing:

Contact: jobs@competitive.com

SENIOR BUSINESS ANALYST / PROJECT LEAD

We are looking for a highly-motivated, results-oriented Senior Business Analyst/Project Lead who will be responsible for delivery of software solutions for our clients. To deliver best practice technical solutions and business value to our clients, this position requires demonstrated leadership skills in IT project management as well as a personality that thrives in a continually changing technology landscape.

The ideal candidate will be a self-starter with the ability to work on a variety of assignments with people at all levels of the client’s organization, leading business/system requirements analysis and application development for several large client projects. Managing clients’ expectations and internal team performance throughout the project lifecycle is a core responsibility. The successful candidate also will need to build strong partnerships across those groups while acting as a key liaison between the technical and business members of both internal and client teams.

Required qualifications:

·         Bachelor's degree in Information Technology or related field, or equivalent along with 5+ years leadership experience in technical systems analysis and development.

·         Very strong business analysis, analytical and problem solving skills, with solid data analysis skills.

·         Excellent written and oral communications and influencing skills, with skills in consensus building and facilitation.

·         Experience using Software Development Life Cycle methodology, to gather/document business, functional and data requirements, in accordance to Object Oriented Analysis & Design (OOAD) guidelines, and in developing/managing delivery of Use Cases.

·         Experience managing project schedules and resources using Microsoft Project or other project management tool.

·         Ability to work independently and proactively with a minimum amount of supervision, working within a collaborative team environment and with senior level business executives.

·         Experience with large scale system development, business re-engineering, and project leadership experience are a plus.

 

SENIOR SOFTWARE ENGINEER / TECHNICAL ARCHITECT

C2 is seeking a senior-level software engineer/technical architect to lead design, architecture, and development of custom application development solutions for large client projects. The successful candidate will have previous experience with ASP.NET (C#) development, with a strong background in web-based development, object-oriented concepts, relational database design, and SQL Server development. Strong hands-on application architecture and design skills are required for the position, with excellent written and verbal communications. Previous experience on large client projects and technical presentation skills are pluses.

Bachelor's degree preferred, Associate's degree considered, along with 5-7 years experience in an application development environment.In addition to a competitive compensation and benefits package, C2 also promotes opportunities to advance technical knowledge through industry certifications, technical conferences, and other training programs. For those seeking significant growth potential, this is a unique opportunity to join a local company and work on challenging projects in a highly collaborative team environment.

Monday, October 08, 2007 11:18:00 AM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 

 RESOURCE SYSTEMS GROUP is a multi-disciplinary, employee-owned consulting firm specializing in the planning, analysis, and management of business, infrastructure and natural resources. We serve clients who share our belief that high-quality, objective analysis is a prerequisite to resolving complex problems. More than just analysts, scientists, and engineers, we’re communicators – our study results are clear, concise, and directly applicable to a client’s particular questions and challenges. Our solutions are creative and grounded by 20 years of experience with clients as large as federal government agencies and Fortune 500 companies or as small as neighborhood interest groups and local municipalities.  Recognized as one of the “Best Places to Work in Vermont” for the 2nd consecutive year and named as one of the “Best Workplaces for Commuters”, RSG employees enjoy excellent benefits, flexible hours and opportunities for advancement. We are an equal-opportunity/affirmative action employer.  Please visit  www.rsginc.com  for more information on Resource Systems Group. 

 

Associate – Software Development

 

This position involves working in the Technology Group, supporting the firm’s software and analysis needs. The primary focus will be developing fully dynamic web-based solutions from interface to business logic to back end database design. We are looking for someone who is organized, resourceful, and analytical to join our team.

 

·         Bachelor’s degree with emphasis in computer science or a related field

·         Practical experience in the following areas:

o    Web Development in HTML and CSS

o    Database programming in SQL

o    Modern object-oriented programming (e.g. C#, VB.Net, Java)

 

Please send resume and cover letter to Recruiting Director at employment@rsginc.com and indicate Associate Software Development in the subject heading.

Monday, October 08, 2007 9:56:16 AM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 
 Sunday, October 07, 2007

I wanted to create a demo of Astoria Data Services being used in a Silverlight app for my ReMix07 Boston session on Tuesday. i have created a little screencast of it in action. Read more here...

Saturday, October 06, 2007 11:19:50 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [4]  | 
 Friday, October 05, 2007

On January 26, 2008, the VT Software Developers Alliance, CEDO, Champlain College, Seven Days, VSAC, the Lake Champlain Regional Chamber of Commerce and GBIC, along with many other partners is hosting an all day "Creative/Technology Career Jam" at the Lake & College Building on the Waterfront. We are hoping to attract at least 30 employers to set up booths and meet with high school, college students and adults to discuss the growing need for qualified technical employees.

This event will include food, music, and keynote and educational seminars presented by respected technology professionals, well known in their fields. Booth spaces will be $150 for 20 or less employees and $350 for more than 20 employees with limited space available. There will also be sponsorship opportunities. The rate sheet is not yet available, but please contact Bruce Seifer of CEDO (bseifer@ci.burlington.vt.us) or Patrick Martell with vtSDA (patrick@vtsda.org) if you are interested.

Friday, October 05, 2007 3:31:21 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 

I've been playing with using EF object in code-behind of aspx web pages.

The first issue to hit is postbacks.

Entity Objects are Binary Serializable so they can be stored in ViewState.

Like many other objects, you need to put your entities into viewstate (I'll deal with session and application caching in another post). Happily entities can be binary serialized so that happens for free when you add an entity object to ViewState. So you can persist the entities, but you cannot persist the ObjectContext, which is where all of the state information for the objects lives. Frankly, the objectcontext is very expensive and you wouldn't want that in viewstate anyway.

The outcome of this is that even if you store the objects in viewstate, and pull them back out to attach to a new ObjectContext when you need them again, you will lose any state for the objects.

One saving grace is that objects stored in viewstate get updated automagically during postback. In other words, if I create a cust object and store it in viewstate, then make a change to the cust object, then postback and pull the object back out of viewstate, it will have the newer property values. Viewstate is synced up to the object.

That means we can be sure to have a current version of the object (or list of objects) hanging around as we muck with the page.

Binary Serialization keeps the entire graph in tact

The next piece of the puzzle is that if you have an obect graph (i.e. order with order details or cust with orders with orderdetails) that all gets persisted during binary serialization. That's a big difference from xml serialization. The big reason is that XML serialization doesn't know what to do with the relationships, which are also first class objets. The relationships "contain" all of the related data. So binary serialization can handle this. When you get the object graph out of viewstate, it's got everything in it that it had when it went in.

Updating related data in an object that is in viewstate

This doesn't act the way you might expect so listen up.

Say I have a  cust object that has orders in it. i have saved the cust object into viewstate. If I edit a property of cust, say FirstName, that's easy:

cust.FirstName="Julie"

Viewstate syncs up that change just before the postback.

But what if I want to change something in one of the orders.

You need to do this directly in the cust object, not be extracting the order and changing it.

Here's what I mean.

My first instinct is to do this:

myOrdtoEdit=(From ord in custs.Orders where ord.OrderID=123 Select ord).First()
myOrdtoedit.ShippedDate="7/1/2007"

On postback, that new ShippedData is not there.

But if I make the change this way:

cust.Orders.Where(Function(o) o.OrderID = currOrderid).First.ShippedDate="7/1/2007"

The change is synched up. Good to be aware of.

What about concurrency? What if I need to remember original values?

I think the real solution is going to be is to have original values cached in another layer. But for the sake of working out the concepts, let's say you are doing everything in code behind. Here's how I'm solving this problem.

When I first get the values, I want to store them in viewstate, too. But if I do that, and I want to take advantage of the synching viewstate (I think I remember seeing that there is a page directive to turn that off, but don't hold me to it), then the original objects will get overwritten.

So what I have done was explicitly serialize the original objects and store the resulting memory stream into viewstate. When it's time to do an update, I deserialize that stream and voila, I have a set of original objects that I can use to compare my current objects to.

I have a post here on how to do an update when you have the original object and current objects available over here.

One object or more? How do I persist multiple objects?

Note that I have wavered between talking about persisting a single object and multiple objects. If you are dealing with a query that returned a collection of objects, then you don't persist the query. Instead, convert the query to a list with .ToList and you will end up with List<Customer> / List(Of Customer) and that's what you can put into viewstate. This is also what you would serialize, the list of customer objects.

When do I work with the objects again?

I have done various tests using databinding (gridviews, textboxes) or just manually getting and setting text properties.

Once you post back, the datasource is gone on a bound control anyway. I just work with the text values that are in the control. When I want to update an object, then I will get the object(s) from viewstate and modify them. Other than that, the only time I need them again is when it's time to do an update back to the data store.

Friday, October 05, 2007 12:57:01 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 

And I missed it!

I was at a board meeting for www.vtsda.org, when Rich came home on Wednesday evening and saw a big male moose with BIG antlers sauntering up our road. When Rich got to our driveway he saw another moose... a girl.

The stage was set and luckily nobody got in the male's way. It is pretty dangerous to get in the way of a male during rutting season; he will knock you down and trample you. Eventually the girl wandered off into our woods and the boy followed her. Luckily they were moving slowly,so most of our neighbors got a chance to see them.

Nobody got a picture.

Friday, October 05, 2007 11:28:17 AM (Eastern Standard Time, UTC-05:00)  #     |  Comments [1]  | 
 Thursday, October 04, 2007

Russ Fustino used to be our little secret when he worked out of the Microsoft New England office. He was well loved by Burlington, VT developers who looked forward to his Russ' Toolshed events. A full day of free training that was before MSDN events existed!

But there's more to Russ' legacy. It was Russ who inspired us to start our user group, VTdotNEt, and it was Russ who pointed his finger at me when he looked around the room to see who might actually take on the challenge.

Then a dark day came and Russ moved to Florida. There was audible moaning and snivelling to be heard all over the Champlain Valley.

Happily, we have been very lucky over the years to have Joe Stagner, Thom Robbins, Susan Wisowaty, Chris Bowen and Bob Familiar come to town.

But next month Russ is coming back!

I wonder if anyone told him that Nectar's has new owners? Well, let's keep that part quiet because they still serve gravy fries and have local bands playing every night. Hasn't changed that much.

On Nov 12th, Russ will be presenting at the VTdotNET User Group Meeting and on Nov 13th he will be doing a full day MSDN Event in Burlington.

You can find out more about the meeting at www.vtdotnet.org.

You can find out more about the MSDN event as well as register at www.msdnevents.com.

And one more thing, Nov 12th is Russ' birthday. I vote for wrapping up the meeting as soon as we can and heading down to his favorite watering hole on the planet (that would be Nectar's)  and try to see if we can convince him to return for good.

Thursday, October 04, 2007 8:35:14 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [1]  | 

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]  | 
 Wednesday, October 03, 2007

Are you sitting? Buckled in?

Yes it's true. It was just announced. Microsoft is releasing source for .NET binaries and letting us debug into it as well!

Read more here!

[A New DevLife Post]

Wednesday, October 03, 2007 11:50:23 AM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 
 Tuesday, October 02, 2007

Last week I did something that may have seemed  a little silly to some, but was a great deal of fun. I left my house at 7am, drove to the airport, flew all the way across the country (from the northeast corner of the U.S. to the southwest corner) to San Diego to give a 2 hour presentaiton to the San Diego Dot Net user group on behalf of INETA.

The silly part was that I spent about 15 hours on 4 different planes to give a 2 hour talk. But I loved doing it!

Frankly, 15 hours on planes gives me more productive work time than I have when I'm working in my home office. Probably a good 10 extra hours! No phone, no email, no distractions. I'm stuck on a plane with my laptop and hopefully a movie that I have no interest in seeing and I can just work work work! Well, the movie on the way out was good and it even made me cry (darn I hate that). On the way back it was Fantastic Four - no interest whatsoever.

The other great part about this trip was that I got to spend some great quality time with a friend as well as  an hour by the ocean on Wednesday morning before heading to the airport to return home.

And of course, I got to do one of my most favorite things - present at a user group  on a topic that I find fascinating. My talk was about LINQ. Originally I had hoped to "practice" a talk on ASP.NET Databinding with LINQ that I'll be doing in November at DevConnections.  I had added some introductory LINQ information to the beginning of the talk as Brian Loesgen, the group leader, told me in advance that they haven't had a LINQ talk yet.

At the start of the meeting, I asked for a show of hands on who had used LINQ already - only two people. So I ended up focusing on the intro part and just showing some of the databinding quickly at the end. The intro stuff includes the always fascinating language enhancements that enable LINQ and then a quick tour of all of the LINQ "flavors" - objects, datasets, xml, entities and of course, LINQ to SQL. That is a lot of fun to go over.

I made the executive decision to try to write some C# code on the fly, which never goes well - especially at bed time. If you consider the time zone difference I started the talk at 10pm. But I aim to entertain, and I certainly didn't disappoint much to a bit of embarrassment on my part. Semi-colons are not an issue. I remembered square brackets rather than parens. But this time it was the spacing that got me. VB just takes care of so many little syntax problems you might forget. C# is 100% unforgiving.

Getting past that, I had a blast and managed somehow to wrap up just about on time even though I could have gone on and on for hours more.

Throughout the course of the talk, I mentioned some great articles and resources and wanted to put them all in one place so here they are:

101 LINQ Queries for C#

101 LINQ Queries for VB

Great LINQ overview article by Don Box and Anders Hejlsberg

Lambda Epxressoins article for VB coders

VB Deep Dive articles by Bill McCarthy in Visual studio Magazine - There seems to be no list of all the columns, you have to go look in each issue in the archives. Here is Bill's blog.

The LINQ Project: consolidated starting point for MSDN materials

LINQ best practices for VB (webcast)

Books I mentioned:

Already published Introducing Microsoft LINQ from MS Press

Coming soon LINQ in Action

Blogs I mentioned:

Roger Jennings

Jim Wooley

LINQ in Action blog

Mike Taulty

Scott Guthrie's LINQ to SQL post series

Tuesday, October 02, 2007 2:14:52 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [0]  | 

Another challenge in working with tiered solutions (notably web services) that require xml serialization of entity objects is that the xml serializer does not serialize the entire graph. It only gets the main object and drops all of the relations. This is not because Entity Framework is trying to be mean, just related to how XML serialization works. In fact, the binary serialization gets the entire graph. But in the web services scenarios, we need XML.

So how to go about passing the full graph from client to server and back or server to server? I had a few ideas of how to approach this but was given a suggestion by Danny Simmons that would help me create something that would be more aligned with what their thinking on this is.

This solution uses a facade pattern. It is possible because the individual objects are serializable and a generic List is serializable. Conceptually, I take all of the objects in the graph that may start out, for example as:

-Customer
  - Orders

and create Lists of each set of objects then combine those lists into another object. After this is created, the above would look like this

-List(Of Customer)   --> would only contain one
-List(Of Orders)

Deserializing would mean pulling the customer out of the first list, then iterating through the list of orders and adding each order to that customer.

It gets a little more complicated when dealing with deeper graphs.

-Customer
  - Order
     - OrderDetails
  - Order
     -orderDetails

now we need to serialize a bit more carefully

-List(Of Customer)
-List(Of OrderwithDetails)

The OrderswithDetails would be another object that looks like this

-List(Of Order)
-List(Of OrderDetails)

The OrderwithDetails class that implements the facade will need these features:

  1. A property to contain List(Of Order)
  2. A property to contain List(Of OrderDetails)
    1. if we are going deeper then this property should be List(Of OrderDetailswithChildren)
  3. Instantiation code needs to take in the original object and construct the lists
  4. A method that knows how to deserialize the object and return the real object, not the lists of it's parts
  5. If this is going to be used in a wcf service, the class needs to be marked as a DataContract and the properties should be marked as DataMembers.

Here is what a simple class for serializing/deserializing the orders with their orderDetails (without considering any possible children of orderDetails right now). This is my first pass at this class and therefore it is not generic code that will work with any complex object. However, it does work out the basic logic (and it does work!).

<Serializable()> _
<DataContract()> _
Public Class SalesOrderwithDetailsGraph
Dim _order As List(Of SalesOrderHeader)
Dim _orderdetails As List(Of SalesOrderDetail)

Public Sub New()
'required for serialization
End Sub

Public Sub New(ByVal salesOrderGraph As SalesOrderHeader)
 _order = New List(Of SalesOrderHeader)
 _order.Add(salesOrderGraph)
 If salesOrderGraph.EntityState <> EntityState.Detached Then
    'if I'm just creating the order for the first time and am attached to the context,
    'this assumes that I didn't Include salesorderHeaders in my query - big assumption, but
    'it is just for my proof of concept. If it's detached, then I am already working with a 
    'full object and can't load anyway - that requires a context and a connection
   
salesOrderGraph.SalesOrderDetail.Load()
 End If
 _orderdetails = New List(Of SalesOrderDetail)(salesOrderGraph.SalesOrderDetail)
End Sub

<DataMember()> _
Public Property Order() As List(Of SalesOrderHeader)
 Get
  Return _order
 End Get
 Set(ByVal value As List(Of SalesOrderHeader))
  _order = value
 End Set
End Property

<DataMember()> _
Public Property OrderDetails() As List(Of SalesOrderDetail)
 Get
  Return _orderdetails
 End Get
 Set(ByVal value As List(Of SalesOrderDetail))
  _orderdetails = value
 End Set
End Property

Public Function GetOrderFromGraph() As SalesOrderHeader
 'this is to Deserialize
  Dim ord As SalesOrderHeader
 'there will only be one header in the object
 ord = _order(0)
 For Each sod As SalesOrderDetail In _orderdetails
  ord.SalesOrderDetail.Add(sod)
 Next
 Return ord
End Function

End Class

Now, if i have a simple scenario where I have a client that requests an order from a web service, mucks with that order, then sends it back to the service for an update, the client can start by retrieving a SalesOrderGraph, deserializing it back to a SalesOrder, then when it's done, create a SalesOrderGraph from the existing SalesOrder and pass it back up to the Service.

There's one big missing piece here and I blogged about that yesterday. When the order gets back to the service for an update, if you plan to update through the objectContext, then you will need to manually set the state to modified. You can do this with or without original data (depending on whether or not you care about concurrency). See my blog post about Disconnected Entities.

Lastly, I have actually gone the next step with the above serializer so that i can get a Customer with Orders and OrderDetails. It means bulding a CustomerGraph just as I built the Order Graph, but the children of the Customer will be SalesOrderwithDetailGraph objects, rather than just SalesOrderHeaders. Then you get a recursive thing happening where the customer knows how to work with it's orders, and each order know how to work with it's details.

But this post is long enough already, eh?

Tuesday, October 02, 2007 11:25:20 AM (Eastern Standard Time, UTC-05:00)  #     |  Comments [1]  | 
 Monday, October 01, 2007

One of the biggest challenges with Entity Framework for updates is that if you want to move objects across tiers, you lose the object's state. All of the state and change tracking information is maintained by the object context. While entity objects are serializable (xml and binary) and can be transported across tiers, object context is not.

 

Therefore, if you have objects being managed  by an ObjectContext in one tier, then move them to another tier and attach them to a new ObjectContext, you will have the current data, but nothing else and the object will be seen as "unchanged".

 

If you want to use the ObjectContext's SaveChanges method to do your db updates (I am using the term "update" generically to refer to updates, inserts and deletes, aka "the CUD in CRUD"), nothing will get sent to the server since it's all seen as unchanged.

 

There are a few ways we have seen to get around this., but they have all boiled down to this:

 

Solution #1 Ignore concurrency and perform an inefficient update

 

Buried in replies by team members in the ADO.NET Orcas forums there is a  conceptual solution that leverages the EntityState object (combined with EF's MetaDataWorkSpace or reflection) to mark the object as modified and then iterate through ALL of the properties in your object and change their state to "modified". Now SaveChanges will push all of the current data up to the server.

 

What I like about this solution:
It's reusable code that will work with any entity which you throw at it.

 

What I don't like about this solution:

1) What is hugely unsatisfying to me is that this is a pure "last data in wins" scenario. While this is probably all that many solutions require, it means that there is no interest in handling any possible conflicts for data that may have changed on the server in between the user's fetch and subsequent save.

2) Every property is flagged for update. Every property value is sent to SQL Server and every property value is updated in the database. Icch.

 

Solution #2 Write object update code for every single entity class

Another workaround I came up with was to extend the entity class so that it knows how to update itself. This assumes we have kept the original data hanging around somewhere. For each class, I created a method that takes in the current entity and the original entity, then I have some code that explicitly updates fields that I care about updating. Here's part of that, where I'm dealing with updates, but not deletes and inserts.

 

 

This works - the original order now has the current values updated to the new values and the state is impacted correctly for the properties and the entity itself. Then I can use SaveChanges or other functionality that I've exposed in my model such as stored procedures to get the data to the server. The key here is that I can leverage the framework for my DML.

 

But this solution stinks because I have to write the same method over and over again for every single class.

 

Solution that I want and finally figured out: Update only relevant properties and have the option to deal with concurrency on a granular level

Mike  Taulty actually pulled this off in this blog post, however I explicitly chose to hold off on reading Mike's post. :-) I didn't want to be influenced by his methods. What's interesting is that in the end, we came up with very similar solutions, even though I went down many many paths before I found success! (I'm sure it took me about 10 times as long as it took Mike though.) Some evidence of my doggedness is in Dave Burke's post about what an un-fun girl I was on Saturday night at code camp.

 

Updating for efficiency and concurrency

 

So here's what I came up with as part of a larger solution of using entity framework in an ASP.NET website, surviving post backs, keeping the original values around and being able to use SaveChanges and also for using entities across tiers (eg web services).

 

I use reflection and the Entity Framework MetaDataWorkspace to accomplish this.

 

I'll write another post about how I'm keeping the original values and surviving the postbacks on the ASP.NET site.

 

#1 important rule that has been discovered by everyone who is trying to solve this problem in Entity Framework and in LINQ to SQL, use a datetime field in your entity. This can be a field that is already in your database table or a special property exposed only by the conceptual model (or an extension to the entity's class). If it's in the database, you're original data is fine, but you need to be sure to update that field any time you make mods to the properties of the entities. If you create a special property in the EDM, then you'll also need to set the date when you first retrieve data from the data store.

 

Below, I am starting with a collection of original objects and a collection of current objects in hand. The collections are generic lists as in List<CustomerEntity>.

 

Step 1: Get a list of only those objects that have been altered since the data was first retrieved as well as objects to be inserted and objects to be deleted.

 

LINQ to the rescue!

 

In my example, origCustList is the List(Of Customer) with the original objects. custList is the List(Of Customer) with the new objects.

 

Inserts and deletes are not so complicated.

 

For inserts, I only need to query the current customers for those with no ID.

 

Dim inserts = custlist.Where(Function(c) (c.CustomerID=0))

 

Now I only need to add the new customers to my ObjectContext.

 

For Each insCust In inserts
   myObjContext.AddToCustomer(insCust)
Next

 

I cheated by using AddToCustomer for the inserts. For a more generic solution, just use the AddObject method instead.

 

 

For deletes I query the original customers and find any that don't have a match in the new customers.

 

Dim deletes = From oc In origCustList _
 
Where Not 
custlist.Any(Function(c) (c.CustomerID=oc.CustomerID))

 

Next you need to attach, then delete these objects from the ObjectContext. These steps for delete may not seem intuitive. The object needs to be known by the ObjectContext (therefore you attach it) and then deleting it from the ObjectContext marks it as a record to be deleted when we SaveChanges back to the database.

 

For Each delCust In deletes
   myObjContext.Attach(delCust)
   myObjContext.DeleteObject(delCust)
Next

 

Now come the updated cystomers. Here's where it gets fun!

 

I'm using LINQ to join on customerid and grab objects that have a newer dateModified value. This is where the date property pays off. Otherwise, I'd have to check every property of every object to see if anything changed.

 

Dim updates = From oc In origCustList Join cc In custlist _
On
oc.CustomerID Equals cc.CustomerID _
Where oc.ModifiedDate <> cc.ModifiedDate _
Select oc, cc

 

Now I have a pair of related objects for each modified customer.

 

In order to emulate a scenario where the objects have been tracked by the ObjectContext from the start, I need to attach the original objects to this new ObjectContext and then make any necessary changes to the objects. I iterate through the collection of object pairs and pass them off to a custom method, updateEntityProperty.


For Each c In updates
  myObjContext.Attach(c.oc)
  updateEntityProperties(aw, c.oc, c.cc)
Next

 

Here is where the cool stuff happens!

 

I want to preface this by saying that I tried using the ObjectStateEntry to pull this off, but it didn't give me quite enough capabilities as I needed for this solution. The class allows you to change the state of the entity to Modified and to change the state of the individual properties to modified, but I also needed to change the values of the properties, which I couldn't acheive with the state manager (which makes perfect sense - values are not state). [Update - apparently what I needed was in there; I just couldn't find it and after trying for some time, chose to move on to reflection.] So instead I am using reflection to help me modify the values programmatically. When I do this, the state changes automatically.

 

Although I didn't get to use ObjectStateEntry, I am leveraging the MetaDataWorkspace which is also part of the Entity Framework.

 

Private Sub updateEntityProperties(ByRef context As AdventureWorksModel.AWModel.Entities, ByRef origcust As Customer, ByRef newcust As Customer)


Dim mdw As Data.Metadata.Edm.MetadataWorkspace = context.MetadataWorkspace

'do an explicit declaration of Edm.EntityType, otherwise custtype becomes an edmtype
'which doesn't expose
properties that I want to grab through reflection!

Dim
custtype As Edm.EntityType = _
mdw.GetType(GetType(Customer).Name, "AWModel", Edm.DataSpace.CSpace)

For
Each p In newcust.GetType.GetProperties '<- that's Reflection at work
   Dim newprop = p.GetValue(newcust, Nothing)
   If newprop Is Nothing Then
     If p.GetValue(origcust, Nothing) IsNot Nothing Then
       p.SetValue(origcust, newprop, Nothing)
     End If
   Else
     If
Not newprop.Equals(p.GetValue(origcust, Nothing)) Then
      'if we are here, then the value in new cust is different from the original
      'SKIP THIS FOR NOW IF IT'S A PROPERTY COLLECTION -

      'TO DO: build in recursive behavior to update property collections as well
      'simplest test to see if it's a property collection or not: straight values are a System
       If newprop.GetType.Namespace = "System" Then
          p.SetValue(origcust, newprop, Nothing)
       End If
     End If
   End If
Next

End Sub

 

So, this is not quite perfect yet, but it does the job and I'm really happy that I got the concept worked out. I need to do a few more things to make this work generically and I also need to add in recursive mods to this. For example, the Customer has a property collection of addresses. In the above code, I am ignoring them, but I need to make this solution so that if it hits a property collection (this is where I'm testing for a System type - if it's not system type then it's a  property collection that is exposing child records) that it calls the same UpdateEntityProperties on that entity as well. SaveChanges will take care of all of the related records that are in the ObjectContext.

 

While this isn't a cut & paste solution like Mike's, i hope that the explanations help you to understand how and why this works. There are quite a lot of lessons that I learned during this exercise.

 

I have two more patterns that I have worked out which I want to write about. The first is dealing with the state when working in an ASP.NET application (postbacks trash the objectcontext, too) and the other is doing xml serialization on the entire object graph. Danny Simmons gave me a hint for how to approach the graph problem, and I was able to work it out from there.

Monday, October 01, 2007 12:09:34 PM (Eastern Standard Time, UTC-05:00)  #     |  Comments [2]  | 
 Thursday, September 27, 2007

We had a wee scare last night flying from Washington/Dulles into Burlington (on my way home from a quick trip to San Diego to do an INETA talk ... that blog post is still on my laptop :-)).

It was a late night flight, due to land at 11:30pm in Burlington.

Five miles before we got to Burlington, the airplane's monitoring system reported excessive heat in one of the front brakes. The worst case scenario was that on touchdown or cruising down the runway, the brake could catch fire and it was quite close to the engine so...scary potential.

The flight attendants (both young and neither had yet been initiated with an emergency landing before this) had to prepare us very quickly with the news and for an emergency landing. Tighten the seat-belts, tuck into a ball, brace yourself against the seat in front of you (I had a wall that was a little far away). There was no time really for much else. They did a fabulous job.

I live with a general disbelief in anything bad happening to me or my loved ones. My parents have both survived minor bouts with cancer; my father has been in two car accidents where the car was totalled and he walked away; my husband cracked his head open on a rock face while we were hiking and while there was a LOT of blood, missing teeth, a scalped scalp and huge gashes to be stapled up, he recovered perfectly and they even shoved his teeth back into his gums and they remain there today. My Newfoundlands are living to ripe old ages beyond the norm for this breed of dogs.

So yes, I live in a little fantasy bubble world.

And because of that, while I was certainly shaking a little, I didn't really think that anything bad would really happen. And it didn't.

We landed. Nothing seemed to happen out of the ordinary. We did an emergency evacuation of the plane because the potential for fire and explosion still remained. Since I was up front I just went down the stairs like I normally would (vs. jumping off the wing like a lot of other people had to). Nobody was hurt. The plane didn't catch fire.

There was a lot of excitement on the runway. Many firetrucks and emergency vehicles and two guys in Hazmat outfits who looked like astronauts inspecting the plane.

It struck me as odd that I was the only person who walked VERY far away from the plane (you know, just in case.). I just kept going further and further out in the field by the runway.  And an hour later, I was allowed to go back on the plane to get my stuff out of the storage bin and I headed home.

One poor girl had been so terrified (possibly she has some past experience that this too closely resembled) that she couldn't stop crying for at least an hour. She had her boyfriend there to comfort her.

So, I got another day out of it. Didn't even bother calling my husband since it was nearly midnight; I knew he was sleeping and all I had to report was "Hi honey. I'm here. The plane didn't crash or blow up and I'm still alive. Talk to you later."  But I call him with that report after every single flight, which means generally twice in a day, since most of my flights include one stop over. So it didn't seem necessary to wake him up for that.

Thursday, September 27, 2007 9:44:33 AM (Eastern Standard Time, UTC-05:00)  #     |  Comments [3]