I've been using Microsofts Hyper-V software for the last month and I gotta say I'm impressed.
At work, we've setup box with 1 Terrabyte of Hard Drive Space (Raid 10), 16GB of RAM and two Quad Core Xeons running Windows Server 2008 x64 and Hypervisor. This is being used for our Test and User Acceptance platform - allow us to create our environments very rapidly (with some help from Sys Prep) and performance is amazing considering we are running 10 VPC's, some even running Biztalk + SQL server instances inside the virtuals as well. I have not yet seen even a hint of any drop in performance and still plenty of room for expansion with this hardware.
The difference between Hyper-V and other Microsoft Virtual technolgies is that the Windows 2008 OS actually allows Hyper-V to sit a hell of a lot closer to the metal (i.e. less layers between the Virtual and the Physical hardware) while still doing a great job of distributing the load of the VPC's accross the machine. It totally rocks compared to Virtual PC Server - it isn't even close to Hyper-V on performance.
It has definately development lives much easier, being able to spin up a new Syspreped OS, drop it on, hook it up to the network and then install whatever we need. The other thing we've been able to do is to port physical machines to virtual (this comes as an easy step by step wizard) - although I have come across some issues and couldn't get some machines to move accross so easily, but this has let us move our current enviroments on seperate physical machines quickly into the virtual fold. I was further impressed in most cases that you can port a machine from physical to virtual while the physical machine your porting is still online.
Just some things to note if you are interested when using the release candidate...
1. It only runs on Windows Server x64 OS, no other OS is supported.
2. It's currently only at a Release Candidate.
3. Although Windows 2008 Server x64 contains the Integration Services components that are required for it be a guest, these are only compatible with Beta 2 of Hyper-V - When using the release candidate you will need to install a patch in the guest OS to allow these drivers to install correctly (See http://support.microsoft.com/kb/949219).
4. When migrating from a previous Virtual PC VHD to Hyper-V, it may not detect the VMBUS and other things correctly, leaving you without some of the comforts of the Integration Services compents. In order to fix this, use Start-->Run MSCONFIG-->Boot Tab-->Advanced Options and Click Detect HAL and reboot.
5. Also when migrating a previous Virtual PC VHD to Hyper-V, if you can't initally get any network access in the Hyper-V admin tool, set the Virtual PC up so it's using a Legacy network driver - this should fix it.
6. If you are stuck wondering why you can't log into the Hyper-V Adminstration tool, you'll need to get the user that original installed Hyper-V on the machine to log into it first and add you as an Administrator before you can use it yourself.
Anyway, check out this for more info
http://www.microsoft.com/windowsserver2008/en/us/virtualization-consolidation.aspx
Cheers
Matt
The good, the bad and the ugly of software development on the .NET platform.
Wednesday, June 25, 2008
Tuesday, May 20, 2008
LINQ2SQL "IS" the DAL
When I first started off with LINQ to SQL, my first intincts were to wrap it in a data acces layer just like we had on many previous projects with ADO.NET, but after using it for a short while I realised one important thing - LINQ to SQL is the Data Access Layer.
The generally accepted method of writing applications in .NET was to have 3 tiers - presentation, business and data. Basically, you had Winforms or ASP.NET in your presentation tier, .NET in the business tier and ADO.NET/SQL Server in your data tier.
Usually in the data tier, stored procedures would be created for CRUD operations and other business logic. ADO.NET code would them written to wrap these stored procedures that service the business tier.
This often ended up being quite a lot of T-SQL and .NET code, and because of this it could take quite some time to get just a single vertical slice of functionality running. It would also mean a lot of code to maintain after the product was shipped and bugs/enhancements were made.
With LINQ to SQL, a lot is simplified (although there's still plenty of room for improvement). There's no need to write tedious CRUD stored procedures which also means that there's no need to write the code to wrap the stored procedures.
In my opinion, if you go down the route of putting a layer around the LINQ to SQL (the DAL) you are making a mistake. You are adding extra code where it's not needed and reducing productivity and adding to maintenance later on.
The way I see it, there's no point - there is nothing to gain by wrapping LINQ to SQL up in layers, instead embrace it's elegance by exposing it directly to the business layer and reduce the amount of code you write significantly. Mix it with your business logic - allow it to save you the time which is much better spent in other area's rather than the plumbing.
Either use the LINQ to SQL entities as your Data Transfer objects or use them as your business entities - either way your still saving on code.
Of course LINQ to SQL is not perfect, but it's definately a step in the right direction.
In Summary, this:
Presentation -> Business -> DAL --> ADO.NET --> TSQL --> DATA
Becomes this:
Presentation -> Business -> Linq2SQL --> DATA
Cheers
Matt.
The generally accepted method of writing applications in .NET was to have 3 tiers - presentation, business and data. Basically, you had Winforms or ASP.NET in your presentation tier, .NET in the business tier and ADO.NET/SQL Server in your data tier.
Usually in the data tier, stored procedures would be created for CRUD operations and other business logic. ADO.NET code would them written to wrap these stored procedures that service the business tier.
This often ended up being quite a lot of T-SQL and .NET code, and because of this it could take quite some time to get just a single vertical slice of functionality running. It would also mean a lot of code to maintain after the product was shipped and bugs/enhancements were made.
With LINQ to SQL, a lot is simplified (although there's still plenty of room for improvement). There's no need to write tedious CRUD stored procedures which also means that there's no need to write the code to wrap the stored procedures.
In my opinion, if you go down the route of putting a layer around the LINQ to SQL (the DAL) you are making a mistake. You are adding extra code where it's not needed and reducing productivity and adding to maintenance later on.
The way I see it, there's no point - there is nothing to gain by wrapping LINQ to SQL up in layers, instead embrace it's elegance by exposing it directly to the business layer and reduce the amount of code you write significantly. Mix it with your business logic - allow it to save you the time which is much better spent in other area's rather than the plumbing.
Either use the LINQ to SQL entities as your Data Transfer objects or use them as your business entities - either way your still saving on code.
Of course LINQ to SQL is not perfect, but it's definately a step in the right direction.
In Summary, this:
Presentation -> Business -> DAL --> ADO.NET --> TSQL --> DATA
Becomes this:
Presentation -> Business -> Linq2SQL --> DATA
Cheers
Matt.
Friday, May 9, 2008
Version 1.0 Release Candidate 1 of LINQ to SQL Entity Base released!!!
Hi people,
I've finally made a release candidate for the LINQ to SQL Entity base which can be found here
A number of things have been done recently to it, namely:
- Added two static helper methods for serialization/de-serialization of entities.
- Now automatically returns KnownTypes if Entity Base class is in the same assembly as your Entity classes.
- You can now set the initial state of the root entity (e.g. New or Deleted)
- Demo is now in the form of a Client/Server architecture, with WCF used for communication.
- Added "LINQEntityState" property which returns an enum indicating the state of the entity.
Oh, and i've re-written the home page to include a list of "How To's" covering a lot of the questions i've been asked. Find it here
Enjoy!
Cheers
Matt.
I've finally made a release candidate for the LINQ to SQL Entity base which can be found here
A number of things have been done recently to it, namely:
- Added two static helper methods for serialization/de-serialization of entities.
- Now automatically returns KnownTypes if Entity Base class is in the same assembly as your Entity classes.
- You can now set the initial state of the root entity (e.g. New or Deleted)
- Demo is now in the form of a Client/Server architecture, with WCF used for communication.
- Added "LINQEntityState" property which returns an enum indicating the state of the entity.
Oh, and i've re-written the home page to include a list of "How To's" covering a lot of the questions i've been asked. Find it here
Enjoy!
Cheers
Matt.
Wednesday, April 30, 2008
Another LINQ to SQL Entity Base Improvement
Just a quick note that the latest source code includes a WCF serialization enhancement being that that you no longer have to specifically mention Known Types - this is being done automagically now.
A new release will follow in the next day or so.
Cheers
Matt.
A new release will follow in the next day or so.
Cheers
Matt.
Wednesday, April 23, 2008
LINQ to SQL Entity Base Improvement
Well,
I've been at it again... This time i've added some requested features that will come in handy for some of you.
These changes are:
1. There is no longer need to have a timestamp field
In earlier versions you had to have a timestamp column primarily because it was the easiest way for me to figure out if an object was new or not. If the column was NULL this meant that I could tell that the object was brand new, if it wasnt't null it meant that the object had been retrieved from the database.
This is no longer needed, as now when you invoke "SetAsChangeTrackingRoot()" it will go through the Entity Tree and mark all entities as IsNew = false. Then, when an entity added somewhere on the entity tree, i detect that it's a new entity (when the FK changes from NULL to the parents ID) and mark IsNew = true on the object.
2. Option to keep original entity values.
When a timestamp (version) column is available on a table, by default it is used by LINQ to SQL to peform concurrency checks when submitting updates and deletes to the database.
Because I have removed the requirement to have a timestamp (version) column on a table, I had to allow for the other method of concurrency checking which is to use the UpdateCheck property that is available on every column in the dbml model.
When the UpdateCheck property is set to "Always" on a column, LINQ to SQL compares the original value of this column against current value in the database before it updates or deletes the record. This is intended to make sure another process has not come in and changed the data since you last retrieved it.
Of course, it's up to the developer to choose the best column(s) for concurrency checks (usually a date/timestamp or update counter of some sort). By default if there is no timestamp column on the table, LINQ to SQL will set all columns = "Always" so that every column is checked in the record for change before an update is done. This is a very safe way to go, but is a little more expensive that just checking a single column.
In order for the UpdateCheck property to work however, the original value has to be available and when working in a disconnected model this is not the case. The first thought was to use the MemberwiseClone() method, but I needed to shallow copy the original version of the record somehow without including it's references (which MemberwiseClone does). To get around it, I created the following method:
The ShallowCopy method creates a new instance of the source entity, copies all the values from the original to the new instance and returns the new instance.
You can specify that you want to keep original values when calling the "SetAsChangeTrackingRoot()" method.
Some things to note:
1. LINQ to SQL will throw an exception if you specify that you want updates checks on columns and you have not indicated that you want to keep original values. This is reasonable, because you need the orignal values to do update checks in the first place...
2. I've updated the demo to include a tick box, clicking this will keep the original values as described above. I've removed the timestamp fields from the dbml model so to try out conurrency using the UpdateCheck property, simply modify the customer table and change one or more of the column's UpdateCheck properties to true.
3. An advantage of keeping the original information is that LINQ to SQL can detect just the columns that have changed and only generate an UPDATE T-SQL statement which updates those particular fields. This is good for those tables that have a lot of columns, becaues the T-SQL generated is smaller and there's less work for the database to do as it's potentially updating less columns.
For example,
// Running without the original values when updateing the freight value on an order
// will create this (note every column is updated)
UPDATE [dbo].[Orders]
SET [CustomerID] = @p1, [EmployeeID] = @p2, [OrderDate] = @p3, [RequiredDate] = @p4, [ShippedDate] = @p5, [ShipVia] = @p6, [Freight] = @p7, [ShipName] = @p8, [ShipAddress] = @p9, [ShipCity] = @p10, [ShipRegion] = @p11, [ShipPostalCode] = @p12, [ShipCountry] = @p13
WHERE [OrderID] = @p0
// Running with original values will create this (note it only updates the freight value)
UPDATE [dbo].[Orders]
SET [Freight] = @p1
WHERE [OrderID] = @p0
The Source Code
I'll be publishing the source code later today, when it's ready grab the source code here - I'll do a realease (Beta 4) once I'm satisified it's stable
Other notes
I would still recommend using a timestamp (rowversion) column if possible because it is the absolute best and easiest way to detect changes in a record.
The reason it is a good choice is that the timestamp (rowversion) column value is changed for every update made on a record.
Unlike other databases, the SQL Server version of a timestamp is not related to time, instead it's actually an 8 byte binary value that is unique within the entire database (not just unique within the table).
This is commonly mis-understood especially for people coming from other database systems which use a time based timestamp value. The issue being with time based timestamps is that you can get duplicates.
See books online for more information.
I've been at it again... This time i've added some requested features that will come in handy for some of you.
These changes are:
1. There is no longer need to have a timestamp field
In earlier versions you had to have a timestamp column primarily because it was the easiest way for me to figure out if an object was new or not. If the column was NULL this meant that I could tell that the object was brand new, if it wasnt't null it meant that the object had been retrieved from the database.
This is no longer needed, as now when you invoke "SetAsChangeTrackingRoot()" it will go through the Entity Tree and mark all entities as IsNew = false. Then, when an entity added somewhere on the entity tree, i detect that it's a new entity (when the FK changes from NULL to the parents ID) and mark IsNew = true on the object.
2. Option to keep original entity values.
When a timestamp (version) column is available on a table, by default it is used by LINQ to SQL to peform concurrency checks when submitting updates and deletes to the database.
Because I have removed the requirement to have a timestamp (version) column on a table, I had to allow for the other method of concurrency checking which is to use the UpdateCheck property that is available on every column in the dbml model.
When the UpdateCheck property is set to "Always" on a column, LINQ to SQL compares the original value of this column against current value in the database before it updates or deletes the record. This is intended to make sure another process has not come in and changed the data since you last retrieved it.
Of course, it's up to the developer to choose the best column(s) for concurrency checks (usually a date/timestamp or update counter of some sort). By default if there is no timestamp column on the table, LINQ to SQL will set all columns = "Always" so that every column is checked in the record for change before an update is done. This is a very safe way to go, but is a little more expensive that just checking a single column.
In order for the UpdateCheck property to work however, the original value has to be available and when working in a disconnected model this is not the case. The first thought was to use the MemberwiseClone() method, but I needed to shallow copy the original version of the record somehow without including it's references (which MemberwiseClone does). To get around it, I created the following method:
/// <summary> /// Make a shallow copy of column values without copying references of the source entity /// </summary> /// <param name="source">the source entity that will have it's values copied</param> /// <returns></returns> private LINQEntityBase ShallowCopy(LINQEntityBase source) {PropertyInfo[] sourcePropInfos = source.GetType().GetProperties(BindingFlags.Public BindingFlags.Instance);
PropertyInfo[] destinationPropInfos = source.GetType().GetProperties(BindingFlags.Public BindingFlags.Instance);
// create an object to copy values intoType entityType = source.GetType();
LINQEntityBase destination;
destination = Activator.CreateInstance(entityType) as LINQEntityBase;foreach (PropertyInfo sourcePropInfo in sourcePropInfos)
{if (Attribute.GetCustomAttribute(sourcePropInfo, typeof(ColumnAttribute), false) != null)
{PropertyInfo destPropInfo = destinationPropInfos.Where(pi => pi.Name == sourcePropInfo.Name).First();
destPropInfo.SetValue(destination, sourcePropInfo.GetValue(source, null), null);
}
}
return destination;}
The ShallowCopy method creates a new instance of the source entity, copies all the values from the original to the new instance and returns the new instance.
You can specify that you want to keep original values when calling the "SetAsChangeTrackingRoot()" method.
Some things to note:
1. LINQ to SQL will throw an exception if you specify that you want updates checks on columns and you have not indicated that you want to keep original values. This is reasonable, because you need the orignal values to do update checks in the first place...
2. I've updated the demo to include a tick box, clicking this will keep the original values as described above. I've removed the timestamp fields from the dbml model so to try out conurrency using the UpdateCheck property, simply modify the customer table and change one or more of the column's UpdateCheck properties to true.
3. An advantage of keeping the original information is that LINQ to SQL can detect just the columns that have changed and only generate an UPDATE T-SQL statement which updates those particular fields. This is good for those tables that have a lot of columns, becaues the T-SQL generated is smaller and there's less work for the database to do as it's potentially updating less columns.
For example,
// Running without the original values when updateing the freight value on an order
// will create this (note every column is updated)
UPDATE [dbo].[Orders]
SET [CustomerID] = @p1, [EmployeeID] = @p2, [OrderDate] = @p3, [RequiredDate] = @p4, [ShippedDate] = @p5, [ShipVia] = @p6, [Freight] = @p7, [ShipName] = @p8, [ShipAddress] = @p9, [ShipCity] = @p10, [ShipRegion] = @p11, [ShipPostalCode] = @p12, [ShipCountry] = @p13
WHERE [OrderID] = @p0
// Running with original values will create this (note it only updates the freight value)
UPDATE [dbo].[Orders]
SET [Freight] = @p1
WHERE [OrderID] = @p0
The Source Code
I'll be publishing the source code later today, when it's ready grab the source code here - I'll do a realease (Beta 4) once I'm satisified it's stable
Other notes
I would still recommend using a timestamp (rowversion) column if possible because it is the absolute best and easiest way to detect changes in a record.
The reason it is a good choice is that the timestamp (rowversion) column value is changed for every update made on a record.
Unlike other databases, the SQL Server version of a timestamp is not related to time, instead it's actually an 8 byte binary value that is unique within the entire database (not just unique within the table).
This is commonly mis-understood especially for people coming from other database systems which use a time based timestamp value. The issue being with time based timestamps is that you can get duplicates.
See books online for more information.
Monday, April 14, 2008
New Version of LINQ to SQL Entity Base Class (Beta 3.0)
Hello there!
Due to popular demand (well at least one person wanted it!) the LINQ to SQL Entity base now supports it... (in thoery!). Find V1.0 Beta 3.0 here.
In the example that comes with the LINQ to Entity Base, i've used the data contract serializer (which is what WCF uses to serialize/deserialize objects) to demonstrate this. I've also improved the example so you can scroll through the results by converting it to a simple windows form.
Don't forget the following when using it in your own projects:
1. You need to set serialization on your data context to uni-directional.
2. You need to use the KnownTypes for you're entities (this is because of the inheritence of entities from the LINQ to SQL Entity base - for more info, see Sowmy Srinivasan's blog).
Cheers
Matt
Due to popular demand (well at least one person wanted it!) the LINQ to SQL Entity base now supports it... (in thoery!). Find V1.0 Beta 3.0 here.
In the example that comes with the LINQ to Entity Base, i've used the data contract serializer (which is what WCF uses to serialize/deserialize objects) to demonstrate this. I've also improved the example so you can scroll through the results by converting it to a simple windows form.
Don't forget the following when using it in your own projects:
1. You need to set serialization on your data context to uni-directional.
2. You need to use the KnownTypes for you're entities (this is because of the inheritence of entities from the LINQ to SQL Entity base - for more info, see Sowmy Srinivasan's blog).
Cheers
Matt
Friday, March 28, 2008
Implementing Disconnected Deletion Change Tracking
In one of my previous blog posts, I described some of the difficulties with change tracking entities which have been removed (i.e. deleted).
The main problem was that once you remove an entity whilst "disconnected", it's no longer referenced by anything, and so the object disappears and hence the entity is no longer available when re-attaching to a new data context.
In the short term, I added a property called "IsDeleted" to the entity base which people could use instead of using the remove method (or setting a refrence to a child property to null), but this had it's disadvantages - mainly being that the user would have to set this themselves (i.e. it wouldn't get picked up automatically on remove) and would un-naturally need to keep the object around.
So the obvious thing to do was to keep a reference (some where?) to the entity when it's deleted (removed), so it can be re-attached and deleted later on. But where would this entity be kept? In the parent that deleted it? In the root object perhaps? In an external change tracking object?
To keep the Entity Base consistant, I decided to keep all the functionality in the Entity Base class, which ruled out having an external object tracking the changes.
Then I went through a lot of options regarding where to store the detached objects and came up with the simplest solution possible - I used the existing infrastructure provided by my Entity Base class - the ToEntityTree() method - as this was the option which seemed the least troublesome for the developer to use.
So, what I have done is implemented "SetAsChangeTrackingRoot()" method which the developer can call before making any changed to the entity objects.
The developer would use this method to mark the section of the Entity Tree (the Entity branch) that would be change tracked.
When this method is invoked on an entity, the following would happen:
1. A snapshot of the entity branch would be taken from the entity that method was invoked on.
2. Indicate to each entity in the branch that it is being change tracked.
The meant a snapshot of the entity branch would be kept locally with the root of the branch, and this also meant the entity that would used for syncronisation with the data context later on.
From there, it was just a matter of waiting for the property changed event to fire on an entity (exposed by INotifyPropertyChanged), and to look to see if the property being changed was a Foreign Key reference (meaning a child to parent relatinship) and that the value was being set to NULL (i.e. detaching the entity from it's parent). Once these conditions were met, I set the IsDeleted flag automatically marking the object for delete.
Next, I modified the ToEntityTree() method to include these "deleted" entities, as these entities would now not be picted up in the traversal of the entity tree, returning a complete list of all entities including the deleted objects.
The SyncroniseWithDataContext() method then used the information returned from the ToEntityTree() method to figure out what to attach, insert and delete.
One issue I came accross was the deletion of child entities under the entity that was marked for deletion. If you simply removed an entity that already had children, the submit changes would fail because LINQ to SQL doesn't support cascading deletes unless specified in the Database Schema and so any Foreign key constraints linking to the record being deleted would mean an exception would be thrown by SQL Server.
I also couldn't rely on the developer to delete the child entities first and then delete the top most entity as they could do it in any order and the order of the deletions is crucial - the child objects must be deleted first.
Instead, I decided by default to have my own cascade delete functionality so when an object is removed, I automatically remove any child objects starting with the child leaves of the branch first. This was achieved by call the ToEntityTree() method internally and using the reverse function so that it would be from order from child leave all the way back to the root of the change tracking.
Even though by default calling the SyncroniseWithDataContext() will perform cascading deletes, I have added an optional parameter so that it can be disabled if need be - which is handy if you didn't expect there to be children of the object you are deleting OR you are handling cascading deletes in the database anyway.
So thats how I've achieved automatic deletion tracking :).
Some more thoughts
After building the LINQ Entity base class in this way, I realised it would be reasonably easy to move all the logic into an external object (not an entity) which was similar to the standard DataContext but performed the tasks in an offline way.
Some people would feel more comfortable with this perhaps, because of the similarities with the existing data context.
I may shortly in the future investigate this further, and perhaps we'll have a alternative if people want it for change tracking whilst disconnected.
The main problem was that once you remove an entity whilst "disconnected", it's no longer referenced by anything, and so the object disappears and hence the entity is no longer available when re-attaching to a new data context.
In the short term, I added a property called "IsDeleted" to the entity base which people could use instead of using the remove method (or setting a refrence to a child property to null), but this had it's disadvantages - mainly being that the user would have to set this themselves (i.e. it wouldn't get picked up automatically on remove) and would un-naturally need to keep the object around.
So the obvious thing to do was to keep a reference (some where?) to the entity when it's deleted (removed), so it can be re-attached and deleted later on. But where would this entity be kept? In the parent that deleted it? In the root object perhaps? In an external change tracking object?
To keep the Entity Base consistant, I decided to keep all the functionality in the Entity Base class, which ruled out having an external object tracking the changes.
Then I went through a lot of options regarding where to store the detached objects and came up with the simplest solution possible - I used the existing infrastructure provided by my Entity Base class - the ToEntityTree() method - as this was the option which seemed the least troublesome for the developer to use.
So, what I have done is implemented "SetAsChangeTrackingRoot()" method which the developer can call before making any changed to the entity objects.
The developer would use this method to mark the section of the Entity Tree (the Entity branch) that would be change tracked.
When this method is invoked on an entity, the following would happen:
1. A snapshot of the entity branch would be taken from the entity that method was invoked on.
2. Indicate to each entity in the branch that it is being change tracked.
The meant a snapshot of the entity branch would be kept locally with the root of the branch, and this also meant the entity that would used for syncronisation with the data context later on.
From there, it was just a matter of waiting for the property changed event to fire on an entity (exposed by INotifyPropertyChanged), and to look to see if the property being changed was a Foreign Key reference (meaning a child to parent relatinship) and that the value was being set to NULL (i.e. detaching the entity from it's parent). Once these conditions were met, I set the IsDeleted flag automatically marking the object for delete.
Next, I modified the ToEntityTree() method to include these "deleted" entities, as these entities would now not be picted up in the traversal of the entity tree, returning a complete list of all entities including the deleted objects.
The SyncroniseWithDataContext() method then used the information returned from the ToEntityTree() method to figure out what to attach, insert and delete.
One issue I came accross was the deletion of child entities under the entity that was marked for deletion. If you simply removed an entity that already had children, the submit changes would fail because LINQ to SQL doesn't support cascading deletes unless specified in the Database Schema and so any Foreign key constraints linking to the record being deleted would mean an exception would be thrown by SQL Server.
I also couldn't rely on the developer to delete the child entities first and then delete the top most entity as they could do it in any order and the order of the deletions is crucial - the child objects must be deleted first.
Instead, I decided by default to have my own cascade delete functionality so when an object is removed, I automatically remove any child objects starting with the child leaves of the branch first. This was achieved by call the ToEntityTree() method internally and using the reverse function so that it would be from order from child leave all the way back to the root of the change tracking.
Even though by default calling the SyncroniseWithDataContext() will perform cascading deletes, I have added an optional parameter so that it can be disabled if need be - which is handy if you didn't expect there to be children of the object you are deleting OR you are handling cascading deletes in the database anyway.
So thats how I've achieved automatic deletion tracking :).
Some more thoughts
After building the LINQ Entity base class in this way, I realised it would be reasonably easy to move all the logic into an external object (not an entity) which was similar to the standard DataContext but performed the tasks in an offline way.
Some people would feel more comfortable with this perhaps, because of the similarities with the existing data context.
I may shortly in the future investigate this further, and perhaps we'll have a alternative if people want it for change tracking whilst disconnected.
Subscribe to:
Posts (Atom)