예제 #1
0
        public TypeSchema Initialize(KnownSerializers serializers, TypeSchema targetSchema)
        {
            var runtimeSchema = TypeSchema.FromType(typeof(T), serializers.RuntimeVersion, this.GetType(), Version);
            var members       = runtimeSchema.GetCompatibleMemberSet(targetSchema);

            this.deserializeImpl = Generator.GenerateDeserializeMethod <T>(il => Generator.EmitDeserializeFields(typeof(T), serializers, il, members));
            this.serializeImpl   = Generator.GenerateSerializeMethod <T>(il => Generator.EmitSerializeFields(typeof(T), serializers, il, members));
            this.cloneImpl       = Generator.GenerateCloneMethod <T>(il => Generator.EmitCloneFields(typeof(T), serializers, il));
            this.clearImpl       = Generator.GenerateClearMethod <T>(il => Generator.EmitClearFields(typeof(T), serializers, il));

            return(targetSchema ?? runtimeSchema);
        }
예제 #2
0
        public TypeSchema Initialize(KnownSerializers serializers, TypeSchema targetSchema)
        {
            if (targetSchema?.Version <= 2)
            {
                // maintain backward compatibility with older serialized data
                this.innerSerializer = new ClassSerializer <MemoryStream>();
            }
            else
            {
                // otherwise default to the new implementation
                this.innerSerializer = new MemoryStreamSerializerImpl();
            }

            return(this.innerSerializer.Initialize(serializers, targetSchema));
        }
예제 #3
0
        /// <summary>
        /// Validate whether two schemas are compatible.
        /// </summary>
        /// <remarks>Schemas are compatible if all required fields are present in both (regardless of type).</remarks>
        /// <param name="other">Other type schema.</param>
        public void ValidateCompatibleWith(TypeSchema other)
        {
            if (this.IsPartial || other == null || other.IsPartial)
            {
                return;
            }

            if ((this.flags & TypeFlags.IsClass) != 0 && (other.flags & TypeFlags.IsStruct) != 0)
            {
                throw new SerializationException($"The type {this.TypeName} changed between versions from struct to class, which is not supported.");
            }
            else if ((this.flags & TypeFlags.IsStruct) != 0 && (other.flags & TypeFlags.IsClass) != 0)
            {
                throw new SerializationException($"The type {this.TypeName} changed between versions from class to struct, which is not supported.");
            }

            // required members in this schema must be present in the other schema
            var requiredAndMissing = this.Members.Where(mbr => mbr.IsRequired && !other.map.ContainsKey(mbr.Name));

            if (requiredAndMissing.Count() > 0)
            {
                if (other.TypeName != this.TypeName)
                {
                    throw new SerializationException($"The schema {other.Name} version {other.Version} (implemented by {other.TypeName}) is missing the following members required in the current version of {this.TypeName}: {string.Join(",", requiredAndMissing)}");
                }
                else
                {
                    throw new SerializationException($"The type {this.TypeName} appears to have changed in a way that makes it incompatible with previous versions. The following members required by the new version are missing: {string.Join(",", requiredAndMissing)}");
                }
            }

            // all members in the other schema need to be present in this schema
            requiredAndMissing = other.Members.Where(o => !this.map.ContainsKey(o.Name));
            if (requiredAndMissing.Count() > 0)
            {
                if (other.TypeName != this.TypeName)
                {
                    throw new SerializationException($"The schema {other.Name} version {other.Version} (implemented by {other.TypeName}) contains the following members which are not present in the current version of {this.TypeName}: {string.Join(",", requiredAndMissing)}");
                }
                else
                {
                    throw new SerializationException($"The type {this.TypeName} appears to have changed in a way that makes it incompatible with previous versions. The following members required by the old version are missing in the new version: {string.Join(",", requiredAndMissing)}");
                }
            }
        }
예제 #4
0
        /// <summary>
        /// Captures the schema provided by a persisted store.
        /// </summary>
        /// <param name="schema">The schema to register.</param>
        internal void RegisterSchema(TypeSchema schema)
        {
            var id = schema.Id;

            // skip if a handler is already registered for this id
            if (this.handlersById.ContainsKey(id))
            {
                return;
            }

            if (schema.IsPartial && this.schemasById.TryGetValue(id, out TypeSchema otherSchema))
            {
                // schema is already registered
                return;
            }

            // store the schema and override whatever is present already
            this.schemas[schema.Name]   = schema;
            this.schemasById[schema.Id] = schema;
        }
예제 #5
0
        /// <summary>
        /// Registers a given type with the specified contract name.
        /// Use this overload to deserialize data persisted before a type name change.
        /// </summary>
        /// <param name="type">The type to use when deserializing objects with the specified contract.</param>
        /// <param name="contractName">The name to remap. This can be a full type name or a contract name.</param>
        /// <param name="cloningFlags">Optional flags that control the cloning behavior for this type.</param>
        public void Register(Type type, string contractName, CloningFlags cloningFlags = CloningFlags.None)
        {
            contractName = contractName ?? TypeSchema.GetContractName(type, this.runtimeVersion);
            if (this.knownTypes.TryGetValue(contractName, out Type existingType) && existingType != type)
            {
                throw new SerializationException($"Cannot register type {type.AssemblyQualifiedName} under the contract name {contractName} because the type {existingType.AssemblyQualifiedName} is already registered under the same name.");
            }

            if (this.cloningFlags.TryGetValue(type, out var existingFlags) || this.handlersByType.ContainsKey(type))
            {
                // cannot re-register once type flags has been registered or handler has been created
                if (existingFlags != cloningFlags)
                {
                    throw new SerializationException($"Cannot register type {type.AssemblyQualifiedName} with cloning flags ({cloningFlags}) because a handler for it has already been created with flags ({existingFlags}).");
                }
            }

            this.knownTypes[contractName] = type;
            this.knownNames[type]         = contractName;
            this.cloningFlags[type]       = cloningFlags;
        }
예제 #6
0
        /// <summary>
        /// Retrieves the <see cref="MemberInfo"/> information for each member of the type schema, based on a target schema specification.
        /// </summary>
        /// <param name="targetSchema">The schema specification describing which members and in which order to enumerate.
        /// If null, all members are returned in their original order.</param>
        /// <returns>A collection of <see cref="MemberInfo"/> objects.</returns>
        public IEnumerable <MemberInfo> GetCompatibleMemberSet(TypeSchema targetSchema = null)
        {
            if (targetSchema == null || targetSchema.IsPartial)
            {
                return(this.Members.Select(t => t.MemberInfo));
            }

            this.ValidateCompatibleWith(targetSchema);

            var set = new List <MemberInfo>(targetSchema.Members.Length);

            foreach (var o in targetSchema.Members)
            {
                if (this.map.TryGetValue(o.Name, out TypeMemberSchema ms))
                {
                    set.Add(ms.MemberInfo);
                }
            }

            return(set);
        }
예제 #7
0
 // helper fn to deal with v0 "schema" ([id, type] pairs)
 internal void RegisterMetadata(IEnumerable <Metadata> metadata)
 {
     foreach (var meta in metadata)
     {
         if (meta.Kind == MetadataKind.StreamMetadata)
         {
             var sm = meta as PsiStreamMetadata;
             if (sm.RuntimeTypes != null)
             {
                 // v0 has runtime types affixed to each stream metadata
                 foreach (var kv in sm.RuntimeTypes)
                 {
                     var schema = new TypeSchema(kv.Value, kv.Key, kv.Value, 0);
                     this.RegisterSchema(schema);
                 }
             }
         }
         else if (meta.Kind == MetadataKind.TypeSchema)
         {
             this.RegisterSchema((TypeSchema)meta);
         }
     }
 }
예제 #8
0
            /// <inheritdoc />
            public TypeSchema Initialize(KnownSerializers serializers, TypeSchema targetSchema)
            {
                // Add a comparerHandler of type IEqualityComparer. This will take care of serializing,
                // deserializing and cloning the Comparer member of Dictionary. Because the comparer field
                // is private, we will also need to generate a dynamic method so that we can set the
                // comparer field upon deserialization or cloning. This method should be invoked right after
                // clearing the target Dictionary, before adding the deserialized or cloned entries to it.
                this.comparerHandler = serializers.GetHandler <IEqualityComparer <TKey> >();
                this.setComparerImpl = this.GenerateSetComparerMethod();

                // Use an array serializer to serialize the dictionary elements as an array of key-value pairs
                this.entriesHandler = serializers.GetHandler <KeyValuePair <TKey, TValue>[]>();

                var type = typeof(Dictionary <TKey, TValue>);
                var name = TypeSchema.GetContractName(type, serializers.RuntimeVersion);

                // Treat the Dictionary as a class with 2 members - a comparer and an array of key-value pairs
                var comparerMember = new TypeMemberSchema("Comparer", typeof(IEqualityComparer <TKey>).AssemblyQualifiedName, true);
                var entriesMember  = new TypeMemberSchema("KeyValuePairs", typeof(KeyValuePair <TKey, TValue>[]).AssemblyQualifiedName, true);
                var schema         = new TypeSchema(name, TypeSchema.GetId(name), type.AssemblyQualifiedName, TypeFlags.IsClass, new[] { comparerMember, entriesMember }, SchemaVersion);

                return(targetSchema ?? schema);
            }
예제 #9
0
 public TypeSchema Initialize(KnownSerializers serializers, TypeSchema targetSchema)
 {
     return(null);
 }
예제 #10
0
        /// <summary>
        /// Creates and registers a handler for the specified type according to the rules added so far.
        /// </summary>
        /// <typeparam name="T">The type being serialized.</typeparam>
        /// <returns>The newly created handler.</returns>
        private SerializationHandler AddHandler <T>()
        {
            SerializationHandler handler = null;
            var             type         = typeof(T);
            ISerializer <T> serializer   = null;
            TypeSchema      schema       = null;

            if (!this.knownNames.TryGetValue(type, out string name))
            {
                name = TypeSchema.GetContractName(type, this.runtimeVersion);
            }

            if (!this.schemas.TryGetValue(name, out schema))
            {
                // try to match to an existing schema without assembly/version info
                string typeName = TypeResolutionHelper.RemoveAssemblyName(type.AssemblyQualifiedName);
                schema = this.schemas.Values.FirstOrDefault(s => TypeResolutionHelper.RemoveAssemblyName(s.TypeName) == typeName);
            }

            int id = schema?.Id ?? TypeSchema.GetId(name);

            serializer = this.CreateSerializer <T>();
            handler    = SerializationHandler.Create <T>(serializer, schema?.Name ?? name, id);

            // first register the handler
            int oldCount    = this.handlers.Length;
            var newHandlers = new SerializationHandler[oldCount + 1];

            Array.Copy(this.handlers, newHandlers, oldCount);
            newHandlers[oldCount] = handler;
            this.handlers         = newHandlers;

            var newIndex = new Dictionary <SerializationHandler, int>(this.index);

            newIndex[handler] = oldCount;
            this.index        = newIndex;

            var newHandlersByType = new Dictionary <Type, SerializationHandler>(this.handlersByType);

            newHandlersByType[type] = handler;
            this.handlersByType     = newHandlersByType;

            var newHandlersById = new Dictionary <int, SerializationHandler>(this.handlersById);

            newHandlersById[handler.Id] = handler;
            this.handlersById           = newHandlersById;

            // find the schema for this serializer (can be null for interfaces)
            if (serializer != null)
            {
                // initialize the serializer after the handler is registered,
                // to make sure all handlers are registered before initialization runs and
                // allow the serializer initialization code to find and cache the handlers for the types it needs
                schema = serializer.Initialize(this, schema);

                // let any subscribers know that we initialized a new serializer that publishes a schema
                if (schema != null)
                {
                    // store the updated schema and override whatever is present already
                    this.schemas[schema.Name]   = schema;
                    this.schemasById[schema.Id] = schema;
                    this.SchemaAdded?.Invoke(this, schema);
                }
            }

            return(handler);
        }
예제 #11
0
 public TypeSchema Initialize(KnownSerializers serializers, TypeSchema targetSchema)
 {
     // schema is not used, since the behavior of arrays is hard-coded
     return(null);
 }
예제 #12
0
 public TypeSchema Initialize(KnownSerializers serializers, TypeSchema targetSchema)
 {
     return(targetSchema ?? TypeSchema.FromType(typeof(string), serializers.RuntimeVersion, this.GetType(), Version));
 }