Beispiel #1
0
        /// <summary> Read state data function for this storage provider.</summary>
        /// <see cref="IGrainStorage.ReadStateAsync(string, GrainReference, IGrainState)"/>.
        public async Task ReadStateAsync(string grainType, GrainReference grainReference, IGrainState grainState)
        {
            //It assumed these parameters are always valid. If not, an exception will be thrown, even if not as clear
            //as with explicitly checked parameters.
            var grainId       = GrainIdAndExtensionAsString(grainReference);
            var baseGrainType = ExtractBaseClass(grainType);

            if (logger.IsEnabled(LogLevel.Trace))
            {
                logger.Trace((int)RelationalStorageProviderCodes.RelationalProviderReading, LogString("Reading grain state", serviceId, this.name, grainState.ETag, baseGrainType, grainId.ToString()));
            }

            try
            {
                SerializationChoice choice = StorageSerializationPicker.PickDeserializer(serviceId, this.name, baseGrainType, grainReference, grainState, null);
                if (choice.Deserializer == null)
                {
                    var errorString = LogString("No deserializer found", serviceId, this.name, grainState.ETag, baseGrainType, grainId.ToString());
                    logger.Error((int)RelationalStorageProviderCodes.RelationalProviderNoDeserializer, errorString);
                    throw new InvalidOperationException(errorString);
                }

                var commandBehavior = choice.PreferStreaming ? CommandBehavior.SequentialAccess : CommandBehavior.Default;
                var grainIdHash     = HashPicker.PickHasher(serviceId, this.name, baseGrainType, grainReference, grainState).Hash(grainId.GetHashBytes());
                var grainTypeHash   = HashPicker.PickHasher(serviceId, this.name, baseGrainType, grainReference, grainState).Hash(Encoding.UTF8.GetBytes(baseGrainType));
                var readRecords     = (await Storage.ReadAsync(CurrentOperationalQueries.ReadFromStorage, (command =>
                {
                    command.AddParameter("GrainIdHash", grainIdHash);
                    command.AddParameter("GrainIdN0", grainId.N0Key);
                    command.AddParameter("GrainIdN1", grainId.N1Key);
                    command.AddParameter("GrainTypeHash", grainTypeHash);
                    command.AddParameter("GrainTypeString", baseGrainType);
                    command.AddParameter("GrainIdExtensionString", grainId.StringKey);
                    command.AddParameter("ServiceId", serviceId);
                }), async(selector, resultSetCount, token) =>
                {
                    object storageState = null;
                    int?version;
                    if (choice.PreferStreaming)
                    {
                        //When streaming via ADO.NET, using CommandBehavior.SequentialAccess, the order of
                        //the columns on how they are read needs to be exactly this.
                        const int binaryColumnPositionInSelect = 0;
                        const int xmlColumnPositionInSelect = 1;
                        const int jsonColumnPositionInSelect = 2;
                        var streamSelector = (DbDataReader)selector;
                        if (!(await streamSelector.IsDBNullAsync(binaryColumnPositionInSelect)))
                        {
                            using (var downloadStream = streamSelector.GetStream(binaryColumnPositionInSelect, Storage))
                            {
                                storageState = choice.Deserializer.Deserialize(downloadStream, grainState.Type);
                            }
                        }

                        if (!(await streamSelector.IsDBNullAsync(xmlColumnPositionInSelect)))
                        {
                            using (var downloadStream = streamSelector.GetTextReader(xmlColumnPositionInSelect))
                            {
                                storageState = choice.Deserializer.Deserialize(downloadStream, grainState.Type);
                            }
                        }

                        if (!(await streamSelector.IsDBNullAsync(jsonColumnPositionInSelect)))
                        {
                            using (var downloadStream = streamSelector.GetTextReader(jsonColumnPositionInSelect))
                            {
                                storageState = choice.Deserializer.Deserialize(downloadStream, grainState.Type);
                            }
                        }

                        version = await streamSelector.GetValueAsync <int?>("Version");
                    }
                    else
                    {
                        //All but one of these should be null. All will be read and an appropriate deserializer picked.
                        //NOTE: When streaming will be implemented, it is worthwhile to optimize this so that the defined
                        //serializer will be picked and then streaming tried according to its tag.
                        object payload;
                        payload = selector.GetValueOrDefault <byte[]>("PayloadBinary");
                        if (payload == null)
                        {
                            payload = selector.GetValueOrDefault <string>("PayloadXml");
                        }

                        if (payload == null)
                        {
                            payload = selector.GetValueOrDefault <string>("PayloadJson");
                        }

                        if (payload != null)
                        {
                            storageState = choice.Deserializer.Deserialize(payload, grainState.Type);
                        }

                        version = selector.GetNullableInt32("Version");
                    }

                    return(Tuple.Create(storageState, version?.ToString(CultureInfo.InvariantCulture)));
                }, CancellationToken.None, commandBehavior).ConfigureAwait(false)).SingleOrDefault();

                object state = readRecords != null ? readRecords.Item1 : null;
                string etag  = readRecords != null ? readRecords.Item2 : null;
                if (state == null)
                {
                    logger.Info((int)RelationalStorageProviderCodes.RelationalProviderNoStateFound, LogString("Null grain state read (default will be instantiated)", serviceId, this.name, grainState.ETag, baseGrainType, grainId.ToString()));
                    state = Activator.CreateInstance(grainState.Type);
                }

                grainState.State = state;
                grainState.ETag  = etag;
                if (logger.IsEnabled(LogLevel.Trace))
                {
                    logger.Trace((int)RelationalStorageProviderCodes.RelationalProviderRead, LogString("Read grain state", serviceId, this.name, grainState.ETag, baseGrainType, grainId.ToString()));
                }
            }
            catch (Exception ex)
            {
                logger.Error((int)RelationalStorageProviderCodes.RelationalProviderReadError, LogString("Error reading grain state", serviceId, this.name, grainState.ETag, baseGrainType, grainId.ToString(), ex.Message), ex);
                throw;
            }
        }
        /// <summary> Write state data function for this storage provider.</summary>
        /// <see cref="IStorageProvider.WriteStateAsync"/>
        public async Task WriteStateAsync(string grainType, GrainReference grainReference, IGrainState grainState)
        {
            //It assumed these parameters are always valid. If not, an exception will be thrown, even if not as clear
            //as with explicitly checked parameters.
            var data          = grainState.State;
            var grainId       = GrainIdAndExtensionAsString(grainReference);
            var baseGrainType = ExtractBaseClass(grainType);

            if (logger.IsEnabled(LogLevel.Trace))
            {
                logger.Trace((int)RelationalStorageProviderCodes.RelationalProviderWriting, LogString("Writing grain state", ServiceId, Name, grainState.ETag, baseGrainType, grainId.ToString()));
            }

            string storageVersion = null;

            try
            {
                var grainIdHash   = HashPicker.PickHasher(ServiceId, Name, baseGrainType, grainReference, grainState).Hash(grainId.GetHashBytes());
                var grainTypeHash = HashPicker.PickHasher(ServiceId, Name, baseGrainType, grainReference, grainState).Hash(Encoding.UTF8.GetBytes(baseGrainType));
                var writeRecord   = await Storage.ReadAsync(CurrentOperationalQueries.WriteToStorage, command =>
                {
                    command.AddParameter("GrainIdHash", grainIdHash);
                    command.AddParameter("GrainIdN0", grainId.N0Key);
                    command.AddParameter("GrainIdN1", grainId.N1Key);
                    command.AddParameter("GrainTypeHash", grainTypeHash);
                    command.AddParameter("GrainTypeString", baseGrainType);
                    command.AddParameter("GrainIdExtensionString", grainId.StringKey);
                    command.AddParameter("ServiceId", ServiceId);
                    command.AddParameter("GrainStateVersion", !string.IsNullOrWhiteSpace(grainState.ETag) ? int.Parse(grainState.ETag, CultureInfo.InvariantCulture) : default(int?));

                    SerializationChoice serializer = StorageSerializationPicker.PickSerializer(ServiceId, Name, baseGrainType, grainReference, grainState);
                    command.AddParameter("PayloadBinary", (byte[])(serializer.Serializer.Tag == UseBinaryFormatPropertyName ? serializer.Serializer.Serialize(data) : null));
                    command.AddParameter("PayloadJson", (string)(serializer.Serializer.Tag == UseJsonFormatPropertyName ? serializer.Serializer.Serialize(data) : null));
                    command.AddParameter("PayloadXml", (string)(serializer.Serializer.Tag == UseXmlFormatPropertyName ? serializer.Serializer.Serialize(data) : null));
                }, (selector, resultSetCount, token) =>
                                                            { return(Task.FromResult(selector.GetValueOrDefault <int?>("NewGrainStateVersion").ToString())); }, CancellationToken.None).ConfigureAwait(false);

                storageVersion = writeRecord.SingleOrDefault();
            }
            catch (Exception ex)
            {
                logger.Error((int)RelationalStorageProviderCodes.RelationalProviderWriteError, LogString("Error writing grain state", ServiceId, Name, grainState.ETag, baseGrainType, grainId.ToString(), ex.Message), ex);
                throw;
            }

            const string OperationString            = "WriteState";
            var          inconsistentStateException = CheckVersionInconsistency(OperationString, ServiceId, Name, storageVersion, grainState.ETag, baseGrainType, grainId.ToString());

            if (inconsistentStateException != null)
            {
                throw inconsistentStateException;
            }

            //No errors found, the version of the state held by the grain can be updated.
            grainState.ETag = storageVersion;

            if (logger.IsEnabled(LogLevel.Trace))
            {
                logger.Trace((int)RelationalStorageProviderCodes.RelationalProviderWrote, LogString("Wrote grain state", ServiceId, Name, grainState.ETag, baseGrainType, grainId.ToString()));
            }
        }