/// <summary> /// Saves pending changes /// </summary> /// <param name="acceptOption">Defines when changes should be accepted locally</param> /// <param name="clearErrors">If set to true, all error entries are cleared before saving chnages</param> /// <param name="failOnValidationErrors">If set to true, exception is thrown if IsValid=false, otherwise invalid entities are skipped from saving silently</param> /// <param name="clientInfo">Optional client info object, to be submitted to the server</param> public override void SaveChanges(AcceptOption acceptOption = AcceptOption.Default, bool clearErrors = true, bool failOnValidationErrors = true, ClientInfo clientInfo = null) { if (acceptOption == AcceptOption.Default) { acceptOption = System.Transactions.Transaction.Current != null ? AcceptOption.AcceptChangesOnTransactionCompleted : AcceptOption.AcceptChangesAfterSave; } if (clearErrors) { ClearErrors(); } if (failOnValidationErrors && !IsValid) { throw new ValidationException("Validation failed for one or more entities."); } // submit data TResultSet resultSet; try { resultSet = SubmitChanges(clientInfo ?? ClientInfo); } catch (FaultException ex) { var faultExceptionType = ex.GetType(); if (faultExceptionType.IsGenericType) { var detailType = faultExceptionType.GetGenericArguments()[0]; var detailProperty = typeof(FaultException <>).MakeGenericType(detailType).GetProperty("Detail"); var detail = detailProperty.GetValue(ex, null); if (detail is OptimisticConcurrencyFault) { var stateEntries = GetStateEntries(((OptimisticConcurrencyFault)detail).Entities); throw new OptimisticConcurrencyException(null, stateEntries); } if (detail is UpdateFault) { var stateEntries = GetStateEntries(((UpdateFault)detail).Entities); throw new UpdateException(null, stateEntries); } } throw; } // accept changes switch (acceptOption) { case AcceptOption.AcceptChangesAfterSave: { // refresh local data var hasValidationErrors = IsServerValidationExceptionSuppressed ? false : resultSet.Any(entity => entity.Errors.Any(e => e.IsError)); Refresh(resultSet); if (hasValidationErrors) { throw new ServerValidationException(); } AcceptChanges(true); } break; case AcceptOption.None: { // store result in private list // allowes user to apply result (accept changes) asynchronousely ResultSets.Add(resultSet); // remove result from private list in case of a transaction rollback var transaction = System.Transactions.Transaction.Current; if (transaction != null) { transaction.TransactionCompleted += (sender, e) => { if (e.Transaction.TransactionInformation.Status != TransactionStatus.Committed) { ResultSets.Remove(resultSet); } }; } } break; case AcceptOption.AcceptChangesOnTransactionCompleted: { var transaction = System.Transactions.Transaction.Current; if (transaction == null) { throw new Exception(string.Format("{0}.{1} requires an active transaction scope.", acceptOption.GetType().Name, acceptOption)); } // accept changes upon successfull completion of transaction transaction.TransactionCompleted += (sender, e) => { if (e.Transaction.TransactionInformation.Status == TransactionStatus.Committed) { // refresh local data var hasValidationErrors = IsServerValidationExceptionSuppressed ? false : resultSet.Any(entity => entity.Errors.Any(error => error.IsError)); Refresh(resultSet); if (hasValidationErrors) { throw new ServerValidationException(); } } }; } break; default: throw new Exception(string.Format("This {0} is not implemented: {1}", acceptOption.GetType().Name, acceptOption)); } //if (resultSet.HasConcurrencyConflicts) //{ // HandleConcurrencyConflicts(resultSet); //} }
/// <summary> /// Saves pending changes /// </summary> /// <param name="acceptOption">Defines when changes should be accepted locally</param> /// <param name="clearErrors">If set to true, all error entries are cleared before saving chnages</param> /// <param name="clientInfo">Optional client info object, to be submitted to the server</param> public override void SaveChangesAsync(AcceptOption acceptOption = AcceptOption.Default, bool clearErrors = true, bool failOnValidationErrors = true, ClientInfo clientInfo = null, Action <Exception> callback = null) { if (acceptOption == AcceptOption.Default) { //acceptOption = Transaction.Current != null ? AcceptOption.AcceptChangesOnTransactionCompleted : AcceptOption.AcceptChangesAfterSave; acceptOption = AcceptOption.AcceptChangesAfterSave; } if (clearErrors) { ClearErrors(); } if (failOnValidationErrors && !IsValid) { throw new ValidationException("Validation failed for one or more entities."); } SubmitChangesAsync( clientInfo ?? ClientInfo, (resultSet, exception) => { var error = exception; if (error == null) { try { // accept changes switch (acceptOption) { case AcceptOption.AcceptChangesAfterSave: // refresh local data var hasValidationErrors = IsServerValidationExceptionSuppressed ? false : resultSet.Any(entity => entity.Errors.Any(e => e.IsError)); Refresh(resultSet); if (hasValidationErrors) { throw new ServerValidationException(); } AcceptChanges(true); break; case AcceptOption.None: // store result in private list // allowes user to apply result (accept changes) asynchronousely ResultSets.Add(resultSet); break; default: throw new Exception(string.Format("This {0} is not implemented: {1}", acceptOption.GetType().Name, acceptOption)); } } catch (Exception ex) { error = ex; } } if (error is FaultException) { var ex = (FaultException)error; var faultExceptionType = ex.GetType(); if (faultExceptionType.IsGenericType) { var detailType = faultExceptionType.GetGenericArguments()[0]; var detailProperty = typeof(FaultException <>).MakeGenericType(detailType).GetProperty("Detail"); var detail = detailProperty.GetValue(ex, null); if (detail is OptimisticConcurrencyFault) { var stateEntries = GetStateEntries(((OptimisticConcurrencyFault)detail).Entities); error = new OptimisticConcurrencyException(null, stateEntries); } if (detail is UpdateFault) { var stateEntries = GetStateEntries(((UpdateFault)detail).Entities); error = new UpdateException(null, stateEntries); } } } if (callback != null) { Invoke(delegate { callback(error); }); } OnSaveChangesCompleted(error); } ); }