/// <summary>
        /// Prepare the SQL to load in all values of all fields in the entity request.
        /// </summary>
        private void ProcessFields( )
        {
            ISet <IEntityRef> fields = new HashSet <IEntityRef>( );

            foreach (var v in _requestNodeMap.Values)
            {
                fields.UnionWith(v.Request.Fields);
            }

            // Cache whether the fields are virtual
            BulkRequestHelper.IsVirtualField(fields);

            // Creates a lookup, that for each type of table contains a list of tuples,
            // with each tuple matching node tags to fields that need to be loaded for that node tag.
            ILookup <string, Tuple <int, long> > fieldMap =
                (from nodeInfo in _requestNodeMap.Values
                 from field in nodeInfo.Request.Fields
                 let fieldInfo = RegisterFieldInfo(field)
                                 where !(fieldInfo.IsVirtualAccessControlField || fieldInfo.IsCalculated)
                                 select new Tuple <int, long>(nodeInfo.Tag, field.Id)
                ).ToLookup(MapTagAndFieldPairToTableName);

            // Create sql
            foreach (var list in fieldMap)
            {
                string tableName = list.Key;

                // Frustrate injection attack.
                if (!(tableName.StartsWith("Data_") && tableName.Length < 15))
                {
                    throw new InvalidOperationException("Field type table name was invalid: " + tableName);
                }

                DataTable dt  = TableValuedParameter.Create(TableValuedParameterType.BulkFldType);
                var       map = new HashSet <Tuple <int, long> >( );

                foreach (Tuple <int, long> entry in list)
                {
                    if (map.Contains(entry))
                    {
                        continue;
                    }

                    map.Add(entry);
                    dt.Rows.Add(entry.Item1, entry.Item2);
                }

                string tvpName = "@fld" + tableName;

                _result.TableValuedParameters [tvpName] = dt;
            }
        }
        /// <summary>
        /// Maps field IDs to the database tables that they get stored in.
        /// </summary>
        /// <param name="fieldId"></param>
        /// <returns></returns>
        private FieldInfo RegisterFieldInfo(IEntityRef fieldId)
        {
            // Get/convert the type info for the field (and store for later, since we're already here)
            FieldInfo fieldInfo;

            if (!_result.FieldTypes.TryGetValue(fieldId.Id, out fieldInfo))
            {
                // Get the field
                Field field = Entity.Get <Field>(fieldId.Id);

                fieldInfo = new FieldInfo();
                _result.FieldTypes.Add(fieldId.Id, fieldInfo);

                fieldInfo.DatabaseType = field.ConvertToDatabaseType();
                fieldInfo.IsWriteOnly  = field.IsFieldWriteOnly ?? false;
                fieldInfo.IsCalculated = Factory.CalculatedFieldMetadataProvider.IsCalculatedField(fieldId.Id);
                fieldInfo.IsVirtualAccessControlField = BulkRequestHelper.IsVirtualAccessControlField(new EntityRef(fieldId));

                FieldType fieldType = field.GetFieldType();
                fieldInfo.DatabaseTable = string.Intern(fieldType.DbFieldTable);    // intern to avoid lots of copies of the same database table names in memory
            }

            return(fieldInfo);
        }