コード例 #1
0
        private static TPayload SafeConvertBack <TPayload>(DynamicTableEntity dte)
        {
            var payloadType = typeof(TPayload);

            if (!_validPropertyNames.ContainsKey(payloadType))
            {
                lock (payloadType)
                {
                    if (!_validPropertyNames.ContainsKey(payloadType))
                    {
                        var properties = payloadType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
                        _validPropertyNames[payloadType] = properties.Select(p => p.Name).ToList();
                    }
                }
            }

            var propertyNames = _validPropertyNames[payloadType];

            var entityProperties = new Dictionary <string, EntityProperty>();

            foreach (var kv in dte.Properties.Where(p => propertyNames.Contains(p.Key)))
            {
                entityProperties[kv.Key] = kv.Value;
            }
            var payload = EntityPropertyConverter.ConvertBack <TPayload>(entityProperties, new OperationContext());

            return(payload);
        }
コード例 #2
0
        public static async Task UpdateAsync <TPayload>(
            this CloudTable table, TPayload payload,
            KeysPair keys)
            where TPayload : class, new()
        {
            Ensure.ArgumentNotNull(keys, nameof(keys));
            Ensure.ArgumentNotNull(payload, nameof(payload));
            Ensure.ArgumentNotNull(table, nameof(table));

            var entity = new DynamicTableEntity(keys.PartitionKey, keys.RowKey);

            entity.Timestamp  = DateTime.UtcNow;
            entity.Properties = EntityPropertyConverter.Flatten(payload, new OperationContext());
            entity.ETag       = "*";

            var batch = new TableBatchOperation();

            batch.Replace(entity);

            await table.ExecuteBatchAsync(batch)
            .ConfigureAwait(false);

            await table.ExecuteBatchAsync(batch)
            .ConfigureAwait(false);
        }
コード例 #3
0
        /// <summary>
        /// Gets a collection of <see cref="IEvent{TAuthenticationToken}"/> for the <see cref="IAggregateRoot{TAuthenticationToken}"/> of type <paramref name="aggregateRootType"/> with the ID matching the provided <paramref name="aggregateId"/>.
        /// </summary>
        /// <param name="aggregateRootType"> <see cref="Type"/> of the <see cref="IAggregateRoot{TAuthenticationToken}"/> the <see cref="IEvent{TAuthenticationToken}"/> was raised in.</param>
        /// <param name="aggregateId">The <see cref="IAggregateRoot{TAuthenticationToken}.Id"/> of the <see cref="IAggregateRoot{TAuthenticationToken}"/>.</param>
        /// <param name="useLastEventOnly">Loads only the last event<see cref="IEvent{TAuthenticationToken}"/>.</param>
        /// <param name="fromVersion">Load events starting from this version</param>
        public override IEnumerable <IEvent <TAuthenticationToken> > Get(Type aggregateRootType, Guid aggregateId, bool useLastEventOnly = false, int fromVersion = -1)
        {
            string streamName = GenerateStreamName(aggregateRootType, aggregateId);

            // Create the table query.
            var rangeQuery = new TableQuery <DynamicTableEntity>().Where
                             (
                TableQuery.CombineFilters
                (
                    TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, StorageStore <object, object> .GetSafeStorageKey(streamName)),
                    TableOperators.And,
                    TableQuery.GenerateFilterCondition("AggregateId", QueryComparisons.Equal, streamName)
                )
                             );

            var operationContext          = new OperationContext();
            IEnumerable <EventData> query = TableStorageStore.ReadableSource.ExecuteQuery(rangeQuery)
#pragma warning disable 0436
                                            .Select(eventData => EntityPropertyConverter.ConvertBack <EventData>(eventData.Properties, operationContext))
#pragma warning restore 0436
                                            .Where(eventData => eventData.AggregateId == streamName && eventData.Version > fromVersion)
                                            .OrderByDescending(eventData => eventData.Version);

            if (useLastEventOnly)
            {
                query = query.AsQueryable().Take(1);
            }

            return(query
                   .Select(eventData => EventDeserialiser.Deserialise(eventData))
                   .ToList());
        }
コード例 #4
0
        public async Task Save(TEntity entity, CancellationToken cancellationToken)
        {
            //Flatten object of type Order) and convert it to EntityProperty Dictionary
            Dictionary <string, EntityProperty> flattenedProperties = EntityPropertyConverter.Flatten(entity, new OperationContext());
            var rowKey       = GetEntityId(entity);
            var partitionKey = GetEntityPartitionKey(entity);

            // Create a DynamicTableEntity and set its PK and RK
            DynamicTableEntity dynamicTableEntity = new DynamicTableEntity(partitionKey, rowKey);

            dynamicTableEntity.Properties = flattenedProperties;

            var existingEntity = await Get(dynamicTableEntity.PartitionKey, dynamicTableEntity.RowKey, cancellationToken);

            TableOperation createOrReplaceOperation;

            if (existingEntity == null)
            {
                createOrReplaceOperation = TableOperation.Insert(dynamicTableEntity);
            }
            else
            {
                dynamicTableEntity.ETag  = existingEntity.ETag;
                createOrReplaceOperation = TableOperation.Replace(dynamicTableEntity);
            }

            await _cloudTable.ExecuteAsync(createOrReplaceOperation);
        }
コード例 #5
0
        /// <summary>
        /// Get the latest <see cref="Snapshot"/> from storage.
        /// </summary>
        /// <returns>The most recent <see cref="Snapshot"/> of</returns>
        protected override Snapshot Get(Type aggregateRootType, string streamName)
        {
            // Create the table query.
            var rangeQuery = new TableQuery <DynamicTableEntity>().Where
                             (
                TableQuery.CombineFilters
                (
                    TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, StorageStore <object, object> .GetSafeStorageKey(streamName)),
                    TableOperators.And,
                    TableQuery.GenerateFilterCondition("AggregateId", QueryComparisons.Equal, streamName)
                )
                             );

            var      operationContext = new OperationContext();
            Snapshot result           = TableStorageStore.ReadableSource.ExecuteQuery(rangeQuery)
#pragma warning disable 0436
                                        .Select(eventData => EntityPropertyConverter.ConvertBack <EventData>(eventData.Properties, operationContext))
#pragma warning restore 0436
                                        .Where(eventData => eventData.AggregateId == streamName)
                                        .OrderByDescending(eventData => eventData.Version)
                                        .Take(1)
                                        .Select(EventDeserialiser.Deserialise)
                                        .SingleOrDefault();

            return(result);
        }
コード例 #6
0
        public async Task <TModel> Get <TModel>(string tableName, string partitionKey, string rowKey)
        {
            var context = new OperationContext();
            var entity  = await GetSinglePointResult(tableName, partitionKey, rowKey);

            var model = EntityPropertyConverter.ConvertBack <TModel>(entity.Properties, context);

            return(model);
        }
コード例 #7
0
        public async Task <MasternodeStats> GetMasternodeStats(string coinId)
        {
            TableOperation getOperation = TableOperation.Retrieve <DynamicTableEntity>("coin", coinId);
            var            result       = await this.masternodeStatsTable.ExecuteAsync(getOperation);

            var tableEntity = result.Result as DynamicTableEntity;

            return(EntityPropertyConverter.ConvertBack <MasternodeStats>(tableEntity.Properties, new OperationContext()));
        }
コード例 #8
0
        public async Task <IEnumerable <T> > QueryAsync <T>(IEnumerable <QueryParameter> queryParameters,
                                                            IGraphRequestContext graphRequestContext)
        {
            var info = Get <T>(graphRequestContext);

            var results = await info.Table.ExecuteQuerySegmentedAsync(GenerateTableQuery(queryParameters.ToList()), null);

            return(results.Results.Select(x => EntityPropertyConverter.ConvertBack <T>(x.Properties, new OperationContext())).ToList());
        }
コード例 #9
0
        public async Task <Rating> GetRating(string coinId, string userId)
        {
            TableOperation getOperation = TableOperation.Retrieve <DynamicTableEntity>(coinId, userId);
            var            result       = await this.ratingsTable.ExecuteAsync(getOperation);

            var tableEntity = result.Result as DynamicTableEntity;

            return(tableEntity == null ? null : EntityPropertyConverter.ConvertBack <Rating>(tableEntity.Properties, new OperationContext()));
        }
コード例 #10
0
        public virtual void Add(T obj)
        {
            //Table Exists?
            _cloudTable = _tableClient.GetTableReference(obj.GetType().Name);
            _cloudTable.CreateIfNotExistsAsync().Wait();

            //Obtain Partition Key and Row Key
            //SetPartitionRowKeys(obj);
            PropertyInfo[] props = obj.GetType().GetProperties();
            foreach (var p in props)
            {
                if (p.Name != nameof(RowKey) && p.Name != nameof(PartitionKey) && p.Name != nameof(ETag) && p.Name != nameof(Timestamp))
                {
                    obj.GetType().GetProperty("RowKey").SetValue(obj, p.Name.ToString(), null);

                    //TODO: Create Object with PPartition, Row and Properties
                    string rowkey       = obj.GetType().GetProperty("RowKey").GetValue(obj).ToString();
                    string partitionKey = obj.GetType().GetProperty("PartitionKey").GetValue(obj).ToString();


                    if (p.PropertyType.Name.Contains("String"))
                    {
                        var entity = new DynamicTableEntity(partitionKey, rowkey);
                        //entity.Properties = EntityPropertyConverter.Flatten(p.GetValue(obj), null);

                        entity.Properties.Add("Value", new EntityProperty(p.GetValue(obj).ToString()));
                        InsertIntoTable(entity);
                    }
                    else if (!p.PropertyType.Name.Contains("List"))
                    {
                        var entity = new DynamicTableEntity(partitionKey, rowkey);
                        entity.Properties = EntityPropertyConverter.Flatten(p.GetValue(obj), null);

                        InsertIntoTable(entity);
                    }
                    else
                    {
                        int i = 1;
                        foreach (var f in (IEnumerable)p.GetValue(obj))
                        {
                            var entity = new DynamicTableEntity(partitionKey.ToUpper(), rowkey + EntityPropertyConverter.DefaultPropertyNameDelimiter + i++);
                            entity.Properties = EntityPropertyConverter.Flatten(f, null);

                            InsertIntoTable(entity);
                        }
                    }

                    //_tableOperation = TableOperation.InsertOrMerge((ITableEntity)entity);
                    //_cloudTable.ExecuteAsync(_tableOperation).Wait();
                }
            }

            //Insert or Upsert
            //_tableOperation = TableOperation.InsertOrMerge((ITableEntity)obj);
            //_cloudTable.ExecuteAsync(_tableOperation).Wait();
        }
コード例 #11
0
        public async Task <TEntity> GetByKeys(string partitionKey, string rowKey, CancellationToken cancellationToken)
        {
            var dynamicTableEntity = await Get(partitionKey, rowKey, cancellationToken);

            if (dynamicTableEntity == null)
            {
                return(null);
            }

            return(EntityPropertyConverter.ConvertBack <TEntity>(dynamicTableEntity.Properties, new OperationContext()));
        }
コード例 #12
0
        public async Task AddMasternodeStats(MasternodeStats masternodeStats)
        {
            var flattenedObject = EntityPropertyConverter.Flatten(masternodeStats, new OperationContext());
            var tableEntity     = new DynamicTableEntity("coin", masternodeStats.CoinId);

            tableEntity.Properties = flattenedObject;

            TableOperation insertOperation = TableOperation.InsertOrReplace(tableEntity);

            await this.masternodeStatsTable.ExecuteAsync(insertOperation);
        }
コード例 #13
0
        private DynamicTableEntity Convert <T>(T item, string partitionKey)
        {
            DynamicTableEntity dynamicTableEntity =
                new DynamicTableEntity(partitionKey, item.GetKey())
            {
                Properties = EntityPropertyConverter.Flatten(item, new OperationContext())
            };

            dynamicTableEntity.ETag = "*";
            return(dynamicTableEntity);
        }
コード例 #14
0
        public async Task AddOrUpdateRating(Rating rating)
        {
            var flattenedObject = EntityPropertyConverter.Flatten(rating, new OperationContext());
            var tableEntity     = new DynamicTableEntity(rating.CoinId, rating.UserId);

            tableEntity.Properties = flattenedObject;

            TableOperation insertOperation = TableOperation.InsertOrReplace(tableEntity);

            await this.ratingsTable.ExecuteAsync(insertOperation);
        }
コード例 #15
0
        public async Task AddCoin(Coin coin)
        {
            var flattenedObject = EntityPropertyConverter.Flatten(coin, new OperationContext());
            var tableEntity     = new DynamicTableEntity("coin", coin.Id);

            tableEntity.Properties = flattenedObject;

            TableOperation insertOperation = TableOperation.InsertOrReplace(tableEntity);

            await this.coinsTable.ExecuteAsync(insertOperation);
        }
コード例 #16
0
        public async Task AddDataPoint(DataPoint dataPoint)
        {
            var flattenedObject = EntityPropertyConverter.Flatten(dataPoint, new OperationContext());
            var tableEntity     = new DynamicTableEntity(dataPoint.CoinId, dataPoint.Date);

            tableEntity.Properties = flattenedObject;

            TableOperation insertOperation = TableOperation.InsertOrReplace(tableEntity);

            await this.dataPointsTable.ExecuteAsync(insertOperation);
        }
コード例 #17
0
        public async Task <TEntity> GetByIds(Guid partitionId, Guid rowId, CancellationToken cancellationToken)
        {
            var dynamicTableEntity = await Get(partitionId.ToString(), rowId.ToString(), cancellationToken);

            if (dynamicTableEntity == null)
            {
                return(null);
            }

            return(EntityPropertyConverter.ConvertBack <TEntity>(dynamicTableEntity.Properties, new OperationContext()));
        }
コード例 #18
0
        public override void Get(string id)
        {
            CloudTableClient tableClient = ProgramNew.storageAccount.CreateCloudTableClient();
            CloudTable       table       = tableClient.GetTableReference(this.GetType().Name);

            // Point Query
            TableOperation retrieveServiceOperation = TableOperation.Retrieve <ServiceDetail>("NLAG-CT-20190224", "SERVICEDETAILS");
            var            serviceDetails           = table.ExecuteAsync(retrieveServiceOperation).Result;

            TableOperation retrieveRehearsalOperation = TableOperation.Retrieve <RehearsalDetail>("NLAG-CT-20190224", "REHEARSALDETAILS");
            var            rehearsalDetails           = table.ExecuteAsync(retrieveRehearsalOperation).Result;

            //Range Query on Row Key
            TableQuery rangeQuery = new TableQuery().Where(
                TableQuery.CombineFilters(
                    TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "NLAG-CT-20190224"),
                    TableOperators.And,
                    TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.GreaterThan, "")));

            var worshipLeaders = table.ExecuteQuerySegmentedAsync(rangeQuery, null).Result;

            foreach (var item in worshipLeaders.Results)
            {
                if (item.RowKey == "REHEARSALDETAILS")
                {
                    var props = item.Properties;

                    RehearsalDetail rehearsal = EntityPropertyConverter.ConvertBack <RehearsalDetail>(item.Properties, null);
                }
            }
            ;

            //Range Query on Partition Key
            var rangeQueryPKey = new TableQuery().Where(
                TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.GreaterThan, "NLAG-CT-20190210"));

            var pkeys = table.ExecuteQuerySegmentedAsync(rangeQueryPKey, null).Result;

            //Partition Scan
            TableQuery queryPScan = new TableQuery().Where(
                TableQuery.CombineFilters(
                    TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "NLAG-CT-20190217"),
                    TableOperators.And,
                    TableQuery.GenerateFilterCondition("AG1", QueryComparisons.Equal, "Manjrekar")));

            var ag1Manjrekar = table.ExecuteQuerySegmentedAsync(queryPScan, null).Result;

            //Table Scan
            TableQuery queryTScan = new TableQuery().Where(
                TableQuery.GenerateFilterCondition("AG1", QueryComparisons.Equal, "Manjrekar"));

            var ag1ManjrekarTScan = table.ExecuteQuerySegmentedAsync(queryTScan, null).Result;
        }
コード例 #19
0
 public static async Task <DynamicTableEntity> Wrap <TPayload>(this KeysPair keyPair, TPayload payload)
 {
     return(await Task.Factory.StartNew(() =>
     {
         return new DynamicTableEntity(keyPair.PartitionKey, keyPair.RowKey)
         {
             Timestamp = DateTime.UtcNow,
             Properties = EntityPropertyConverter.Flatten(payload, new OperationContext()),
             ETag = "*"
         };
     }).ConfigureAwait(false));
 }
コード例 #20
0
        public async Task AddCoinExchange(CoinExchange coinExchange)
        {
            var flattenedObject = EntityPropertyConverter.Flatten(coinExchange, new OperationContext());
            var rowKey          = $"{coinExchange.ExchangeIdentifier}-{coinExchange.Base}-{coinExchange.Target}";
            var tableEntity     = new DynamicTableEntity(coinExchange.CoinId, rowKey);

            tableEntity.Properties = flattenedObject;

            TableOperation insertOperation = TableOperation.InsertOrReplace(tableEntity);

            await this.coinExchangesTable.ExecuteAsync(insertOperation);
        }
コード例 #21
0
        private TableOperation BuildOperation <TModel>(TModel source, string partitionKey, string rowKey, Func <DynamicTableEntity, TableOperation> action)
        {
            var context             = new OperationContext();
            var flattenedProperties = EntityPropertyConverter.Flatten(source, context);
            var model = new DynamicTableEntity(partitionKey, rowKey)
            {
                Properties = flattenedProperties
            };

            var operation = action(model);

            return(operation);
        }
コード例 #22
0
        public override void Update(TData data)
        {
            DynamicTableEntity dynamicTableEntity = CreateTableEntity(data) as DynamicTableEntity;

            if (dynamicTableEntity == null)
            {
                base.Update(data);
                return;
            }
            //Convert the DynamicTableEntity back to original complex object.
            TData result = EntityPropertyConverter.ConvertBack <TData>(dynamicTableEntity.Properties, new OperationContext());

            Update(new EntityTableEntity <TData>(result));
        }
コード例 #23
0
            protected override ITableEntity CreateTableEntity(EventData data)
            {
                var tableEntity = new EventDataTableEntity <EventData>(data, IsCorrelationIdTableStorageStore);
                //Flatten object of type TData and convert it to EntityProperty Dictionary
                Dictionary <string, EntityProperty> flattenedProperties = EntityPropertyConverter.Flatten(data, new OperationContext());

                // Create a DynamicTableEntity and set its PK and RK
                DynamicTableEntity dynamicTableEntity = new DynamicTableEntity(tableEntity.PartitionKey, tableEntity.RowKey)
                {
                    Properties = flattenedProperties
                };

                return(dynamicTableEntity);
            }
コード例 #24
0
        public override IEnumerable <EventData> Get(Guid correlationId)
        {
            // Create the table query.
            var rangeQuery = new TableQuery <DynamicTableEntity>().Where
                             (
                TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, StorageStore <object, object> .GetSafeStorageKey(correlationId.ToString("N")))
                             );

            var operationContext          = new OperationContext();
            IEnumerable <EventData> query = CorrelationIdTableStorageStore.ReadableSource.ExecuteQuery(rangeQuery)
                                            .Select(eventData => EntityPropertyConverter.ConvertBack <EventData>(eventData.Properties, operationContext))
                                            .OrderBy(eventData => eventData.Timestamp);

            return(query.ToList());
        }
コード例 #25
0
        public async Task <IEnumerable <Coin> > GetCoins()
        {
            TableContinuationToken token = null;
            var list = new List <Coin>();

            do
            {
                var queryResult = await this.coinsTable.ExecuteQuerySegmentedAsync(new TableQuery <DynamicTableEntity>(), token);

                list.AddRange(queryResult.Results.Select(x => EntityPropertyConverter.ConvertBack <Coin>(x.Properties, new OperationContext())));
                token = queryResult.ContinuationToken;
            } while (token != null);

            return(list);
        }
コード例 #26
0
        /// <summary>
        /// Creates a new instance of <see cref="DynamicTableEntity"/> populating it with the provided <paramref name="data"/>
        /// </summary>
        /// <param name="data">The data to store in <see cref="DynamicTableEntity.Properties"/>.</param>
        protected override ITableEntity CreateTableEntity(TData data)
        {
            var tableEntity = new EntityTableEntity <TData>(data);

            //Flatten object of type TData and convert it to EntityProperty Dictionary
#pragma warning disable 0436
            Dictionary <string, EntityProperty> flattenedProperties = EntityPropertyConverter.Flatten(data, new OperationContext());
#pragma warning restore 0436
            // Create a DynamicTableEntity and set its PK and RK
            DynamicTableEntity dynamicTableEntity = new DynamicTableEntity(tableEntity.PartitionKey, tableEntity.RowKey)
            {
                Properties = flattenedProperties
            };

            return(dynamicTableEntity);
        }
コード例 #27
0
        public static async Task <TPayload> Unwrap <TPayload>
            (this DynamicTableEntity dte, string columnName = Columns.Json)
        {
            if (dte == null)
            {
                return(default(TPayload));
            }

            return(await Task.Factory.StartNew <TPayload>(() =>
            {
                var payload =
                    EntityPropertyConverter.ConvertBack <TPayload>
                        (dte.Properties, new OperationContext());
                return payload;
            }).ConfigureAwait(false));
        }
コード例 #28
0
        public static async Task AddAsync <TPayload>(
            this CloudTable table, TPayload payload,
            KeysPair keys)
        {
            Ensure.ArgumentNotNull(keys, nameof(keys));
            Ensure.ArgumentNotNull(payload, nameof(payload));
            Ensure.ArgumentNotNull(table, nameof(table));

            await table.ExecuteAsync(
                TableOperation.Insert(
                    new DynamicTableEntity(keys.PartitionKey, keys.RowKey)
            {
                Timestamp  = DateTime.UtcNow,
                Properties = EntityPropertyConverter.Flatten(payload, new OperationContext())
            }))
            .ConfigureAwait(false);
        }
コード例 #29
0
        public void Should_gather_Blob_and_CollectionRefs_recursively_correctly_and_EntityPropertyConverter_should_successfully_flatten()
        {
            var testObject = new TableRow()
            {
                Name        = "Outer",
                Document    = new LargeBlob(),
                RelatedItem = new Attachment()
                {
                    Name             = "Att-01",
                    File             = new LargeBlob(),
                    NestedAttachment = new Attachment()
                    {
                        Name             = "Att01-Inner",
                        File             = new LargeBlob(),
                        NestedAttachment = null,
                        Keywords         = new List <string>()
                        {
                            "alpha", "bravo"
                        }
                    }
                },
                Dict = new Dictionary <string, int>()
                {
                    { "Key", 123 }
                }
            };

            var collectedBlobPropertyRefs       = ReflectionUtils.GatherPropertiesWithBlobsRecursive(testObject, new EntityPropertyConverterOptions());
            var collectedCollectionPropertyRefs = ReflectionUtils.GatherPropertiesWithCollectionsRecursive(testObject, new EntityPropertyConverterOptions());

            foreach (var blobPropRef in collectedBlobPropertyRefs)
            {
                blobPropRef.Property.SetValue(blobPropRef.SourceObject, null);
            }

            foreach (var collectionPropertyRef in collectedCollectionPropertyRefs)
            {
                collectionPropertyRef.Property.SetValue(collectionPropertyRef.SourceObject, null);
            }

            var flattened = EntityPropertyConverter.Flatten(testObject, new EntityPropertyConverterOptions(), null);

            collectedBlobPropertyRefs.Count.Should().Be(3);
            collectedCollectionPropertyRefs.Count.Should().Be(2);
        }
コード例 #30
0
        public override EntityTableEntity <TData> GetByKeyAndRow(Guid rsn)
        {
            TableOperation searchQuery = TableOperation.Retrieve <DynamicTableEntity>(typeof(TData).FullName, rsn.ToString("N"));

            TableResult searchResult = ReadableSource.Execute(searchQuery);

            var dynamicTableEntity = searchResult.Result as DynamicTableEntity;

            if (dynamicTableEntity == null)
            {
                return(base.GetByKeyAndRow(rsn));
            }

            //Convert the DynamicTableEntity back to original complex object.
            TData result = EntityPropertyConverter.ConvertBack <TData>(dynamicTableEntity.Properties, new OperationContext());

            return(new EntityTableEntity <TData>(result));
        }