예제 #1
0
        // Determines key values for a list of changes. Side effect: populates <see cref="keys" /> which
        // includes an entry for every key involved in a change.
        private Dictionary <CompositeKey, PropagatorResult> ProcessKeys(UpdateCompiler compiler, List <PropagatorResult> changes, Set <CompositeKey> keys)
        {
            Dictionary <CompositeKey, PropagatorResult> map = new Dictionary <CompositeKey, PropagatorResult>(
                compiler.m_translator.KeyComparer);

            foreach (PropagatorResult change in changes)
            {
                // Reassign change to row since we cannot modify iteration variable
                PropagatorResult row = change;

                CompositeKey key = new CompositeKey(GetKeyConstants(row));

                // Make sure we aren't inserting another row with the same key
                PropagatorResult other;
                if (map.TryGetValue(key, out other))
                {
                    DiagnoseKeyCollision(compiler, change, key, other);
                }

                map.Add(key, row);
                keys.Add(key);
            }

            return(map);
        }
예제 #2
0
        private void DiagnoseKeyCollision(UpdateCompiler compiler, PropagatorResult change, CompositeKey key, PropagatorResult other)
        {
            KeyManager   keyManager = compiler.m_translator.KeyManager;
            CompositeKey otherKey   = new CompositeKey(GetKeyConstants(other));

            // determine if the conflict is due to shared principal key values
            bool sharedPrincipal = true;

            for (int i = 0; sharedPrincipal && i < key.KeyComponents.Length; i++)
            {
                int identifier1 = key.KeyComponents[i].Identifier;
                int identifier2 = otherKey.KeyComponents[i].Identifier;

                if (!keyManager.GetPrincipals(identifier1).Intersect(keyManager.GetPrincipals(identifier2)).Any())
                {
                    sharedPrincipal = false;
                }
            }

            if (sharedPrincipal)
            {
                // if the duplication is due to shared principals, there is a duplicate key exception
                var stateEntries = SourceInterpreter.GetAllStateEntries(change, compiler.m_translator, m_table)
                                   .Concat(SourceInterpreter.GetAllStateEntries(other, compiler.m_translator, m_table));
                throw EntityUtil.Update(Strings.Update_DuplicateKeys, null, stateEntries);
            }
            else
            {
                // if there are no shared principals, it implies that common dependents are the problem
                HashSet <IEntityStateEntry> commonDependents = null;
                foreach (PropagatorResult keyValue in key.KeyComponents.Concat(otherKey.KeyComponents))
                {
                    var dependents = new HashSet <IEntityStateEntry>();
                    foreach (int dependentId in keyManager.GetDependents(keyValue.Identifier))
                    {
                        PropagatorResult dependentResult;
                        if (keyManager.TryGetIdentifierOwner(dependentId, out dependentResult) &&
                            null != dependentResult.StateEntry)
                        {
                            dependents.Add(dependentResult.StateEntry);
                        }
                    }
                    if (null == commonDependents)
                    {
                        commonDependents = new HashSet <IEntityStateEntry>(dependents);
                    }
                    else
                    {
                        commonDependents.IntersectWith(dependents);
                    }
                }

                // to ensure the exception shape is consistent with constraint violations discovered while processing
                // commands (a more conventional scenario in which different tables are contributing principal values)
                // wrap a DataConstraintException in an UpdateException
                throw EntityUtil.Update(Strings.Update_GeneralExecutionException,
                                        EntityUtil.Constraint(Strings.Update_ReferentialConstraintIntegrityViolation), commonDependents);
            }
        }
        private void DiagnoseKeyCollision(UpdateCompiler compiler, PropagatorResult change, CompositeKey key, PropagatorResult other)
        {
            KeyManager keyManager = compiler.m_translator.KeyManager;
            CompositeKey otherKey = new CompositeKey(GetKeyConstants(other));

            // determine if the conflict is due to shared principal key values
            bool sharedPrincipal = true;
            for (int i = 0; sharedPrincipal && i < key.KeyComponents.Length; i++)
            {
                int identifier1 = key.KeyComponents[i].Identifier;
                int identifier2 = otherKey.KeyComponents[i].Identifier;

                if (!keyManager.GetPrincipals(identifier1).Intersect(keyManager.GetPrincipals(identifier2)).Any())
                {
                    sharedPrincipal = false;
                }
            }

            if (sharedPrincipal)
            {
                // if the duplication is due to shared principals, there is a duplicate key exception
                var stateEntries = SourceInterpreter.GetAllStateEntries(change, compiler.m_translator, m_table)
                    .Concat(SourceInterpreter.GetAllStateEntries(other, compiler.m_translator, m_table));
                throw EntityUtil.Update(Strings.Update_DuplicateKeys, null, stateEntries);
            }
            else
            {
                // if there are no shared principals, it implies that common dependents are the problem
                HashSet<IEntityStateEntry> commonDependents = null;
                foreach (PropagatorResult keyValue in key.KeyComponents.Concat(otherKey.KeyComponents))
                {
                    var dependents = new HashSet<IEntityStateEntry>();
                    foreach (int dependentId in keyManager.GetDependents(keyValue.Identifier))
                    {
                        PropagatorResult dependentResult;
                        if (keyManager.TryGetIdentifierOwner(dependentId, out dependentResult) &&
                            null != dependentResult.StateEntry)
                        {
                            dependents.Add(dependentResult.StateEntry);
                        }
                    }
                    if (null == commonDependents)
                    {
                        commonDependents = new HashSet<IEntityStateEntry>(dependents);
                    }
                    else
                    {
                        commonDependents.IntersectWith(dependents);
                    }
                }

                // to ensure the exception shape is consistent with constraint violations discovered while processing
                // commands (a more conventional scenario in which different tables are contributing principal values)
                // wrap a DataConstraintException in an UpdateException
                throw EntityUtil.Update(Strings.Update_GeneralExecutionException,
                    EntityUtil.Constraint(Strings.Update_ReferentialConstraintIntegrityViolation), commonDependents);
            }
        }
        // Determines key values for a list of changes. Side effect: populates <see cref="keys" /> which
        // includes an entry for every key involved in a change.
        private Dictionary<CompositeKey, PropagatorResult> ProcessKeys(UpdateCompiler compiler, List<PropagatorResult> changes, Set<CompositeKey> keys)
        {
            Dictionary<CompositeKey, PropagatorResult> map = new Dictionary<CompositeKey, PropagatorResult>(
                compiler.m_translator.KeyComparer);

            foreach (PropagatorResult change in changes)
            {
                // Reassign change to row since we cannot modify iteration variable
                PropagatorResult row = change;

                CompositeKey key = new CompositeKey(GetKeyConstants(row));

                // Make sure we aren't inserting another row with the same key
                PropagatorResult other;
                if (map.TryGetValue(key, out other))
                {
                    DiagnoseKeyCollision(compiler, change, key, other);
                }

                map.Add(key, row);
                keys.Add(key);
            }

            return map;
        }
        // Processes all insert and delete requests in the table's <see cref="ChangeNode" />. Inserts
        // and deletes with the same key are merged into updates.
        internal List<UpdateCommand> CompileCommands(ChangeNode changeNode, UpdateCompiler compiler)
        {
            Set<CompositeKey> keys = new Set<CompositeKey>(compiler.m_translator.KeyComparer);

            // Retrieve all delete results (original values) and insert results (current values) while
            // populating a set of all row keys. The set contains a single key per row.
            Dictionary<CompositeKey, PropagatorResult> deleteResults = ProcessKeys(compiler, changeNode.Deleted, keys);
            Dictionary<CompositeKey, PropagatorResult> insertResults = ProcessKeys(compiler, changeNode.Inserted, keys);

            List<UpdateCommand> commands = new List<UpdateCommand>(deleteResults.Count + insertResults.Count);

            // Examine each row key to see if the row is being deleted, inserted or updated
            foreach (CompositeKey key in keys)
            {
                PropagatorResult deleteResult;
                PropagatorResult insertResult;

                bool hasDelete = deleteResults.TryGetValue(key, out deleteResult);
                bool hasInsert = insertResults.TryGetValue(key, out insertResult);

                Debug.Assert(hasDelete || hasInsert, "(update/TableChangeProcessor) m_keys must not contain a value " +
                    "if there is no corresponding insert or delete");

                try
                {
                    if (!hasDelete)
                    {
                        // this is an insert
                        commands.Add(compiler.BuildInsertCommand(insertResult, this));
                    }
                    else if (!hasInsert)
                    {
                        // this is a delete
                        commands.Add(compiler.BuildDeleteCommand(deleteResult, this));
                    }
                    else
                    {
                        // this is an update because it has both a delete result and an insert result
                        UpdateCommand updateCommand = compiler.BuildUpdateCommand(deleteResult, insertResult, this);
                        if (null != updateCommand)
                        {
                            // if null is returned, it means it is a no-op update
                            commands.Add(updateCommand);
                        }
                    }
                }
                catch (Exception e)
                {
                    if (UpdateTranslator.RequiresContext(e))
                    {
                        // collect state entries in scope for the current compilation
                        List<IEntityStateEntry> stateEntries = new List<IEntityStateEntry>();
                        if (null != deleteResult)
                        {
                            stateEntries.AddRange(SourceInterpreter.GetAllStateEntries(
                                deleteResult, compiler.m_translator, m_table));
                        }
                        if (null != insertResult)
                        {
                            stateEntries.AddRange(SourceInterpreter.GetAllStateEntries(
                                insertResult, compiler.m_translator, m_table));
                        }

                        throw EntityUtil.Update(System.Data.Entity.Strings.Update_GeneralExecutionException,
                            e, stateEntries);
                    }
                    throw;
                }
            }

            return commands;
        }
        /// <summary>
        /// Produce dynamic store commands for this translator's changes.
        /// </summary>
        /// <returns>Database commands in a safe order</returns>
        private IEnumerable<UpdateCommand> ProduceDynamicCommands()
        {
            // Initialize DBCommand update compiler
            UpdateCompiler updateCompiler = new UpdateCompiler(this);
            
            // Determine affected
            Set<EntitySet> tables = new Set<EntitySet>();

            foreach (EntitySetBase extent in GetDynamicModifiedExtents())
            {
                Set<EntitySet> affectedTables = m_viewLoader.GetAffectedTables(extent, m_metadataWorkspace);
                //Since these extents don't have Functions defined for update operations,
                //the affected tables should be provided via MSL.
                //If we dont find any throw an exception
                if (affectedTables.Count == 0)
                {
                    throw EntityUtil.Update(System.Data.Entity.Strings.Update_MappingNotFound(
                        extent.Name), null /*stateEntries*/);
                }

                foreach (EntitySet table in affectedTables)
                {
                    tables.Add(table);
                }
            }

            // Determine changes to apply to each table
            foreach (EntitySet table in tables)
            {
                DbQueryCommandTree umView = m_connection.GetMetadataWorkspace().GetCqtView(table);
                
                // Propagate changes to root of tree (at which point they are S-Space changes)
                ChangeNode changeNode = Propagator.Propagate(this, table, umView);
                
                // Process changes for the table
                TableChangeProcessor change = new TableChangeProcessor(table);
                foreach (UpdateCommand command in change.CompileCommands(changeNode, updateCompiler))
                {
                    yield return command;
                }
            }
        }
예제 #7
0
        // Processes all insert and delete requests in the table's <see cref="ChangeNode" />. Inserts
        // and deletes with the same key are merged into updates.
        internal List <UpdateCommand> CompileCommands(ChangeNode changeNode, UpdateCompiler compiler)
        {
            Set <CompositeKey> keys = new Set <CompositeKey>(compiler.m_translator.KeyComparer);

            // Retrieve all delete results (original values) and insert results (current values) while
            // populating a set of all row keys. The set contains a single key per row.
            Dictionary <CompositeKey, PropagatorResult> deleteResults = ProcessKeys(compiler, changeNode.Deleted, keys);
            Dictionary <CompositeKey, PropagatorResult> insertResults = ProcessKeys(compiler, changeNode.Inserted, keys);

            List <UpdateCommand> commands = new List <UpdateCommand>(deleteResults.Count + insertResults.Count);

            // Examine each row key to see if the row is being deleted, inserted or updated
            foreach (CompositeKey key in keys)
            {
                PropagatorResult deleteResult;
                PropagatorResult insertResult;

                bool hasDelete = deleteResults.TryGetValue(key, out deleteResult);
                bool hasInsert = insertResults.TryGetValue(key, out insertResult);

                Debug.Assert(hasDelete || hasInsert, "(update/TableChangeProcessor) m_keys must not contain a value " +
                             "if there is no corresponding insert or delete");

                try
                {
                    if (!hasDelete)
                    {
                        // this is an insert
                        commands.Add(compiler.BuildInsertCommand(insertResult, this));
                    }
                    else if (!hasInsert)
                    {
                        // this is a delete
                        commands.Add(compiler.BuildDeleteCommand(deleteResult, this));
                    }
                    else
                    {
                        // this is an update because it has both a delete result and an insert result
                        UpdateCommand updateCommand = compiler.BuildUpdateCommand(deleteResult, insertResult, this);
                        if (null != updateCommand)
                        {
                            // if null is returned, it means it is a no-op update
                            commands.Add(updateCommand);
                        }
                    }
                }
                catch (Exception e)
                {
                    if (UpdateTranslator.RequiresContext(e))
                    {
                        // collect state entries in scope for the current compilation
                        List <IEntityStateEntry> stateEntries = new List <IEntityStateEntry>();
                        if (null != deleteResult)
                        {
                            stateEntries.AddRange(SourceInterpreter.GetAllStateEntries(
                                                      deleteResult, compiler.m_translator, m_table));
                        }
                        if (null != insertResult)
                        {
                            stateEntries.AddRange(SourceInterpreter.GetAllStateEntries(
                                                      insertResult, compiler.m_translator, m_table));
                        }

                        throw EntityUtil.Update(System.Data.Entity.Strings.Update_GeneralExecutionException,
                                                e, stateEntries);
                    }
                    throw;
                }
            }

            return(commands);
        }