Showing posts with label Linq to SQL Entity Base. Show all posts
Showing posts with label Linq to SQL Entity Base. Show all posts

Thursday, December 3, 2009

LINQ to SQL Entity Base - Version 1.2 Beta

Hi everyone,

I've released a beta of the LINQ 2 SQL Entity Base - included in it is the following changes since the V1.1 final:

  • Bug Fix: If the entity's state is "New", LINQ to SQL Tries to attach an FK references as "New" as well - even though this was unintended. Fixed it so it does not try and insert FK referenced entities when adding a new object. Thanks Mark & Stephan for finding/fixing the issue
  • Bug Fix: If the onModifyKeepOriginal parameter on SetAsChangeTrackingRoot() is set to true and an object is detached from a collection (i.e. x.remove(y)), the object was not being attached properly to the data context when SynchroniseWithDataContext() was called, causing an error to be thrown. Thanks Yaki for finding the issue!
  • Bug Fix: When EntityState == CanceNew, don't change to EntityState == Deleted when SetAsDeleteOnSubmit is called.
  • Feature: Added ShallowCompare Method, which compares the properties of two entities and returns true if all the properties match (compare LINQ to SQL ColumnAttribute properties only).
  • Feature: If the onModifyKeepOriginal parameter on SetAsChangeTrackingRoot() is set to true, and an object is modified (i.e. EntityState == Modified) and then modified again in a way which set's it back to be the same as property values as it's original state, the entity will now have it's EntityState set to back Original (instead of remaining as EntityState == "Modified").

It can be downloaded here:

http://linq2sqleb.codeplex.com

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.

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.

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:




        /// <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 into

            Type 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

Thursday, March 27, 2008

New Version of LINQ to SQL Entity Base Class (Beta 2.0)

Hi there!

Just wanted everyone to know I've release a new version of the LINQ to SQL Entity base class.

It now supports change tracking for deletes, as well as the ability to cascade delete.

I guess from my point of view it's feature complete for ASP.NET use, assuming you store the entities in the session.

Still have to work on serialization for WCF and other uses.

LINQ to SQL Entity Base Class Version 1.0 Beta 2.0

Anyhow, check it out and let me know what you think.


Cheers

Matthew Hunter

Friday, March 14, 2008

Implementing Change Tracking when disconnected.

If you have a look at the LINQ to SQL Entity Base source code on codeplex, you'll see that the way I've implemented change tracking is by putting a few flags on the base class IsNew, IsModified, IsDeleted.

I've been able to get IsNew & IsModified to set automacally, here's how they work:

IsNew
This can be established by checking the entities RowVersion (TimeStamp) field (which BTW is a requirement to have for this to work). If the RowVersion is null, it's never been applied to the database (as the database sets this value not the developer) and hence we can tell with absolute certainty that it's a new object.
But it's in the child class how did I accomplish this?
Since the RowVersion property is in the class, there's a few options we can use to achieve this:

(1) Write extra code in the child entity class
Nope, this is out of the question! We are trying to avoid coding here!

(2) Create an interface (OK)
This is probably the best action for performance, but you need to make sure that all entities use the same column name for the RowVersion. If you use this method, you could cast the current object to this an interface and get the value that way. Of course, to get the entity to implement this, you'll need to force it to implement the interface by adding it to the DataContext dbml file (just like described here) .
However, as I'm writing something to share with one and all, and no doubt everyones gonna want to name it differently, this isn't the appropriate option (however it's still a damn good one!).

(3) Use a virtual property and override it in the entity (OK)
This is also good for performance, however it also means that you need to set every RowVersion field property property to have it's access modifier set to "override" which is a bit annoying. Personally I'm impressed that you can do this in the DBML model viewer, but it's still a little hard to maintain when you are adding tables - just another thing to remember, and again everthing has to be set to the same name for it to work.

(4) Use reflection (OK)
This is not so bad, we can simple get the properties using reflection and find which property is marked with ColumnAttribute.IsVersion = true. Seeing there can only be 1 of these per table (enforce by SQL Server) this is pretty safe. It also means i can throw a custom exception with a message if I discover that there is now RowVersion field and let the developer know.

So, after considering the options, I went with the later option being reflection mainly because it's the most flexible for this situation, but all things being equal I think the best option if you can control it is to use an interface as in (2) above.



IsModified
This ones easy, there's already an interface supplied called INotifyPropertyChanged that each entity implements, which you can then use and attach to the childs PropertyChangedEventHandler in your parent class. Whenever this event is raised, we know a column has been changed and we know to set the IsModified Flag to true.
Interestingly enough too, if the event is raised and it's the field that is the RowVersion (TimeStamp) property that's being updated, we know that the data has just been applied to the database, and hence we know we can reset the IsNew and IsModified and IsDeleted to false. So this is something we definately want to do, if after committing the data we want to keep working with our Entity Tree.
One propblem is though, I noticed that this event is also raised for child entities and entity collections not just columns. I need to avoid these non-column events because they are not the type of property changes I am looking for. So, with a little reflection, I can find out if the property has an AssociationAttribue applied to it and ignore the change events raised for these. So that's solved too.

IsDeleted
*** UPDATE --> I've come up with a solution to this problem, see this link for more details ***

I'm still looking for a good way to do this. Unfortantely, the one draw back with the way the entities are organised when disconnected, is there's no good place to handle this because if you remove the entity, you remove the entity - it's gone - not much use setting a flag if you can no longer find it!

One Idea I have is to store the object in the parent, but I haven't got around to working this one out. It's definately the trickiest of the lot.

So for now, there's just a simple flag indicating that the object needs to be deleted, which is not ideal at this stage, but it mostly works, apart from where you have a single child entity (not a collection of entities) and you want to delete it and replace it with a diffent object - currenlty you have to commit to the database in between otherwise, again you'll loose the original object.

Cheers

Matt.

Entity Base Project Added to Codeplex

For anyone that's interested, I've added an example project of some of the things I've found to codeplex and Called it the LINQ to SQL Entity Base.

Check it out at the following Address:

http://www.codeplex.com/LINQ2SQLEB

It just demonstrates some change tracking, the entity tree enumeration feature and auto-syncing to the database.

Anyway, go check it out if you want to see how some of these things I've found out can be put to use!

Cheers

Matt.