Just spent another couple days trying to track down and work around an issue I was having with LINQ 2 SQL and the current entity structure that is used. The problem is basically, the change tracking routines to keep up with what changes you have made to an entity, is handled by the DataContext and not in your entity. If you close your DataContext, you will have no change tracking. Even worse yet, you will have problems "attaching" that instance to a new DataContext unless it is truly detached and you have a copy of the original data that the attach method can use to see what changes were made.
Boy or boy, there was a lot of fun tracking all this stuff down. Here is a post that talks about these kind of issues:
http://west-wind.com/weblog/posts/135659.aspx
In my books, the problem is that LINQ 2 SQL does not go far enough to provide features to make it fully useful when workign with disconnected data. I understand the desire to have lightweight entities and simplistic syntax, but a couple of extra features could really help out. Currently, I am working with VS 2008 Beta 2 and changes to the Attach are expected in the RTM, but I still do not think they will go as far as I need.
My goal is simply an entity that keeps track of its own original state to use when I wish to attach an entity to complete changes. So, the first step I wanted to handle is adding a new property and instance variable for the original state.
In the follow example, let us say we have a database table such as:
table Customer:
Customer_ID int NOT NULL
Phone nvarchar[26] NOT NULL
CustomerName nvarchar[128]
Okay, I use the designer and create a LINQ 2 SQL class file by dropping the table on it. Now add another class file to the project and a partial class for my Customer class. In this class I will drop an variable called "original". I also add a property called "Original":
public partial class Customer
{
internal Customer original = null;
public Customer Original
{
get { return original; }
set { original = value; }
}
}
Now, I wanted a method to copy all the fields of the class, but did not want to bother with reflection or anything like that. I just made a CopyData method which copied the data properties from the old to the new instance without copying the "original" variable. Then I build a Clone method that fully copied the instance along with the Original value (I used static methods but you can make the instance methods:
internal static void CopyData( Customer newCustomer, Customer oldCustomer)
{
if(oldCustomer == null)
{
newCustomer = null;
}
else
{
newCustomer.Customer_ID = oldCustomer.Customer_ID;
newCustomer.Phone = oldCustomer.Phone;
newCustomer.CustomerName = oldCustomer.CustomerName;
}
}
public static Customer Clone(Customer oldCustomer)
{
Customer newCustomer = new Customer();
CopyData(newCustomer, oldCustomer);
Customer newOriginal = new Customer();
Customer oldOriginal = oldCustomer.Original;
CopyData(newOriginal, oldOriginal)
newCustomer.Original = newOriginal;
return newCustomer;
}
Now, the only thing left to do is to copy off the original value when read in. There is a method called OnLoaded() that will fit that bill!
partial void OnLoaded()
{
original = Clone(this);
}
That is a done deal now. Our Entity will carry a copy of its original data. To make a copy that we want to use disconnected from the DataContext, we would use the Clone() method to make the instance such as:
Customer myNewSpiffyEntity = Customer.Clone(myFoundEntityIWantToUse);
It is now safe to do what we want, we can pass this up through tiers or just about anything we want and when the time to update has arrived we simply call:
DataClassesDataContext db = new DataClassesDataContext();
db.Attach(myNewSpiffyEntity, myNewSpiffyEntity.Original);
db.SubmitChanges();
That is all there is to it. The entity is still simple, yet it tracks its changes to the individual properites. Of course, this will not work in all situations, but in basic needs, this can go a LONG ways to making LINQ much more useful for disconnected data manipulation. I know I will use this a lot in my web applications without the need to persist DataContext'es or any funky stuff like that.
Now if only this featuer was built in or at least offered as an option inside the designer so I did not have to go through this extra effort on each class I plan to offer this functionality. I did ponder how nice it would be to simply click an option in the designer or a property on the table inside the entity editor, to tell it to generate the copydata, clone and OnLoaded to maintain the original state. I see a number of posts out there where it appears people would like to have the entities manage state when not handled by the DataContext.