Пример #1
0
        internal RecipeRecord GetRecord(string nrdoId, RecordKey key)
        {
            RecipeRecord resultById = null;

            if (nrdoId != null)
            {
                if (records.ContainsKey(nrdoId))
                {
                    resultById = verifyRecord(records[nrdoId]);
                }
            }
            RecipeRecord resultByKey = null;

            if (key != null && key.IsDefined)
            {
                Dictionary <RecordKey, RecipeRecord> byKey;
                if (!recordsByKey.ContainsKey(key.Table.Name))
                {
                    // This is where we load all the records from the context based on their primary key.
                    // Normally, this is pretty straightforward. However, since adding support for table renaming, it's possible that more than one record can end
                    // up existing with the same primary key - because at some point in the past we had used a new nrdo.id and a find.by to compensate for the
                    // previous lack of renaming support. So there's a record from the old table name with an old nrdo.id and a record from the current table
                    // name with the new nrdo.id and they both are now understood to represent the same record with the same id. Having two records for the same
                    // table with the same primary key but different nrdo.ids is of course forbidden. The way we disambiguate is to look and see if one of them
                    // has the current table name and the other does not - and if so we remove the one with the outdated table name.
                    // There is a theoretical possibility that there could be a double-rename of a table so two records could *both* have outdated table names.
                    // If that happens, the correct solution would be to determine which table name is more recent, but at the moment we do not attempt to do this.
                    // It'd also be possible to attempt to check whether the record in question actually still exists in the DB - and if not, of course,
                    // we can ignore it in the RecipeContext. Again, we don't currently attempt to do this.
                    List <RecipeRecord> recordsToRemove = new List <RecipeRecord>();
                    byKey = new Dictionary <RecordKey, RecipeRecord>();
                    foreach (RecipeRecord record in records.Values)
                    {
                        if (record.TableName == key.Table.Name)
                        {
                            var thisRecord = record;
                            var recordKey  = new RecordKey(thisRecord);
                            if (byKey.ContainsKey(recordKey))
                            {
                                var otherRecord      = byKey[recordKey];
                                var thisOneIsCurrent = record.TableName == thisRecord.OriginalTableName;
                                var otherIsCurrent   = otherRecord.TableName == otherRecord.OriginalTableName;

                                if (thisOneIsCurrent == otherIsCurrent)
                                {
                                    throw new ArgumentException("The recipe context contains the same record key " + recordKey.ToString() + " for two separate records (nrdo.ids " + thisRecord.NrdoId + " and " + otherRecord.NrdoId + ").\r\n" +
                                                                "This can happen due to the fact that recipes now support table renaming and records with old table names that were being ignored can now pop up and conflict with new records.\r\n" +
                                                                "However, in this case, we cannot disambiguate based on table name because " + (thisOneIsCurrent ? "both" : "neither") + " of them have the current table name. See the comments in RecipeContext.cs for more details.");
                                }
                                else if (thisOneIsCurrent)
                                {
                                    recordsToRemove.Add(otherRecord);
                                }
                                else
                                {
                                    recordsToRemove.Add(thisRecord);
                                    thisRecord = otherRecord;
                                }
                            }
                            byKey[recordKey] = thisRecord;
                        }
                    }
                    recordsByKey[key.Table.Name] = byKey;
                    foreach (var remove in recordsToRemove)
                    {
                        records.Remove(remove.InternalId);
                    }
                }
                byKey = recordsByKey[key.Table.Name];
                byKey.TryGetValue(key, out resultByKey);
                resultByKey = verifyRecord(resultByKey);
            }
            // If they both got results and they weren't equal, that's an error
            if (resultById != null && resultByKey != null && resultById != resultByKey)
            {
                throw new ArgumentException("Record found by nrdo.id='" + nrdoId + "' does not match record found by primary key");
            }

            // Getting here means that either one or both is null, or they're equal, so there's
            // only one possible record to return as the result:
            RecipeRecord result = resultById ?? resultByKey;

            // If a result found by key has a nrdoId, but it doesn't match the specified
            // nrdoId, that's an error
            if (resultByKey != null && nrdoId != null &&
                resultByKey.NrdoId != null && resultByKey.NrdoId != nrdoId)
            {
                throw new ArgumentException("Record in context found by primary key has nrdo.id='" + resultByKey.NrdoId + "', does not match specified nrdo.id='" + nrdoId + "'");
            }
            // If key was given, and a record was found by nrdoId that doesn't
            // match any of the specified fields of key, that's an error
            if (resultById != null && key != null)
            {
                foreach (NrdoFieldRef field in key.Table.PkeyGet.Fields)
                {
                    object value = key.GetValue(field.Field);
                    if (!(value is Undefined))
                    {
                        object actualValue = field.Field.Get(resultById.GetData());
                        if (!(value is Undefined) && !object.Equals(value, actualValue))
                        {
                            throw new ArgumentException("Record found in context for nrdo.id='" + nrdoId + "' has value of field " + field.Field.Name + " (" + actualValue + ") that does not match what was specified (" + value + ")");
                        }
                    }
                }
            }

            // If it couldn't be found in the context it might still be findable in
            // the database itself...
            if (result == null && key != null && key.IsDefined)
            {
                result = new RecipeRecord(this, key.Table, nrdoId);
                foreach (NrdoFieldRef field in key.Table.PkeyGet.Fields)
                {
                    object value = key.GetValue(field.Field);
                    result.PutField(new RecipeValueField(result, field.Field.Name, value));
                }
                if (result.GetData() == null)
                {
                    result = null;
                }
            }

            return(result);
        }