Working with binary data types and detached entities in entity framework
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();
}
}
}