My Technical Notes

Wednesday, 2 October 2013

Entity Framework: Loading Scalar Properties

When using EntityDataSource with a FormView, what is usually the case is that when you tap into the event EntityDataSource.Updating, we find that the only properties loaded are the ones that are not editable. If, for example, we added the ReadOnly attribute on a property, then its field value would be null.

This causes us problems when we validate the object. For example, if we had StartDate and EndDate properties and only one of them was loaded then we cannot validate it so that StartDate < EndDate.

Therefore what I decided was to implement a method which would fill in the missing scalar property values so that validation is easier:


public static void LoadScalarProperties(EntityObject entity, ObjectContext context)
{
    var type = entity.GetType();

    var stateEntry = context.ObjectStateManager.GetObjectStateEntry(entity);
    var entityState = stateEntry.State;
    var container = context.MetadataWorkspace.GetEntityContainer(context.DefaultContainerName, System.Data.Metadata.Edm.DataSpace.CSpace);
    var baseSet = container.BaseEntitySets[entity.EntityKey.EntitySetName];

    var newDb = Activator.CreateInstance(context.GetType()) as ObjectContext;  // create a new instance
    newDb.MetadataWorkspace.LoadFromAssembly(newDb.GetType().Assembly);        // load the MetaData too.
    var orig = newDb.GetObjectByKey(entity.EntityKey);                         // retrieve the original database object
    var scalarProperties =
        baseSet.ElementType.Members
        .Where(x => baseSet.ElementType.KeyMembers.Contains(x) == false && x.BuiltInTypeKind == BuiltInTypeKind.EdmProperty)
        .Select(x => x.Name)
        .Select(x => type.GetProperty(x));

    var modifiedProperties =
        stateEntry
        .GetModifiedProperties()
        .ToList()
        .ToDictionary(prop => prop, prop => type.GetProperty(prop).GetValue(entity, null)); // get all of the modified property names and values

    foreach (var scalarProperty in scalarProperties)
    {
        scalarProperty.SetValue(entity, scalarProperty.GetValue(orig, null), null);  
    } // set the scalar properties of the entity to be that of the original object

    stateEntry.AcceptChanges(); // these are the original values so accept the changes as the original values

    foreach (var modifiedProperty in modifiedProperties)
    {
        var modifiedPropertyInfo = scalarProperties.FirstOrDefault(x => x.Name == modifiedProperty.Key); 
        if (modifiedPropertyInfo != null)
        {
            modifiedPropertyInfo.SetValue(entity, modifiedProperty.Value, null);
        }
    } // fill the properties with the modified values.

    stateEntry.ChangeState(entityState);
}

No comments: