Friday, October 5, 2007
First Experiences With VS2K8 Part 6 (Disabling Optimistic Concurrency Checks)
It's been a while since I posted and that's because I've fallen a bit behind schedule on my first production ASP.NET LINQ project. In my previous posts I explained how easy it is to bind controls to data with LINQ to SQL. My next step was to be the creation of the data entry portion of the application. And that's when the problems started. I started getting errors from LINQ on updates. Stuff like 'Row not found or changed' and 'An entity can only be attached or modified without original state if it declares a version member or does not have an update check policy.'
'What the hell do those mean?' I thought. So I turned to Google. Turns out that these error messages are the result of a feature of LINQ that is failing. This feature is called optimistic concurrency checking and it works like this: in an ideal situation when an object that has been retrieved from the database by LINQ is updated, LINQ generates an update statement that takes into account both the original and new states of the object by only updating the row in the database to the new state of the object if it still matches the original state of the object. Perhaps an example is in order.
Let's say for simplicity's sake that I have a customer table with three columns: CustID (int), Name(varchar) and ModifiedDate(datetime with default of getdate()). Now let's say I ask LINQ for the Customer object with CustID = 1. Let's say I get back the following:
{ CustID = 1,
Name = 'skain',
ModifiedDate = '10/2/2007 8:35'
}
Now let's say I change the Name property to 'skainsez' and then call the SubmitChanges() method on my data context. If I have optimistic concurrency checking enabled on all of my fields then I get an update statement similar to the following:
UPDATE customers
SET Name='skainsez'
WHERE CustID=1
AND Name='skain'
AND ModifiedDate='10/2/2007 8:35'
Notice the extra constraints in the where clause. In order to get the update done it would have been sufficient to just have said 'WHERE CustID=1' but LINQ added the extra two constraints. In doing so it's effectively telling the database to only update the record if it hasn't changed since we last requested it.
This is the heart of optimistic concurrency checking. It helps to deal with concurrency issues that arise when many users are all writing to the same database at the same time. LINQ detects whether the row was successfully updated and can let you know if it wasn't. If it wasn't then you probably need to alert the user and ask them whether they want to overwrite the new data or not.
And this is all great. It's built in. The designer turns it all on by default and it just works. It just works that is unless you happen to be working in ASP.NET or any other essentially stateless environment where DataContexts are only instantiated for the length of a request. And since DataContexts must be disposed, if you're in an ASP.NET environment I can't think of any way to handle them other than to instantiate them at the beginning of the request and dispose them at the end.
So why is this a problem? Well the problem arises when in one request we get an object for LINQ and populate a data entry form and then on the next we rebuild the object from the form data and then try to update it with a new data context. For instance our data binding method might look like:
private void bindData()
{
using (CustomerDataContext db = new CustomerDataContext())
{
Customer c = Customer.GetByID(Convert.ToInt32(Request["CustID"]));
bindCustomerToForm(c);
}
}
Our update method (exectued when the user clicks the update button) might look like this:
private void updateCustomerFromForm()
{
Customer c = getCustomerObjectFromFormElements();
using (CustomerDataContext db = new CustomerDataContext())
{
c.Update(db);
}
}
I believe that many current ASP.NET apps follow this basic pattern. This assumes that the primary key field (CustID) was written out as a read-only form value and is the same on postback as it was when it was written. Now you'll notice that I'm calling an instance method on my customer object called 'Update'. Let's look at how this method might be implemented:
public void Update(CustomerDataContext db)
{
db.SubmitChanges();
}
Now if we were in a stateful application where the datacontext and our objects were not disappearing into the ether this would work fine. But we're in a stateless environment and if we call this version of the Update method no error will be thrown but the database won't be updated either. That's because we created a brand new data context for the purpose of executing the update. This data context doesn't even know about the customer object we created from the form values.
Luckily LINQ has a way for us to alert a data context of the existence of a LINQ object: the Attach method. The attach method allows us to pass an object to a data context so that the data context knows to update it when SubmitChanges is called. So it should be pretty easy to modify our Update method to call it:
public void Update(CustomerDataContext db)
{
db.Customers.Attach(this);
db.SubmitChanges();
}
This compiles and runs just fine. But again no update in the database occurs. What gives? Well I did some digging and I came to the conclusion that no updates were occurring because no changes were made to the object after it was attached so maybe I needed to attach and then set my properties. This meant some kind of ugly code but I figured if it worked I could try and pretty it up later. So I tried:
public static void Update(CustomerDataContext db, Customer newCustData)
{
Customer newCust = new Customer();
db.Customers.Attach(newCust);
newCust.CustID = newCustData.CustID;
newCust.Name = newCustData.Name;
newCust.ModifiedDate = newCustData.ModifiedDate;
db.SubmitChanges();
}
But when I tried this I got an InvalidOperationException:
'Value of member 'CustomerID' of an object of type 'Customer' changed. A member defining the identity of the object cannot be changed. Consider adding a new object with new identity and deleting the existing one instead.'
Adding a new one and deleting the existing one? That sounds a little crazy so I figured this must not be the right way. Did some more digging and noticed that the Attach method has an overload that accepts a bool called 'asModified'. Looking this up on MSDN (which is really scanty for LINQ currently) provided the following definition and nothing more:
'True if the entity is to be attached as modified.'
Hmm. 'Well,' I reasoned, 'I do want the entity to be attached as it's been modified.' So I went back to my simpler version of Update and added a 'true' like so:
public void Update(CustomerDataContext db)
{
db.Customers.Attach(this, true);
db.SubmitChanges();
}
I have to admit I thought this would work. But it didn't. I got a different InvalidOperationException:
'An entity can only be attached as modified without original state if it declares a version member or does not have an update check policy.'
Hmm. 'if it declares a version member or does not have an update check policy.' What could that mean? Well, first I tried playing around with timestamps thinking that maybe that's what 'version member' means. But I won't waste your time with that because I just couldn't get them to work at all.
Next I started looking into that whole 'update check policy' thing. I did some more digging and found that this is most likely referring to optimistic concurrency checking as I described at the beginning of this article. But how could I make it so that my entity wouldn't have an update check policy and would that even solve my problem?
Turns out that every field of a LINQ to SQL created entity has an attribute on it called UpdateCheck and by default it is set to a value of Always. For every field on every table. What I found out is that if you click on every field (including your primary key field) and in its properties window set the Update Check property value to Never then the last version of the Update method works just fine. No errors and the update happens in the database like you'd expect it to.
But there's also no concurrency checking going on so the last person to hit update is going to be the one whose changes actually show up in the database. Not a horrible situation but wouldn't it be nice if there was a solution here that allowed for optimistic concurrency checking in stateless environment?
I can't say for sure right now whether it's a problem in the beta version, whether I'm just doing it wrong or whether optimistic concurrency checking just doesn't make sense in a stateless environment. What I do know is that by turning it off I was able to get my project back on schedule. Which at this point is pretty important seeing as I went out on a bit of limb here using LINQ for a project with a real deadline.
But that's me. I love to live a life of danger!
Wednesday, September 19, 2007
iPhone Unlocking
OK. So I don't have an iPhone and I don't really have a desire for one. But my boss is crazy over it. But he hates AT&T. Which I understand is a fairly common sentiment. So he's been reading all this stuff on the web about unlocking the iPhone and Monday he comes into my office to ask me if I think I can unlock his phone.
I started looking on the web and found a bunch of confusing and often contradictory instructions. Nonetheless I decided to take a shot at it. The effort did not go particularly smoothly and all in all it took me about 5 hours until I got it working with T-Mobile. But I did get it working and my boss was psyched.
So this morning he brings in his girlfriends iPhone and wants me to unlock it too. Now, I certainly wasn't looking forward to spending another 5 hours on this nonsense so I was really hoping my previous experiences would allow me to improve that time. And they did. This time it took about 20 minutes and nearly all of that time was just waiting for various operations to complete.
Since I feel like I've got it pretty well figured out at this point I figured I'd share my method here. The most helpful source of info I found was this page. But it's poorly written and repeats itself often so here's my quick guide to getting it done. This procedure was performed on a phone that had been activated with AT&T but then had the AT&T account cancelled so when I started the phone was activated but had no service. The first phone I unlocked was brand new, never activated and I'm pretty sure the steps would be similar. These instructions also assume that you've got your phone's firmware upgraded to 1.0.2. You can use iTunes to upgrade the firmware if you need to.
First you need to download a Windows app called iBrickr. You can get it here. Once you've got it plug your iPhone into your computer and run it. The program should detect your iPhone and tell you that the first step is to 'free your iPhone'. A lot of sites refer to this step as 'jailbreaking'. Just follow the instructions given by iBrickr. NOTE: iBrickr will prompt you during this process to restart the iPhone in 'restore mode'. To do this you need to first turn off the iPhone by holding down the power button until a red slider appears on the screen to shut it down. Slide the slider and once the phone has powered down hit the power button again to restart it. When the gray apple logo appears press and hold both the power and home buttons until a yellow triangle with and exclamation point appears. It takes a while for the triangle to appear and while you're holding the buttons and waiting the phone will appear to shut down and restart twice. Just keep holding those buttons! You've got to wait until the triangle appears. It will, but it takes longer than you'd probably expect.
The next step is to patch and activate the phone. This is done with a batch file (that comes with a bunch of supporting files) that can be found here. Just download this package, extract it from the archive and then find the file named '(CLICK HERE) activate.bat'. Double click this file and follow the instructions it gives you on the screen.
Once that's done you need to go back into iBrickr and install the applications that actually do the unlocking. With your phone connected to your PC open iBrickr and click on 'Applications'. This will cause iBrickr to notify you that you first need to install PXL. PXL is apparently some sort of installer application on the iPhone that allows you to install third party software.
Once you've followed all the instructions for installing PXL iBrickr should present you with a button that says 'Browse Applications'. Click this button and then wait for a list of applications to appear. Scroll through the list and select an item called anySim by clicking on it's graphic. iBrickr will let you know that it's downloading the app from the internet and then uploading it to your phone. When this is done repeat the steps but this time choose BinKit from the list and install that.
Now we're almost there. First power down your phone and put an activated T-Mobile sim card in the phone. This should be a card that works in your T-Mobile phone currently. Then power up the phone. You may or may not get an 'unauthorized sim card' error. Just ignore it if you do.
Next make sure your phone isn't going to go to sleep during the unlocking process by going into Settings - General -AutoLock and set it to never. Now go back to your home screen on the iPhone and you should see an icon for anySim. Tap the icon and then slide the slider displayed to unlock. anySim will then display a rather long disclaimer which you need to scroll down to the bottom of. At the bottom of the disclaimer is a red button. Tap this button and wait. This process takes probably close to 10 minutes. Just let it churn away. When it's finally finished it's supposed to tell you that the unlock was successful. But in my two times doing this it hasn't. Instead at the end it says something to the effect of 'The flash was successful but the unlock failed.' Don't panic! In both of my tries I got this message. But when I powered down the phone and then restarted it up in the upper left corner was that sweet, sweet 'T-Mobile' with full signal strength bars. Once you see that the phone is unlocked and working on T-Mobile.
Congratulations!
Monday, August 27, 2007
First Experiences With VS2K8 Part 5 (Data Shaping)
This is the fifthin a series of posts I'm going to be making about my experiences with Visual Studio 2008 and .NET3.5. You might also be interested in the other posts which are linked at the right.
This is another quick hit post to talk about something I've been doing a lot of with LINQ that I think is pretty damn powerful. MS calls is data shaping and the basic idea is that you can use LINQ queries to 'reshape' your data in order to make it easier to work with in a given context.
That's probably not too helpful a description without an example so here goes.
Let's say in my order application I need to create a quick reporting page that lists all of our customers' order totals. The page needs three columns: cust id, cust name and total for all orders. For the purpose of this illustration let's assume that I've already got a method that returns a Dictionary where the keys are my customers and the values are the sum total of each customer's orders.
Without LINQ I could simply bind a GridView to this Dictionary and then specify a ColumnTemplate for cust id, cust name and orders total. With LINQ however I don't even need to specify those ColumnTemplates. Instead I can use LINQ to shape the data before I bind like so:
var query = from cust in custOrdersDict.Keys
select new
{
ID = cust.Id,
Name = string.Concat(cust.FirstName, " ", cust.LastName),
OrdersTotal = custOrdersDict[key].ToString("C")
};
I then bind my plain vanilla GridView to 'query' and I get exactly what I want. My column headers are 'ID', 'Name' and 'OrdersTotal' and the values in the columns are formatted just how I specified. Name is a concatenation of first and last name and the OrdersTotal value is formatted as currency.
This seems quite useful to me because now my GridView is not so intimately tied to the data it is being bound to. For instance if I needed to add City/State info to the report I would not have to touch my GridView at all. I would simply modify my LINQ query as follows:
var query = from cust in custOrdersDict.Keys
select new
{
ID = cust.Id,
Name = string.Concat(cust.FirstName, " ", cust.LastName),
Location = string.Concat(cust.City, ", ", cust.State),
OrdersTotal = custOrdersDict[key].ToString("C")
};
And now when I bind I get a new Location column. And I didn't have to mess with my GridView at all.
Pretty neat!
Monday, August 20, 2007
First Experiences With VS2K8 Part 4
This is the fourth in a series of posts I'm going to be making about my experiences with Visual Studio 2008 and .NET3.5. You might also be interested in the other posts which are linked at the right.
This is just a quick hit entry because I'm deep in the middle of my project now but I just figured something out that I want to share.
One of the screens in my order taking application needs to allow the user to choose one or more orders from a list and then display those orders with all of their header information. The SQL equivalent would be something like:
select * from OrderHeaders
where OrderID in (list of user-selected ints)
The trouble I ran into is that LINQ doesn't have an 'in' keyword. After a good deal of searching I finally figured out how to do it. LINQ doesn't have an 'in' keyword but it does have a 'Contains' method. Here is how you would use it:
int[] orderIDs = getSelectedOrderIDsFromForm();
OrdersDataContext db = new OrdersDataContext();
var query = from oh in db.OrderHeaders
where orderIDs.Contains(oh.OrderID)
select oh;
It's a little ass-backwards from the SQL way in that rather than in SQL we say we want OrderHeader records that contain the selected IDs. Here it sounds more like we're asking the list of IDs for matches instead.
But using the Sql Profiler I see that the generated SQL is a normal query with an IN clause just like I would have written if I was writing the SQL myself.
A little obtuse but pretty cool nonetheless.
Wednesday, August 15, 2007
First Experiences With VS2K8 Part 3
This is the third in a series of posts I'm going to be making about my experiences with Visual Studio 2008 and .NET3.5. You might also be interested in the first and second posts.
At the end of the last post I had successfully created my basic data model and used it on a web page to display a list all of my customers. The problem was that the list really displayed ALL of my customers and the page was way too long. Nobody would want to sit there and sift through all of the rows much less wait for them to download so I figured the next step would be to add sorting and paging.
Luckily for me this couldn't be easier. I simply set the AllowPaging and AllowSorting properties of the GridView to true and everything else was taken care of. I also used the AutoFormat functionality on the GridView to select a simple display style. I chose Classic.
I now had a GridView that was displaying all of my Customer data in a fairly accessible way and it didn't look half bad. Since our phone operators would be taking calls from customers placing orders the first step in the system should probably be to find a customer and now I had a way to do that.
But it still wasn't ideal. A user would have to page through customers to find the customer they wanted. Of course a search box would solve the problem. And I decided a little associated AJAX magic would make for a really nice UI.
The search box would work sort of like an autocomplete box but instead of a drop down appearing with likely candidates when the user started typing I decided to populate the whole GridView with the likely candidates. I also decided not to start the autocomplete operation until after at least 3 characters had been typed.
First of all let me admit that I couldn't figure out how to programmatically configure the LinqDataSource's where clause the way I needed to so I decided to ditch it. I deleted that control and the GridView's reference to it. Then I was ready to proceed with getting the autocomplete working.
To accomplish this I first added an HTML TextBox Input element to the page. I didn't use the ASP:TextBox control becuase it's easier to set the 'onKeyUp' event on the HTML version. I did however set the input element's 'runat=server' property so that it would be easy to access the value of the box in my code behind. For the 'onKeyUp' property I set it to call a javascript function called 'onKeyUp()'. I'll get to that function in a bit.
Next I wrapped my existing GridView with an UpdatePanel and its associated ContentTemplate. I set the onload property of the UpdatePanel to call the server side method "updatePanel1_Load". This is to facilitate the auto-complete functionality. Here's an explanation of how that works:
The search textbox has a function called 'onKeyUp()' assigned to its 'onkeyup' event. This function is defined as:
function onKeyUp()
{
var tb = $get('<%= searchTB.ClientID %>', null);
if (tb.value.length > 2)
{
__doPostBack('<%= updatePanel1.ClientID %>', '');
}
}
This javascript function executes on the client. It gets a reference to the search textbox. It checks the length of the value there and if it's longer than two characters it triggers an AJAX postback that appears to come from the UpdatePanel.
Notice that the ids of the search textbox and the UpdatePanel are being generated at runtime by the ASP engine. This is because ASP.NET prepends the IDs of containing controls onto the ID of their children controls. (So if the textbox's ID is 'searchTB' but it's in an UpdatePanel called updatePanel1 its rendered ID at runtime will be updatePanel1_searchTB, not just searchTB. Every control though has a ClientID property which returns the rendered value of the ID rather than that assigned to it programmatically.)
The postback operation triggered by the above call will then cause the UpdatePanel's OnLoad event to fire and since we assigned the server side method 'updatePanel1_Load' to the onload property this method will now fire. It's contents are:
protected void updatePanel1_Load(object sender, EventArgs e)
{
string searchTerm = searchTB.Value;
if (searchTerm.Length < 2)
{
return;
}
OrdersDataContext db = new OrdersDataContext();
var query = from cust in db.Customers
where cust.lastName.StartsWith(searchTerm)
select cust;
gv1.DataKeyNames = new string[] { "CustID" };
gv1.DataSource = query;
gv1.DataBind();
}
Put this all together and when the page loads it first presents the user with just a textbox. Once the user types three characters the client side script fires an AJAX postback and when that returns a GridView appears with all of the customers whose last names start with the letters in the text box. Each additional letter the user types further refines the results.
In my next post we'll make this page a little more user friendly and then we'll look into giving the user a way to actually select the proper customer when they see the record on the screen.
First Experiences With VS2K8 Part 2
This is the second in a series of posts I'm going to be making about my experiences with Visual Studio 2008 and .NET3.5. The first post can be found here.
In this post I will discuss how I set up my LINQ to SQL data model and how I started using it to build my order taking application. This is not intended to be a tutorial on the specifics of LINQ to SQL. For that I recommend ScottGu's excellent series of posts on the subject which can be found here.
For now I've got two projects making up this solution. One is a Web Project (called OrderEntryApp) and is the site with that users will be accessing. The other is a class libarary and is called OrderDBLINQ. It contains the LINQ to SQL mapping and generated classes.
For the OrderDBLINQ project I simply chose to add a new item and selected the Linq To Sql classes option. This created a designer view for me. I then opened the Server Explorer and dragged the tables I was interested in accessing from there onto the new designer view.
This is a legacy database that was created by one of our vendors and for various reasons it would be quite difficult to change it. This is kind of lame since the db in question is not very well designed. Naming conventions are nonexistent and there isn't a single foreign key defined anywhere. But I figured this would make it a good test for LINQ to SQL.
Since there aren't any foreign keys in my crappy db schema the LINQ to SQL designer was unable to infer the relationships of my new objects. This is only a minor stumbling block however since the designer offers tools for defining relationships between objects. The interface is quite similar to the one used by SqlServer to create foreign keys.
Each order in my application is placed by a single customer so I created a relationship between the OrderHeaders table and the Customers table. An order header will have multiple detail items associated with it so I created a relationship between OrderHeaders and OrderDetails. Each OrderDetails entry references a single Product so I created a relationship between OrderDetails and Products. Finally there is a many-to-many relationship between Products and Categories. This many-to-many is defined in the ProductCategories table so I created relationships between Products and ProductCategories and also between Categories and ProductCategories.
This all took me about 20 minutes and a good portion of that time was spent just figuring things out for the first time. All in all it was quite easy to do. But would the code generated from this diagram actually be able to service all of my applications needs?
Well first I figured I'd start with a simple test page. In my OrderEntryApp web project I added a new AJAX Web Form item called Test.aspx. My current goal here was simply to bind a read-only GridView to my Customers table.
The first step was to add a LinqDataSource object to my page and configure it. I was able to choose the DataContext object that was created by the code generator in my OrderDBLINQ project. Next I selected the Customers table from the drop down provided and left everything else at the defaults.
Next I added a GridView to my page. The only property other than 'ID' and 'runat' that I had to set was DataSourceID. I set this to the ID of the LinqDataSource I'd just created and ran the page.
Sure enough there on the page was a simple GridView with all of my customers' information listed. Pretty neat!
Now this was a fairly simple test I suppose but it was also quite simple to implement. Certainly easier than doing this the old way.
Of course now I had a gigantic web page listing every single customer in my database. My next step would be to add some tools to refine the list and add support for paging and sorting. And that will be the subject of my next post.
Tuesday, August 14, 2007
My First Post (First Experiences with Visual Studio 2008)
This is not only my first post but also the first post in a series of articles I intend to write about my experiences transitioning from Visual Studio 2005 to Visual Studio 2008 (and .NET 3.5). I'm doing this in the hopes that my stumblings will save others from having to do so in the future.
My first and only experience with VS2K8 is with the recently released Visual Studio 2008 Beta 2 Release. I installed both it and the MSDN documentation from the same page. I installed the stuff a day or so after it came out but didn't really start playing around with it until a week ago when I got handed a new project that I figured would make a good test for 2K8.
The two things I was looking forward to most were the improvements to the HTML editing side of things and LINQ (specifically LINQ to SQL.) I'd read so much about LINQ but hadn't yet bothered to download any of the earlier releases. And since I usually handle backend development in my job I was very interested to learn if and how LINQ to SQL might make my life easier.
While I can't discuss the specifics of the project I was handed it is pretty similar to an order-taking application such as might be used by a phone representative to enter orders into a fulfillment system. It needed to be web based, support multiple users and have a reasonably slick UI. And no one cared what I wrote it in. So I decided why not dive into 2K8 and see what it had to offer?
The application I'll be describing for my examples will be a pretty straightforward order entry system. It will be web-based and will utilize AJAX when called for (I hope.) There will be at a minimum a Customers table, an OrderHeaders table, an OrderDetails table, a Products table, a Categories table and finally a ProductCategories table that defines a many-to-many relationship between Categories and Products (a Category can have many Products and a Product can have many Categories.)
In my next post I'll describe how I created a LINQ to SQL representation of my data model and how I started using that representation to access my database.
Subscribe to:
Posts (Atom)