Working with binary data types and detached entities in entity framework

January 28, 2009

Entity framework supports creating an entity type that holds binary data (byte[]), this is useful if you need your entity to hold things like file streams, photos etc.

But there is a problem when your entity has a property of type binary.

If you’ve worked with EF and N-tier applications, you know that you can serialize your entity and pass it to the client, deserialize it the client-side, change it and the send it to the server to be updated.

MS suggests using the ApplyPropertyChanges method of the ObjectContext to apply the changes made to the entity onto the current entity, thus making sure that no unnecessary updates will run in the DB unless something has changed in the entity.

The way the method does this is simply by comparing each property with its original value (call the Equals method on both properties) – this works for almost every primitive type (string, int, boolean, datetime) but not on binary types, because it uses Equals to compare the two arrays instead of comparing its data, so actually every time you use this method on an entity that has a binary field the entity is marked as changed.

Until the EF team fixes this bug (probably in V2), here’s a simple method you can call after calling ApplyPropertyChanges, in order to reset the state of entities which weren’t really changed.

static HashAlgorithm hash = new MD5CryptoServiceProvider();

private static void ApplyBinaryProperyChanges(ObjectContext model, EntityObject entity)
{
    bool entityModified = false;
    // Get the state of the entity and its metadata (to find the type of the changed properties)
    ObjectStateEntry state = model.ObjectStateManager.GetObjectStateEntry(entity.EntityKey);
    ReadOnlyMetadataCollection<EdmProperty> entityProperties = (state.EntitySet.ElementType as EntityType).Properties;

    if (state.State == EntityState.Modified)
    {                
        foreach (string property in state.GetModifiedProperties())
        {
            // Find the primitive type that describes this property
            PrimitiveType propertyMetadata = entityProperties[property].TypeUsage.EdmType as PrimitiveType;
            if (propertyMetadata != null && propertyMetadata.PrimitiveTypeKind == PrimitiveTypeKind.Binary)
            {
                // Get the byte array from the entity (current & original)
                byte[] newArr = state.CurrentValues[property] as byte[];
                byte[] orgArr = state.OriginalValues[property] as byte[];

                // Init null values since hash needs to work on initiated arrays
                if (newArr == null)
                    newArr = new byte[0];
                if (orgArr == null)
                    orgArr = new byte[0];

                // Compare hashes for both arrays 
                // (if both null, they will return same hash because both arrays are 0 length arrays)
                if (Convert.ToBase64String(hash.ComputeHash(newArr)) !=
                    Convert.ToBase64String(hash.ComputeHash(orgArr)))
                {
                    // Checksum isn't the same - entity was modified
                    entityModified = true;
                    break;
                }
            }
            else
            {
                // Some other primitive property was modified - no need to check further
                entityModified = true;
                break;
            }
        }

        if (!entityModified)
        {
            // entity wasn't modified (all modified properties are actually binary data which wasn't modified)
            state.AcceptChanges();
        }
    }
}

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

one comment

  1. Nir PinhasovMay 26, 2010 ב 3:50 pm

    Brilliant!

    Thanks for this post, it was very helpful.

    One note – the code is missing the “hash” variable, to fix it:

    using System.Security.Cryptography;

    var hash = HashAlgorithm.Create();

    Reply