CosmosDB Entity with the specified id does not exist - c#

I am developing an ASP.NET Core MVC API to call resources in an Azure Cosmos DB. When I try to perform a GET for any specific ID, I receive DocumentClientException: Entity with the specified id does not exist in the system. I can confirm that the entity does exist in the system, and the connection is successful because I can successfully perform other methods and requests. The partition key is _id .
Debugging with breakpoints in Visual Studio, I can see where the correct ID is received at the API, but I can't confirm what specifically it is sending to Azure
The controller methods: (the ID field is a random string of numbers and text)
//controller is MoviesController decorated with [Route(api/[controller])]
//sample GET is to localhost:port/api/Movies/5ca6gdwndkna99
[HttpGet("{id}")]
public async Task<MoviesModel> Get(string id)
{
MoviesModel movie = await _persistence.GetMovieAsync(id);
return movie;
}
The data handling method:
public async Task<MoviesModel> GetMovieAsync(string Id)
{
string _id = Id;
RequestOptions options = new RequestOptions();
options.PartitionKey = new PartitionKey(_id);
var documentUri = UriFactory.CreateDocumentUri(_databaseId, "movies", Id);
Document result = await _client.ReadDocumentAsync(documentUri,options);
return (MoviesModel)(dynamic)result;
}
Other methods, like getting a list of all movies and returning to a table are working fine, so we can rule out network issues
public async Task<List<MoviesModel>> GetMoviesAsync()
{
var documentCollectionUri = UriFactory.CreateDocumentCollectionUri(_databaseId, "movies");
// build the query
var feedOptions = new FeedOptions() { EnableCrossPartitionQuery = true };
var query = _client.CreateDocumentQuery<MoviesModel>(documentCollectionUri, "SELECT * FROM movies", feedOptions);
var queryAll = query.AsDocumentQuery();
// combine the results
var results = new List<MoviesModel>();
while (queryAll.HasMoreResults)
{
results.AddRange(await queryAll.ExecuteNextAsync<MoviesModel>());
}
return results;
}
public async Task<List<GenresModel>> GetGenresAsync()
{
await EnsureSetupAsync();
var documentCollectionUri = UriFactory.CreateDocumentCollectionUri(_databaseId, "genres");
// build the query
var feedOptions = new FeedOptions() { EnableCrossPartitionQuery = true };
var query = _client.CreateDocumentQuery<GenresModel>(documentCollectionUri, "SELECT * FROM genres", feedOptions);
var queryAll = query.AsDocumentQuery();
// combine the results
var results = new List<GenresModel>();
while (queryAll.HasMoreResults)
{
results.AddRange(await queryAll.ExecuteNextAsync<GenresModel>());
}
return results;
}

Firstly, I would suggest to re-look at your cosmosDb design once, bcz of the following reasons...
Problems:
If your _id is random string of numbers and text, then its not good
to have the entire _id as your partition key, bcz this would create a
new partition for each entry.(although azure will range parition it
later)
Querying just by partition key is not efficient, for pin point
queries we should have both partition key and row key.
Solution:
Make the first one or two letters of your _id as your partition key. (so your partitions will be finite).
Make your _id as your row key.
If your _id = "abwed123asdf", then your query should be..
RequestOptions options = new RequestOptions();
options.PartitionKey = new PartitionKey(_id.Substring(0,1));
options.RowKey = _id;
This way, your look up will pin point to the exact required entry with the help of partition and row key. (saves lot of RUs)
Please refer docs for choosing a better partition keys for your needs https://learn.microsoft.com/en-us/azure/cosmos-db/partitioning-overview

I was able to get this to work by completely refactoring to the dotnet v3 SDK. My code for the solution is in the comments of the gitHub link:
using Microsoft.Azure.Cosmos;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using VidlyAsp.DataHandlers;
namespace VidlyAsp.DataHandlers
{
public class PersistenceNew
{
private static string _endpointUri;
private static string _primaryKey;
private CosmosClient cosmosClient;
private CosmosDatabase database;
private CosmosContainer movieContainer;
private CosmosContainer genreContainer;
private string containerId;
private string _databaseId;
public PersistenceNew(Uri endpointUri, string primaryKey)
{
_databaseId = "Vidly";
_endpointUri = endpointUri.ToString();
_primaryKey = primaryKey;
this.GetStartedAsync();
}
public async Task GetStartedAsync()
{
// Create a new instance of the Cosmos Client
this.cosmosClient = new CosmosClient(_endpointUri, _primaryKey);
database = await cosmosClient.Databases.CreateDatabaseIfNotExistsAsync(_databaseId);
CosmosContainer moviesContainer = await GetOrCreateContainerAsync(database, "movies");
CosmosContainer genresContainer = await GetOrCreateContainerAsync(database, "genres");
movieContainer = moviesContainer;
genreContainer = genresContainer;
}
public async Task<GenresModel> GetGenre(string id)
{
var sqlQueryText = ("SELECT * FROM c WHERE c._id = {0}", id).ToString();
var partitionKeyValue = id;
CosmosSqlQueryDefinition queryDefinition = new CosmosSqlQueryDefinition(sqlQueryText);
CosmosResultSetIterator<GenresModel> queryResultSetIterator = this.genreContainer.Items.CreateItemQuery<GenresModel>(queryDefinition, partitionKeyValue);
List<GenresModel> genres = new List<GenresModel>();
while (queryResultSetIterator.HasMoreResults)
{
CosmosQueryResponse<GenresModel> currentResultSet = await queryResultSetIterator.FetchNextSetAsync();
foreach (GenresModel genre in currentResultSet)
{
genres.Add(genre);
}
}
return genres.FirstOrDefault();
}
public async Task<MoviesModel> GetMovie(string id)
{
var sqlQueryText = "SELECT * FROM c WHERE c._id = '" + id + "'";
var partitionKeyValue = id;
CosmosSqlQueryDefinition queryDefinition = new CosmosSqlQueryDefinition(sqlQueryText);
CosmosResultSetIterator<MoviesModel> queryResultSetIterator = this.movieContainer.Items.CreateItemQuery<MoviesModel>(queryDefinition, partitionKeyValue);
List<MoviesModel> movies = new List<MoviesModel>();
while (queryResultSetIterator.HasMoreResults)
{
CosmosQueryResponse<MoviesModel> currentResultSet = await queryResultSetIterator.FetchNextSetAsync();
foreach (MoviesModel movie in currentResultSet)
{
movies.Add(movie);
}
}
return movies.FirstOrDefault();
}
/*
Run a query (using Azure Cosmos DB SQL syntax) against the container
*/
public async Task<List<MoviesModel>> GetAllMovies()
{
List<MoviesModel> movies = new List<MoviesModel>();
// SQL
CosmosResultSetIterator<MoviesModel> setIterator = movieContainer.Items.GetItemIterator<MoviesModel>(maxItemCount: 1);
while (setIterator.HasMoreResults)
{
foreach (MoviesModel item in await setIterator.FetchNextSetAsync())
{
movies.Add(item);
}
}
return movies;
}
public async Task<List<GenresModel>> GetAllGenres()
{
List<GenresModel> genres = new List<GenresModel>();
// SQL
CosmosResultSetIterator<GenresModel> setIterator = genreContainer.Items.GetItemIterator<GenresModel>(maxItemCount: 1);
while (setIterator.HasMoreResults)
{
foreach (GenresModel item in await setIterator.FetchNextSetAsync())
{
genres.Add(item);
}
}
return genres;
}
private static async Task<CosmosContainer> GetOrCreateContainerAsync(CosmosDatabase database, string containerId)
{
CosmosContainerSettings containerDefinition = new CosmosContainerSettings(id: containerId, partitionKeyPath: "/_id");
return await database.Containers.CreateContainerIfNotExistsAsync(
containerSettings: containerDefinition,
throughput: 400);
}
}
}

Related

ExecuteStoredProcedureAsync() returning different results

Long story short, a stored procedure in cosmosDB is returning 2 when executed inside the portal, and is returning 0 when called from ExecuteStoredProcedureAsync() in my c# console app.
The correct response is 2
Here's the stored procedure:
JS:
function countItems() {
var context = getContext();
var collection = context.getCollection();
var collectionLink = collection.getSelfLink();
var response = context.getResponse();
var query = "SELECT * FROM c";
var isAccepted = collection.queryDocuments(
collectionLink,
query,
function(err, documents, responseOptions) {
if (err) {
throw err;
}
response.setBody(documents.length);
}
);
}
When I run this from the azure portal, it returns the correct result: 2.
/////////////////////
Here's the C# call:
C#
private static async Task ExecuteStoredProc(string spId, CosmosContext cosmosContext)
{
using (var client = new CosmosClient(cosmosContext.Endpoint, cosmosContext.MasterKey))
{
var container = client.GetContainer(cosmosContext.DbId, cosmosContext.ContainerId);
var scripts = container.Scripts;
var pk = new PartitionKey(cosmosContext.DbId);
var result = await scripts.ExecuteStoredProcedureAsync<string>(spId, pk, null);
var message = result.Resource;
Console.WriteLine(message);
}
}
When I run this from the C# console app, it returns 0
What's the deal?
Based on my test, you may not set the PartitionKey correctly.
If you have set partition key, you need to pass the correct partition key.
static void Main(string[] args)
{
using (var client = new CosmosClient(Endpoint, Key))
{
// With Partition Key
var container = client.GetContainer("TestDB", "Demo");
var scripts = container.Scripts;
// With Partition Key
var pk = new PartitionKey("B");
var result =scripts.ExecuteStoredProcedureAsync<string>("length", pk, null).GetAwaiter().GetResult();
var message = result.Resource;
Console.WriteLine(message);
}
Console.ReadLine();
}
If there is no partition key, then you need to pass PartitionKey.None
static void Main(string[] args)
{
using (var client = new CosmosClient(Endpoint, Key))
{
// Without Partition Key
var container = client.GetContainer("ToDoList", "Items");
var scripts = container.Scripts;
//Without Partition Key
var result = scripts.ExecuteStoredProcedureAsync<string>("length", PartitionKey.None, null).GetAwaiter().GetResult();
var message = result.Resource;
Console.WriteLine(message);
}
Console.ReadLine();
}

Managing DbContext correctly in a multi thread app

I've read that I shouldn't be using same db context in a multi thread app.
so here is my scenario: I have 2 functions that I run for each years, and each function call sub functions multiple times depending on the row count.
The goal here is : If there are new records add it to the database otherwise update some fields of the record.
What I do is:
1- await StaticSettings.CACHED_DATA.FillAll(); Caches the POCO classes I need into lists. (I'm doing this because I thought that it would be faster to check the records from a cached context rather than using linq on dbcontext multiple times.
2- I run the below code. and Inside it I compare data with cached data
await GetAllData(cookie, datTypes.Select(x => x.DMT_CODE).ToList(), "me", year.Y_YEAR);
await GetAllData(cookie, datTypes.Select(x => x.DMT_CODE).ToList(), "they", year.Y_YEAR);
3- When all tasks are done I call to save changes and dispose objects..
await StaticSettings.CACHED_DATA.ProcessTasks(); //Saves
so What I would like to know is if I can rely on this code..
Trace.WriteLine($#"Caching data");
await StaticSettings.CACHED_DATA.FillAll();
Trace.WriteLine($#"Caching done");
var ts = years.Select(year => Task.Run(async () =>
{
Trace.WriteLine($"Login Start {year.Y_YEAR}");
var cookie = await MahkemeWebUtils.ForceLoginWebClient(user.MU_USERNAME, user.MU_PASSWORD);
Trace.WriteLine($"Login End {year.Y_YEAR}");
await GetAllData(cookie, datTypes.Select(x => x.DMT_CODE).ToList(), "me", year.Y_YEAR);
await GetAllData(cookie, datTypes.Select(x => x.DMT_CODE).ToList(), "they", year.Y_YEAR);
//GC.Collect();
}))
.ToList();
await Task.WhenAll(ts);
await StaticSettings.CACHED_DATA.ProcessTasks(); //Saves
UPDATE
public class CACHED_DATA
{
public AppStructMahkeme _db;
public List<DAVALAR> DavaLarToProcess { get; set; } = new List<DAVALAR>();
public List<KAZALAR> KazaList = new List<KAZALAR>();
public List<DAVA_TURLERI> DavaTurList = new List<DAVA_TURLERI>();
public List<DAVA_SCALE> DavaScaleList = new List<DAVA_SCALE>();
public List<STATUSES> StatusList = new List<STATUSES>();
public List<DAVA_KONULARI> DavakonuList = new List<DAVA_KONULARI>();
public List<AVUKATLAR> AvukatList = new List<AVUKATLAR>();
public List<EVRAK_TYPES> EvrakTypeList = new List<EVRAK_TYPES>();
public List<SENDERS> SendersList = new List<SENDERS>();
public List<ICRA_STATUS> IcraStatusList = new List<ICRA_STATUS>();
public List<DAVALAR> DavaList = new List<DAVALAR>();
public List<ISTIDA_TURLERI> IstidaTurList = new List<ISTIDA_TURLERI>();
public List<ISTIDALAR> IstidaList = new List<ISTIDALAR>();
public async Task FillAll()
{
_db = new AppStructMahkeme();
DavaLarToProcess = new List<DAVALAR>();
//_db.Configuration.LazyLoadingEnabled = false;
//KazaList = null;
KazaList = await _db.KAZALAR.ToListAsync();
DavaTurList = await _db.DAVA_TURLERI.ToListAsync();
DavaScaleList = await _db.DAVA_SCALE.ToListAsync();
StatusList = await _db.STATUSES.ToListAsync();
AvukatList = await _db.AVUKATLAR.ToListAsync();
EvrakTypeList = await _db.EVRAK_TYPES.ToListAsync();
SendersList = await _db.SENDERS.ToListAsync();
IcraStatusList = await _db.ICRA_STATUS.ToListAsync();
DavakonuList = await _db.DAVA_KONULARI.ToListAsync();
DavaList = await _db.DAVALAR.ToListAsync();
IstidaTurList = await _db.ISTIDA_TURLERI.ToListAsync();
IstidaList = await _db.ISTIDALAR.ToListAsync();
}
public async Task ProcessTasks()
{
if(DavaLarToProcess.Count!=0)
_db.DAVALAR.AddRange(DavaLarToProcess);
Trace.WriteLine("saving");
await _db.SaveChangesAsync();
Trace.WriteLine("saved");
_db.Dispose();
DavaLarToProcess = null;
KazaList = null;
DavaTurList = null;
DavaScaleList = null;
StatusList = null;
AvukatList = null;
EvrakTypeList = null;
SendersList = null;
IcraStatusList = null;
DavakonuList = null;
DavaList = null;
IstidaTurList = null;
IstidaList = null;
}
}

API is mixing up data from different devices

I have an API that has devices firing data to it at the same time or within a few milliseconds. What I am finding is that the data is getting mixed up. The data is sent every five minutes (on the clock 05, 10, 15 etc.) I have an execution filter that traps the URL data coming in so I always have a real source, then it goes to the endpoint and then onto processing. For example, there will a be random five minute period missing. When I debug step by step with the missing URL from the execution filter it works fine. By that I mean I take the URL and debug, then it inserts.
In summary, I have device id 1 and device id 2.I will get missing intervals even though, I can see the data has hit the execution filter.
I am assuming that the API is not handling these as separate transactions, but somehow mixing them up together, hence the data missing and the serial numbers appearing in the wrong place, such that data from id 1 is appearing in id 2 vice versa etc.
API End Point:
public class SomeController : ApiController
{
[HttpGet]
[ExecutionFilter]
public async Task<HttpResponseMessage> Get([FromUri] FixedDataModel fdm)
{
var reply = new HttpResponseMessage();
string url = HttpUtility.UrlDecode(HttpContext.Current.Request.QueryString.ToString());
if (url.Contains("timestamp"))
{
reply = TimeSyncValidation.TimeSync;
return reply;
}
else if (!url.Contains("timestamp"))
{
reply = await Task.Run(() => DeviceClass.DeviceApiAsync(fdm, url));
}
return reply;
}
}
Processing class:
namespace API.Services
{
public class DeviceClass
{
private static string serialNumber;
private static byte chk;
private static string channelName, channelReadingNumber, channelValue, queryString, readingDate;
private static int colonPosition, chanCountFrom, equalsPosition;
private static bool checkSumCorrect;
public static HttpResponseMessage DeviceApiAsync(FixedDataModel fdm, string urlQqueryString)
{
Guid guid = Guid.NewGuid();
//ExecutionTrackerHandler.Guid = guid;
//Remove question mark
var q = urlQqueryString;
queryString = q.Substring(0);
var items = HttpUtility.ParseQueryString(queryString);
serialNumber = items["se"];
//Store raw uri for fault finding
var rawUri = new List<RawUriModel>
{
new RawUriModel
{
UniqueId = guid,
RawUri = q,
TimeStamp = DateTime.Now
}
};
//Checksum validation
chk = Convert.ToByte(fdm.chk);
checkSumCorrect = CheckSumValidator.XorCheckSum(queryString, chk);
if (!checkSumCorrect)
{
return ValidationResponseMessage.ResponseHeaders("Checksum");
}
//Create list of items that exist in URL
var urldata = new UrlDataList
{
UrlData = queryString.Split('&').ToList(),
};
var data = new List<UriDataModel>();
//Split the URL string into its parts
foreach (var item in urldata.UrlData)
{
colonPosition = item.IndexOf(":");
chanCountFrom = colonPosition + 1;
equalsPosition = item.LastIndexOf("=");
if (colonPosition == -1)
{
channelName = item.Substring(0, equalsPosition);
channelReadingNumber = "";
channelValue = item.Substring(item.LastIndexOf("=") + 1);
}
else
{
channelName = item.Substring(0, colonPosition);
channelReadingNumber = item.Substring(chanCountFrom, equalsPosition - chanCountFrom);
channelValue = item.Substring(item.LastIndexOf("=") + 1);
if (channelName == "atime" || channelName == "adate")
{
readingDate = DateValidator.CreateDate(channelValue);
}
};
bool nullFlag = false;
if (channelValue == null)
nullFlag = true;
bool missingFlag = false;
if (channelValue == "x") {
missingFlag = true;
channelValue = "0";
}
//Add data to model ready for DB insert.
data.Add(new UriDataModel
{
uid = guid,
SerialNumber = serialNumber,
ChannelName = channelName,
ChannelReadingNumber = channelReadingNumber,
ChannelValue = channelValue.Replace(",", "."),
ReadingDate = readingDate,
TimeStamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm"),
Processed = false,
NullFlag = nullFlag,
MissingFlag = missingFlag
});
};
//Validate dates
var allDates = (from x in data where x.ChannelName.Contains("atime") || x.ChannelName.Contains("adate") select x.ChannelValue).ToList();
bool dateValidation = DateValidator.IsValid(allDates);
if (!dateValidation)
{
return ValidationResponseMessage.ResponseHeaders("Date");
};
//Validate values
var channels = Enum.GetNames(typeof(Channels)).ToList();
List<string> allChannelValues = data.Where(d => channels.Contains(d.ChannelName)).Select(d => d.ChannelValue).ToList();
bool valueValidation = ValueValidator.IsValid(allChannelValues);
if (!valueValidation)
{
return ValidationResponseMessage.ResponseHeaders("Values");
};
//Insert live data
var insertData = DataInsert<UriDataModel>.InsertData(data, "Staging.UriData");
if (!insertData)
{
return ValidationResponseMessage.ResponseHeaders("Sql");
}
var content = "\r\nSUCCESS\r\n";
var reply = new HttpResponseMessage(System.Net.HttpStatusCode.OK)
{
Content = new StringContent(content)
};
return reply;
}
}
}
TIA
You are using global variables and static method to process your data.
Change your method to non-static.
Each DeviceClass worker must update only its own isolated data then push that off back to controller.

How to get Bitcoin value for corresponding USD value in ASP.NET C#?

I want to get Bitcoin value for corresponding USD value and store it in table or variable. I got this URL from which I can get a Bitcoin value for USK amount. I searched on blockchain and I found this URL.
For example:
500usd = 0.76105818 btc
I tried:
https://blockchain.info/tobtc?currency=USD&value=500
at the end, its USD value which we want to convert in Bitcoin.
I want to get the result in the variable in C# (backend).
How can I accomplish this?
You need to just make call to server and parse the response.
var uri = String.Format("https://blockchain.info/tobtc?currency=USD&value={0}", 500);
WebClient client = new WebClient();
client.UseDefaultCredentials = true;
var data = client.DownloadString(uri);
var result = Convert.ToDouble(data);
Install-Package CoinMarketCapClient
using CoinMarketCap;
public static async Task<double> GetBitcoinInUsd(double usd){
//https://api.coinmarketcap.com/v1/ticker/bitcoin/
CoinMarketCapClient client = CoinMarketCapClient.GetInstance();
var entity = await client.GetTickerAsync("bitcoin");
return entity.PriceUsd * usd;
}
var uri = String.Format(#"https://blockchain.info/tobtc?currency=USD&value={0}",1);
WebClient client = new WebClient();
client.UseDefaultCredentials = true;
var data = client.DownloadString(uri);
var result = 1/Convert.ToDouble(data.Replace('.',',')); //you will receive 1 btc = result;
There are several APIs out there that will allow you to request the prices for a list of crypto currencies, and/or fiat currencies. The problem is that all the APIs do it in a disparate way. The follow on from that is that any one could be down at any given time, so you need to have some failure tolerance built in. I.e. the code should attempt to use one API, and if that fails, move to the next. Of course, this is further complicated by the fact that price is subjective and localised to a given country, exchange and so on. So, getting an accurate value is very difficult.
Here is example Crypto Compare client from CryptoCurrency.Net (https://github.com/MelbourneDeveloper/CryptoCurrency.Net/blob/master/src/CryptoCurrency.Net/APIClients/PriceEstimationClients/CryptoCompareClient.cs):
public class CryptoCompareClient : PriceEstimationClientBase, IPriceEstimationClient
{
public CryptoCompareClient(IRestClientFactory restClientFactory) : base(restClientFactory)
{
RESTClient = restClientFactory.CreateRESTClient(new Uri("https://min-api.cryptocompare.com"));
}
protected override Func<GetPricesArgs, Task<EstimatedPricesModel>> GetPricesFunc { get; } = async a =>
{
var retVal = new EstimatedPricesModel();
if (a.Currencies.ToList().Count == 0)
{
return retVal;
}
retVal.LastUpdate = DateTime.Now;
var symbolsPart = string.Join(",", a.Currencies.Select(c => c.Name));
var priceJson = await a.RESTClient.GetAsync<string>($"data/pricemultifull?fsyms={symbolsPart}&tsyms={a.FiatCurrency}");
var jObject = (JObject)JsonConvert.DeserializeObject(priceJson);
var rawNode = (JObject)jObject.First.First;
foreach (JProperty coinNode in rawNode.Children())
{
var fiatNode = (JProperty)coinNode.First().First;
var allProperties = fiatNode.First.Children().Cast<JProperty>().ToList();
var change24HourProperty = allProperties.FirstOrDefault(p => string.Compare(p.Name, "CHANGEPCT24HOUR", true) == 0);
var priceProperty = allProperties.FirstOrDefault(p => string.Compare(p.Name, "PRICE", true) == 0);
var price = (decimal)priceProperty.Value;
var change24Hour = (decimal)change24HourProperty.Value;
retVal.Result.Add(new CoinEstimate { CurrencySymbol = new CurrencySymbol(coinNode.Name), ChangePercentage24Hour = change24Hour, FiatEstimate = price, LastUpdate = DateTime.Now });
}
//Extreme hack. It's better to show zero than nothing at all and get the coins stuck
foreach (var currency in a.Currencies)
{
if (retVal.Result.FirstOrDefault(ce => ce.CurrencySymbol.Equals(currency)) == null)
{
retVal.Result.Add(new CoinEstimate { ChangePercentage24Hour = 0, CurrencySymbol = currency, FiatEstimate = 0, LastUpdate = DateTime.Now });
}
}
return retVal;
};
}
The PriceEstimationManager will flick through APIs until it finds one that works (https://github.com/MelbourneDeveloper/CryptoCurrency.Net/blob/master/src/CryptoCurrency.Net/APIClients/PriceEstimationClients/PriceEstimationManager.cs):
public class PriceEstimationManager
{
#region Fields
private readonly Collection<IPriceEstimationClient> _Clients = new Collection<IPriceEstimationClient>();
#endregion
#region Constructor
public PriceEstimationManager(IRestClientFactory restClientFactory)
{
foreach (var typeInfo in typeof(PriceEstimationManager).GetTypeInfo().Assembly.DefinedTypes)
{
var type = typeInfo.AsType();
if (typeInfo.ImplementedInterfaces.Contains(typeof(IPriceEstimationClient)))
{
_Clients.Add((IPriceEstimationClient)Activator.CreateInstance(type, restClientFactory));
}
}
}
#endregion
#region Public Methods
/// <summary>
/// TODO: This needs to be averaged. The two current clients give wildly different values. Need to include some Australian exchanges etc.
/// </summary>
public async Task<EstimatedPricesModel> GetPrices(IEnumerable<CurrencySymbol> currencySymbols, string fiatCurrency)
{
//Lets try a client that hasn't been used before if there is one
var client = _Clients.FirstOrDefault(c => c.AverageCallTimespan.TotalMilliseconds == 0);
var currencies = currencySymbols.ToList();
if (client != null)
{
try
{
return await client.GetPrices(currencies, fiatCurrency);
}
catch
{
//Do nothing
}
}
foreach (var client2 in _Clients.OrderBy(c => c.SuccessRate).ThenBy(c => c.AverageCallTimespan).ToList())
{
try
{
return await client2.GetPrices(currencies, fiatCurrency);
}
catch (Exception ex)
{
Logger.Log("Error Getting Prices", ex, nameof(PriceEstimationManager));
}
}
throw new Exception("Can't get prices");
}
#endregion
}
At a higher level, you can use the code like this (https://github.com/MelbourneDeveloper/CryptoCurrency.Net/blob/master/src/CryptoCurrency.Net.UnitTests/PricingTests.cs):
public async Task GetUSDBitcoinPrice()
{
var priceEstimationManager = new PriceEstimationManager(new RESTClientFactory());
var estimatedPrice = await priceEstimationManager.GetPrices(new List<CurrencySymbol> { CurrencySymbol.Bitcoin }, "USD");
Console.WriteLine($"Estimate: {estimatedPrice.Result.First().FiatEstimate}");
}
As more pricing clients are added, it will get more and more reliable.

How to change redis DB that the redis client is from redis pool

How can I changedb(redis command select), when I used the pool of redis.
I want write host and read host from different DB.
for example:
just now I only have one redis server,so the readWriteHosts = readOnlyHosts
pool = RedisDao.CreateManager(hostIp, hostIp);
public static PooledRedisClientManager CreateManager(string[] readWriteHosts, string[] readOnlyHosts)
{
return new PooledRedisClientManager(readWriteHosts, readOnlyHosts, new RedisClientManagerConfig
{
MaxWritePoolSize = 50,//
MaxReadPoolSize = 5,//
AutoStart = true,
});
}
public RedisDB ReadRedisForModel(String ID)
{
//here I want to use DB number is day%15
using (var redis = pool.GetClient())
{
RedisDB model = new RedisDB();
Dictionary<string, string> dic = redis.GetAllEntriesFromHash(keyPrefix + ID);
model.ID = ID;//Int32.Parse(ids[i]);
return model;
}
}
public void WriteRedis(RedisDB model)
{
//here I want to use DB number is (day-1)%15
using (var redis = pool.GetClient())
{
EDIT:
I find a way to set different DB,But I feel this solution is not best way.
if(redis is RedisClient)
{
long db = redis.DB;//db always = 0;
((RedisClient)redis).ChangeDB((day-1)%15);
}
Is it need to lock thread? when i am read or write to redis.
I am afraid, I got the same redis client in mutil-thread . then the redis DB is ?
Edit end
int time = DateTimeUtil.ConvertDateTimeInt(DateTime.Now);
model.ID = time + redis.Increment(incrementKey, 1) + "";//.Incr("ID");
using (var pip = redis.CreatePipeline())
{
pip.QueueCommand(r => r.AddItemToList(primaryKey, model.ID + ""));
pip.Flush();
};
};
}
I got redisClient from pool, but redisClient is not have the function of changeDB.
So anybody kowns how to set it?
for example:
//write
bool IsNeedChangeDB=true;
int WriteDBNumber=3
public static PooledRedisClientManager pool = RedisDao.CreateManager(hostIp, hostIp);
using (var redis = pool.GetClient())
{
if (redis is RedisClient && IsNeedChangeDB)
{
if (redis.Db != this.WriteDBNumber)
{
((RedisClient)redis).ChangeDb(this.WriteDBNumber);
}
else
{
Trace.WriteLine("it is a test" + redis.Host);
}
}
redis.Set<string>("key","value");
}
int ReadDBNumber=3;
//read
protected IRedisClient GetRedisClient()
{
var redis = pool.GetClient();
if (redis is RedisClient && IsNeedChangeDB)
{
if (redis.Db != this.ReadDBNumber)
((RedisClient)redis).ChangeDb(this.ReadDBNumber);
}
return redis;
}

Categories