public IEnumerable <Record> Read(IEnumerable <Tuple> source, RecordSetHeader header, Session session)
        {
            CacheItem cacheItem;
            var       recordPartCount = header.ColumnGroups.Count;
            var       context         = new MaterializationContext(session, recordPartCount);

            lock (_lock) {
                if (!cache.TryGetItem(header, false, out cacheItem))
                {
                    var typeIdColumnName = Domain.Handlers.NameBuilder.TypeIdColumnName;
                    var model            = context.Model;
                    var mappings         = new RecordPartMapping[recordPartCount];
                    for (int i = 0; i < recordPartCount; i++)
                    {
                        var columnGroup       = header.ColumnGroups[i];
                        var approximateType   = columnGroup.TypeInfoRef.Resolve(model);
                        var columnMapping     = new List <Pair <int> >();
                        var typeIdColumnIndex = -1;
                        foreach (var columnIndex in columnGroup.Columns)
                        {
                            var       column     = (MappedColumn)header.Columns[columnIndex];
                            var       columnInfo = column.ColumnInfoRef.Resolve(model);
                            FieldInfo fieldInfo;
                            if (!approximateType.Fields.TryGetValue(columnInfo.Field.Name, out fieldInfo))
                            {
                                continue;
                            }
                            var targetColumnIndex = fieldInfo.MappingInfo.Offset;
                            if (columnInfo.Name == typeIdColumnName)
                            {
                                typeIdColumnIndex = column.Index;
                            }
                            columnMapping.Add(new Pair <int>(targetColumnIndex, columnIndex));
                        }
                        mappings[i] = new RecordPartMapping(typeIdColumnIndex, columnMapping.ToArray(), approximateType);
                    }
                    cacheItem = new CacheItem(header, mappings);
                    cache.Add(cacheItem);
                }
            }
            return(source.Select(tuple => ParseRow(tuple, context, cacheItem.Mappings)));
        }
        private Pair <Key, Tuple> ParseColumnGroup(Tuple tuple, MaterializationContext context, int groupIndex, RecordPartMapping mapping)
        {
            TypeReferenceAccuracy accuracy;
            int typeId      = ExtractTypeId(mapping.ApproximateType, context.TypeIdRegistry, tuple, mapping.TypeIdColumnIndex, out accuracy);
            var typeMapping = typeId == TypeInfo.NoTypeId ? null : context.GetTypeMapping(groupIndex, mapping.ApproximateType, typeId, mapping.Columns);

            if (typeMapping == null)
            {
                return(new Pair <Key, Tuple>(null, null));
            }

            bool canCache = accuracy == TypeReferenceAccuracy.ExactType;
            Key  key;

            if (typeMapping.KeyTransform.Descriptor.Count <= WellKnown.MaxGenericKeyLength)
            {
                key = KeyFactory.Materialize(Domain, context.Session.StorageNodeId, typeMapping.Type, tuple, accuracy, canCache, typeMapping.KeyIndexes);
            }
            else
            {
                var keyTuple = typeMapping.KeyTransform.Apply(TupleTransformType.TransformedTuple, tuple);
                key = KeyFactory.Materialize(Domain, context.Session.StorageNodeId, typeMapping.Type, keyTuple, accuracy, canCache, null);
            }
            if (accuracy == TypeReferenceAccuracy.ExactType)
            {
                var entityTuple = typeMapping.Transform.Apply(TupleTransformType.Tuple, tuple);
                return(new Pair <Key, Tuple>(key, entityTuple));
            }
            return(new Pair <Key, Tuple>(key, null));
        }