コード例 #1
0
    /// <summary>
    /// Validates a single entity.
    /// Skips validation (returns true) if entity is marked Deleted.
    /// </summary>
    /// <param name="entityInfo">contains the entity to validate</param>
    /// <param name="entityErrors">An EntityError is added to this list for each error found in the entity</param>
    /// <returns>true if entity is valid, false if invalid.</returns>
    public bool ValidateEntity(EntityInfo entityInfo, List<EntityError> entityErrors) {
      if (entityInfo.EntityState == EntityState.Deleted) return true;
      bool isValid = true;
      var entity = entityInfo.Entity;
      var entityType = entity.GetType();
      var entityTypeName = entityType.FullName;
      var sType = _structuralTypeMap[entityTypeName];
      var dataProperties = sType.dataProperties;
      object[] keyValues = null;
      foreach (var dp in sType.dataProperties) {
        if (dp.validators == null) continue;
        if (dp.propertyInfo == null) {
          dp.propertyInfo = entityType.GetProperty(dp.name);  // try converting from camelCase?
          if (dp.propertyInfo == null) continue;
        }
        var value = dp.propertyInfo.GetValue(entity, null);

        foreach (var validator in dp.validators) {
          var errorMessage = validator.Validate(value);
          if (errorMessage != null) {
            if (keyValues == null) keyValues = _contextProvider.GetKeyValues(entityInfo);

            entityErrors.Add(new EntityError() {
              EntityTypeName = entityTypeName,
              ErrorMessage = errorMessage,
              ErrorName = "ValidationError",
              KeyValues = keyValues,
              PropertyName = dp.name
            });
            isValid = false;
          }
        }
      }
      return isValid;
    }
コード例 #2
0
ファイル: NonEFController.cs プロジェクト: Javvar/Breeze
 public override bool BeforeSaveEntity(EntityInfo entityInfo) {
   // prohibit any additions of entities of type 'Role'
   if (entityInfo.Entity.GetType() == typeof(Role) && entityInfo.EntityState == EntityState.Added) {
     return false;
   } else {
     return true;
   }
 }
コード例 #3
0
 // Todo: delegate to helper classes when it gets more complicated
 protected override bool BeforeSaveEntity(EntityInfo entityInfo)
 {
     var entity = entityInfo.Entity;
       if (entity is TodoList) { return true; }
       if (entity is TodoItem) {
     return BeforeSaveTodoItem(entity as TodoItem, entityInfo);
       }
       throw new InvalidOperationException("Cannot save entity of unknown type");
 }
コード例 #4
0
        protected override bool BeforeSaveEntity(Breeze.WebApi.EntityInfo entityInfo)
        {
            if (entityInfo.Entity is AnnouncementModel)
            {
                var announcementModel = (AnnouncementModel)entityInfo.Entity;
                if (announcementModel != null)
                {
                    announcementModel.AnnouncementDate = DateTime.Now;
                }
            }
            if (entityInfo.Entity is StaffModel)
            {
                var staffModel = (StaffModel)entityInfo.Entity;
                if (staffModel != null)
                {
                    staffModel = BreezeDataController.ValidateStaffModel(staffModel);
                }
            }
            else if (entityInfo.Entity is StaffLeaveModel)
            {
                var leave = (StaffLeaveModel)entityInfo.Entity;
                if (leave != null)
                {
                    leave = BreezeDataController.ValidateLeaveApplication(leave);
                }
            }
            else if (entityInfo.Entity is StaffLeaveCounterModel)
            {
                StaffLeaveCounterModel leaveCounter = (StaffLeaveCounterModel)entityInfo.Entity;
                if (leaveCounter != null)
                {
                    leaveCounter = BreezeDataController.ValidateLeaveCounterModel(leaveCounter);
                }
            }

            //else if (entityInfo.Entity is StaffClockModel)
            //{
            //    StaffClockModel clockData = (StaffClockModel)entityInfo.Entity;
            //    if (clockData != null)
            //    {
            //        clockData = BreezeDataController.ValidateClockModel(clockData);
            //    }

            //}

            return(base.BeforeSaveEntity(entityInfo));
        }
コード例 #5
0
ファイル: ZzaEntitySaveGuard.cs プロジェクト: jeffweng/Breeze
        /// <summary>
        /// True if can save this entity else throw exception
        /// </summary>
        /// <exception cref="System.InvalidOperationException"></exception>
        public bool BeforeSaveEntity(EntityInfo arg)
        {
            var typeName = arg.Entity.GetType().Name;
            var saveError = string.Empty;
            var saveable = arg.Entity as ISaveable;

            if (UserStoreId == Guid.Empty)
            {
                saveError = "you are not authorized to save.";

            }
            else if (saveable == null)
            {
                saveError = "changes to '" + typeName + "' are forbidden.";

            }
            else
            {
                switch (arg.EntityState)
                {
                    case EntityState.Added:
                        saveable.StoreId = UserStoreId;
                        arg.OriginalValuesMap.Add("StoreId", UserStoreId);
                        saveError = saveable.CanAdd();
                        break;
                    case EntityState.Modified:
                    case EntityState.Deleted:
                        saveError = CanSaveExistingEntity(arg);
                        break;
                    default:
                        var stateName = Enum.GetName(typeof(EntityState), arg.EntityState);
                        saveError = " unexpected EntityState of " + stateName;
                        break;
                }
            }

            if (saveError != null)
            {
                throw new InvalidOperationException(
                    "'" + arg.Entity.GetType().Name + "' may not be saved because " +
                    saveError);
            }
            return true;
        }
コード例 #6
0
ファイル: NHRelationshipFixer.cs プロジェクト: Javvar/Breeze
        /// <summary>
        /// Add the relationship to the dependencyGraph
        /// </summary>
        /// <param name="child">Entity that depends on parent (e.g. has a many-to-one relationship to parent)</param>
        /// <param name="parent">Entity that child depends on (e.g. one parent has one-to-many children)</param>
        /// <param name="removeReverse">True to find and remove the reverse relationship.  Used for handling one-to-ones.</param>
        private void AddToGraph(EntityInfo child, EntityInfo parent, bool removeReverse)
        {
            List<EntityInfo> list;
            if (!dependencyGraph.TryGetValue(child, out list))
            {
                list = new List<EntityInfo>(5);
                dependencyGraph.Add(child, list);
            }
            if (parent != null) list.Add(parent);

            if (removeReverse)
            {
                List<EntityInfo> parentList;
                if (dependencyGraph.TryGetValue(parent, out parentList))
                {
                    parentList.Remove(child);
                }
            }
        }
コード例 #7
0
 /// <summary>
 /// Validates a single entity.
 /// Skips validation (returns true) if entity is marked Deleted.
 /// </summary>
 /// <param name="entityInfo">contains the entity to validate</param>
 /// <param name="entityErrors">An EntityError is added to this list for each error found in the entity</param>
 /// <returns>true if entity is valid, false if invalid.</returns>
 public bool ValidateEntity(EntityInfo entityInfo, List<EntityError> entityErrors) {
   if (entityInfo.EntityState == EntityState.Deleted) return true;
   // Perform validation on the entity, based on DataAnnotations.  
   var entity = entityInfo.Entity;
   var validationResults = new List<ValidationResult>();
   if (!Validator.TryValidateObject(entity, new ValidationContext(entity, null, null), validationResults, true)) {
     var keyValues = _contextProvider.GetKeyValues(entityInfo);
     var entityTypeName = entity.GetType().FullName;
     foreach (var vr in validationResults) {
       entityErrors.Add(new EntityError() {
         EntityTypeName = entityTypeName,
         ErrorMessage = vr.ErrorMessage,
         ErrorName = "ValidationError",
         KeyValues = keyValues,
         PropertyName = vr.MemberNames.FirstOrDefault()
       });
     }
     return false;
   }
   return true;
 }
コード例 #8
0
ファイル: UnitOfWork.cs プロジェクト: jahmani/Accounting
        private bool BefoueSaveEntity(EntityInfo arg)
        {
            var entity = arg.Entity;
            //var type = arg.

            return true;
        }
コード例 #9
0
 public TempKeyInfo(EntityInfo entityInfo)
 {
     _entityInfo = entityInfo;
 }
コード例 #10
0
ファイル: ZzaEntitySaveGuard.cs プロジェクト: jeffweng/Breeze
        private string CanSaveExistingEntity(EntityInfo arg)
        {
            var type = arg.Entity.GetType();
            if (type == typeof(Customer))
            {
                return ExistingCustomerSaveGuard(arg);
            }
            if (type == typeof(Order))
            {
                return ExistingOrderSaveGuard(arg);
            }
            if (type == typeof(OrderItem))
            {
                return ExistingOrderItemSaveGuard(arg);
            }
            if (type == typeof(OrderItemOption))
            {
                return ExistingOrderItemOptionSaveGuard(arg);
            }

            return "is not a saveable type";
        }
コード例 #11
0
 public bool BeforeSaveEntity(Breeze.WebApi.EntityInfo entityInfo)
 {
     return(true);
 }
コード例 #12
0
ファイル: ZzaEntitySaveGuard.cs プロジェクト: jeffweng/Breeze
 private string ExistingOrderItemOptionSaveGuard(EntityInfo arg)
 {
     var entity = (OrderItemOption)arg.Entity;
     var orig = readContext.OrderItemOptions.SingleOrDefault(e => e.Id == entity.Id);
     return ExistingEntityGuard(orig, entity.Id);
 }
コード例 #13
0
 private bool BeforeSaveToDoItem(ToDoItem todoItem, EntityInfo info)
 {
     var todoList = Context.ToDoLists.FirstOrDefault( l => l.ToDoListId == todoItem.ToDoListId);
       return (null == todoList)
          ? throwCannotFindParentToDoList()
          : UserId == todoList.UserId || throwCannotSaveEntityForThisUser();
 }
コード例 #14
0
 public virtual object[] GetKeyValues(EntityInfo entityInfo)
 {
     throw new NotImplementedException();
 }
コード例 #15
0
 private string ExistingOrderSaveGuard(EntityInfo arg)
 {
     var entity = (Order)arg.Entity;
     var orig = readContext.Orders.SingleOrDefault(e => e.OrderID == entity.OrderID);
     return ExistingEntityGuard(orig, entity.OrderID);
 }
コード例 #16
0
 private string CanSaveExistingEntity(EntityInfo arg)
 {
     var type = arg.Entity.GetType();
     if (type == typeof(Customer))
     {
         return ExistingCustomerSaveGuard(arg);
     }
     if (type == typeof(Employee))
     {
         return ExistingEmployeeSaveGuard(arg);
     }
     if (type == typeof(Order))
     {
         return ExistingOrderSaveGuard(arg);
     }
     if (type == typeof(OrderDetail))
     {
         return ExistingOrderDetailSaveGuard(arg);
     }
     if (type == typeof(InternationalOrder))
     {
         return ExistingInternationalOrderSaveGuard(arg);
     }
     if (type == typeof(User))
     {
         return ExistingUserSaveGuard(arg);
     }
     return "is is not a saveable type";
 }
コード例 #17
0
 /// <summary>
 /// The method is called for each entity to be saved before the save occurs.  If this method returns 'false'
 /// then the entity will be excluded from the save. There is no need to call the base implementation of this
 /// method when overriding it. 
 /// </summary>
 /// <param name="entityInfo"></param>
 /// <returns></returns>
 protected internal virtual bool BeforeSaveEntity(EntityInfo entityInfo) {
   if (BeforeSaveEntityDelegate != null) {
     return BeforeSaveEntityDelegate(entityInfo);
   } else {
     return true;
   }
 }
コード例 #18
0
 private string ExistingEmployeeSaveGuard(EntityInfo arg)
 {
     var entity = (Employee)arg.Entity;
     var orig = readContext.Employees.SingleOrDefault(e => e.EmployeeID == entity.EmployeeID);
     return ExistingEntityGuard(orig, entity.EmployeeID);
 }
コード例 #19
0
 private bool BeforeSaveTodoItem(TodoItem todoItem, EntityInfo info)
 {
     var todoList = Context.TodoLists.FirstOrDefault(tl => tl.TodoListId == todoItem.TodoListId);
     return (null != todoList) || throwCannotFindParentTodoList();
 }
コード例 #20
0
ファイル: NHRelationshipFixer.cs プロジェクト: Cyclic/Breeze
        /// <summary>
        /// Get a related entity based on the value of the foreign key.
        /// </summary>
        /// <param name="propName">Name of the navigation/association property of the entity, e.g. "Customer".  May be null if the property is the entity's identifier.</param>
        /// <param name="propType">Type of the property</param>
        /// <param name="entityInfo">Breeze EntityInfo</param>
        /// <param name="meta">Metadata for the entity class</param>
        /// <param name="canUseSession">Whether we can load the related entity via the Session.  
        /// If false, we only connect entities that are also in the saveMap</param>
        /// <returns></returns>
        private object GetRelatedEntity(string propName, IType propType, EntityInfo entityInfo, IClassMetadata meta, bool canUseSession)
        {
            object relatedEntity = null;
            var relKey = meta.EntityName + '.' + propName;
            var foreignKeyName = fkMap[relKey];

            object id = GetForeignKeyValue(entityInfo, meta, foreignKeyName, canUseSession);

            if (id != null)
            {
                relatedEntity = FindInSaveMap(propType.ReturnedClass, id);

                if (relatedEntity == null && canUseSession)
                {
                    var relatedEntityName = propType.Name;
                    relatedEntity = session.Load(relatedEntityName, id);
                }
            }
            return relatedEntity;
        }
コード例 #21
0
ファイル: NHRelationshipFixer.cs プロジェクト: Cyclic/Breeze
        /// <summary>
        /// Set an association value based on the value of the foreign key.  This updates the property of the entity.
        /// </summary>
        /// <param name="propName">Name of the navigation/association property of the entity, e.g. "Customer".  May be null if the property is the entity's identifier.</param>
        /// <param name="propType">Type of the property</param>
        /// <param name="entityInfo">Breeze EntityInfo</param>
        /// <param name="meta">Metadata for the entity class</param>
        /// <param name="canUseSession">Whether we can load the related entity via the Session.  
        /// If false, we only connect entities that are also in the saveMap</param>
        private void FixupRelationship(string propName, IType propType, EntityInfo entityInfo, IClassMetadata meta, bool canUseSession)
        {
            var entity = entityInfo.Entity;
            object relatedEntity = GetPropertyValue(meta, entity, propName);
            if (relatedEntity != null) return;    // entities are already connected

            relatedEntity = GetRelatedEntity(propName, propType, entityInfo, meta, canUseSession);

            if (relatedEntity != null)
                meta.SetPropertyValue(entity, propName, relatedEntity, EntityMode.Poco);
        }
コード例 #22
0
 private bool BeforeSaveToDoList(ToDoList todoList, EntityInfo info)
 {
     if (info.EntityState == EntityState.Added) {
     todoList.UserId = UserId;
     return true;
       }
       return UserId == todoList.UserId || throwCannotSaveEntityForThisUser();
 }
コード例 #23
0
 private ObjectStateEntry HandleDeletedPart1(EntityInfo entityInfo)
 {
     return(null);
 }
コード例 #24
0
 public virtual object[] GetKeyValues(EntityInfo entityInfo) {
   throw new NotImplementedException();
 }
コード例 #25
0
ファイル: NHRelationshipFixer.cs プロジェクト: Javvar/Breeze
        /// <summary>
        /// Recursively add entities to the saveOrder or deleteOrder according to their dependencies
        /// </summary>
        /// <param name="entityInfo">Entity to be added.  Its dependencies will be added depth-first.</param>
        /// <param name="depth">prevents infinite recursion in case of cyclic dependencies</param>
        private void AddToSaveOrder(EntityInfo entityInfo, int depth)
        {
            if (saveOrder.Contains(entityInfo)) return;
            if (deleteOrder.Contains(entityInfo)) return;
            if (depth > 10) return;

            var dependencies = dependencyGraph[entityInfo];
            foreach (var dep in dependencies)
            {
                AddToSaveOrder(dep, depth + 1);
            }

            if (entityInfo.EntityState == EntityState.Deleted)
                deleteOrder.Add(entityInfo);
            else
                saveOrder.Add(entityInfo);
        }
コード例 #26
0
 public TempKeyInfo(EntityInfo entityInfo) {
   _entityInfo = entityInfo;
 }
コード例 #27
0
ファイル: NHRelationshipFixer.cs プロジェクト: Javvar/Breeze
        /// <summary>
        /// Connect the related entities based on the foreign key values.
        /// Note that this may cause related entities to be loaded from the DB if they are not already in the session.
        /// </summary>
        /// <param name="entityInfo">Entity that will be saved</param>
        /// <param name="meta">Metadata about the entity type</param>
        private void FixupRelationships(EntityInfo entityInfo, IClassMetadata meta)
        {
            var propNames = meta.PropertyNames;
            var propTypes = meta.PropertyTypes;


            if (meta.IdentifierType != null)
            {
                var propType = meta.IdentifierType;
                if (propType.IsAssociationType && propType.IsEntityType)
                {
                    FixupRelationship(meta.IdentifierPropertyName, (EntityType)propType, entityInfo, meta);
                }
                else if (propType.IsComponentType)
                {
                    FixupComponentRelationships(meta.IdentifierPropertyName, (ComponentType)propType, entityInfo, meta);
                }
            }

            for (int i = 0; i < propNames.Length; i++)
            {
                var propType = propTypes[i];
                if (propType.IsAssociationType && propType.IsEntityType)
                {
                    FixupRelationship(propNames[i], (EntityType)propTypes[i], entityInfo, meta);
                }
                else if (propType.IsComponentType)
                {
                    FixupComponentRelationships(propNames[i], (ComponentType)propType, entityInfo, meta);
                }
            }
        }
コード例 #28
0
 private string ExistingOrderDetailSaveGuard(EntityInfo arg)
 {
     var entity = (OrderDetail)arg.Entity;
     var orig = readContext.OrderDetails
                           .SingleOrDefault(e => e.OrderID == entity.OrderID &&
                                                 e.ProductID == entity.ProductID);
     var key = "(" + entity.OrderID + "," + entity.ProductID + ")";
     return ExistingEntityGuard(orig, key);
 }
コード例 #29
0
ファイル: NHRelationshipFixer.cs プロジェクト: Javvar/Breeze
        /// <summary>
        /// Connect the related entities based on the foreign key values found in a component type.
        /// This updates the values of the component's properties.
        /// </summary>
        /// <param name="propName">Name of the (component) property of the entity.  May be null if the property is the entity's identifier.</param>
        /// <param name="compType">Type of the component</param>
        /// <param name="entityInfo">Breeze EntityInfo</param>
        /// <param name="meta">Metadata for the entity class</param>
        private void FixupComponentRelationships(string propName, ComponentType compType, EntityInfo entityInfo, IClassMetadata meta)
        {
            var compPropNames = compType.PropertyNames;
            var compPropTypes = compType.Subtypes;
            object component = null;
            object[] compValues = null;
            bool isChanged = false;
            for (int j = 0; j < compPropNames.Length; j++)
            {
                var compPropType = compPropTypes[j];
                if (compPropType.IsAssociationType && compPropType.IsEntityType)
                {
                    if (compValues == null)
                    {
                        // get the value of the component's subproperties
                        component = GetPropertyValue(meta, entityInfo.Entity, propName);
                        compValues = compType.GetPropertyValues(component, EntityMode.Poco);
                    }
                    if (compValues[j] == null)
                    {
                        // the related entity is null
                        var relatedEntity = GetRelatedEntity(compPropNames[j], (EntityType)compPropType, entityInfo, meta);
                        if (relatedEntity != null)
                        {
                            compValues[j] = relatedEntity;
                            isChanged = true;
                        }
                    }
                    else if (removeMode)
                    {
                        // remove the relationship
                        compValues[j] = null;
                        isChanged = true;
                    }
                }
            }
            if (isChanged)
            {
                compType.SetPropertyValues(component, compValues, EntityMode.Poco);
            }

        }
コード例 #30
0
 private string ExistingUserSaveGuard(EntityInfo arg)
 {
     var entity = (User)arg.Entity;
     var orig = readContext.Users.SingleOrDefault(e => e.Id == entity.Id);
     return ExistingEntityGuard(orig, entity.Id);
 }
コード例 #31
0
ファイル: NHRelationshipFixer.cs プロジェクト: Javvar/Breeze
        /// <summary>
        /// Set an association value based on the value of the foreign key.  This updates the property of the entity.
        /// </summary>
        /// <param name="propName">Name of the navigation/association property of the entity, e.g. "Customer".  May be null if the property is the entity's identifier.</param>
        /// <param name="propType">Type of the property</param>
        /// <param name="entityInfo">Breeze EntityInfo</param>
        /// <param name="meta">Metadata for the entity class</param>
        private void FixupRelationship(string propName, EntityType propType, EntityInfo entityInfo, IClassMetadata meta)
        {
            var entity = entityInfo.Entity;
            if (removeMode)
            {
                meta.SetPropertyValue(entity, propName, null, EntityMode.Poco);
                return;
            }
            object relatedEntity = GetPropertyValue(meta, entity, propName);
            if (relatedEntity != null) return;    // entities are already connected

            relatedEntity = GetRelatedEntity(propName, propType, entityInfo, meta);

            if (relatedEntity != null)
                meta.SetPropertyValue(entity, propName, relatedEntity, EntityMode.Poco);
        }
コード例 #32
0
ファイル: NHRelationshipFixer.cs プロジェクト: Javvar/Breeze
        /// <summary>
        /// Get a related entity based on the value of the foreign key.  Attempts to find the related entity in the
        /// saveMap; if its not found there, it is loaded via the Session (which should create a proxy, not actually load 
        /// the entity from the database).
        /// Related entities are Promoted in the saveOrder according to their state.
        /// </summary>
        /// <param name="propName">Name of the navigation/association property of the entity, e.g. "Customer".  May be null if the property is the entity's identifier.</param>
        /// <param name="propType">Type of the property</param>
        /// <param name="entityInfo">Breeze EntityInfo</param>
        /// <param name="meta">Metadata for the entity class</param>
        /// <returns></returns>
        private object GetRelatedEntity(string propName, EntityType propType, EntityInfo entityInfo, IClassMetadata meta)
        {
            object relatedEntity = null;
            string foreignKeyName = FindForeignKey(propName, meta);
            object id = GetForeignKeyValue(entityInfo, meta, foreignKeyName);

            if (id != null)
            {
                EntityInfo relatedEntityInfo = FindInSaveMap(propType.ReturnedClass, id);

                if (relatedEntityInfo == null)
                {
                    var state = entityInfo.EntityState;
                    if (state != EntityState.Deleted || !propType.IsNullable)
                    {
                        var relatedEntityName = propType.Name;
                        relatedEntity = session.Load(relatedEntityName, id, LockMode.None);
                    }
                }
                else
                {
                    bool removeReverseRelationship = propType.UseLHSPrimaryKey;
                    AddToGraph(entityInfo, relatedEntityInfo, removeReverseRelationship);
                    relatedEntity = relatedEntityInfo.Entity;
                }
            }
            return relatedEntity;
        }
コード例 #33
0
ファイル: NHRelationshipFixer.cs プロジェクト: Javvar/Breeze
        /// <summary>
        /// Get the value of the foreign key property.  This comes from the entity, but if that value is
        /// null, and the entity is deleted, we try to get it from the originalValuesMap.
        /// </summary>
        /// <param name="entityInfo">Breeze EntityInfo</param>
        /// <param name="meta">Metadata for the entity class</param>
        /// <param name="foreignKeyName">Name of the foreign key property of the entity, e.g. "CustomerID"</param>
        /// <returns></returns>
        private object GetForeignKeyValue(EntityInfo entityInfo, IClassMetadata meta, string foreignKeyName)
        {
            var entity = entityInfo.Entity;
            object id = null;
            if (foreignKeyName == meta.IdentifierPropertyName)
                id = meta.GetIdentifier(entity, EntityMode.Poco);
            else if (meta.PropertyNames.Contains(foreignKeyName))
                id = meta.GetPropertyValue(entity, foreignKeyName, EntityMode.Poco);
            else if (meta.IdentifierType.IsComponentType)
            {
                // compound key
                var compType = meta.IdentifierType as ComponentType;
                var index = Array.IndexOf<string>(compType.PropertyNames, foreignKeyName);
                if (index >= 0)
                {
                    var idComp = meta.GetIdentifier(entity, EntityMode.Poco);
                    id = compType.GetPropertyValue(idComp, index, EntityMode.Poco);
                }
            }

            if (id == null && entityInfo.EntityState == EntityState.Deleted)
            {
                entityInfo.OriginalValuesMap.TryGetValue(foreignKeyName, out id);
            }
            return id;
        }
コード例 #34
0
 protected override bool BeforeSaveEntity(EntityInfo entityInfo)
 {
     return true;
 }
コード例 #35
0
        /// <summary>
        /// True if can save this entity else throw exception
        /// </summary>
        /// <exception cref="System.InvalidOperationException"></exception>
        private bool EntitySaveGuard(EntityInfo arg)
        {
            var typeName = arg.Entity.GetType().Name;
            string saveError;

            var saveable = arg.Entity as ISaveable;
            if (saveable == null) {
                saveError = "changes to '" + typeName + "' are forbidden.";
            } else switch (arg.EntityState) {
                    case EntityState.Added:
                        saveError = saveable.canAdd();
                        break;
                    case EntityState.Modified:
                        saveError = saveable.canUpdate();
                        break;
                    case EntityState.Deleted:
                        saveError = saveable.canDelete();
                        break;
                    default:
                        var stateName = System.Enum.GetName(typeof(EntityState), arg.EntityState);
                        saveError = " unexpected EntityState of " + stateName;
                        break;
            }
            if (saveError == null) return true;
            throw new System.InvalidOperationException(
                "'" + arg.Entity.GetType().Name + "' may not be saved because " +
                saveError);
        }