I have what I consider a basic web application MVC, EF6. The user has a dashboard that presents a table that contains data from two different database systems. MSSQL and Informix (Using IBM.Data.Informix).
Over time the IIS process just keeps eating away at the ram. I grabbed dotMemory to help me try to locate it but now trying to figure out how to read this data.
I left the web page open and every 10 seconds there is an Ajax call that returns the new data.
The 4th snapshot was taken a few hours after the 3rd one.
The total doesn't match the numbers below but something isn't as it should be.
The picture below seems to tell me I'm only using at most 10mb with my application.
I'm also still looking into the heap but it doesn't seem like this is where the big chunk is.
I'm still digging up videos and guides to help me figure out locating this issue. I'm using a lot of the built in framework and I really don't see an issue with the code I'm using unless there is a bug somewhere or I really am missing something I shouldn't be doing within the code.
DatabaseManager
public class DatabaseManager : IDisposable
{
private bool disposed = false;
private SafeHandle handle = new SafeFileHandle(IntPtr.Zero, true);
private PatientCheckinEntities db { get; set; }
private IfxConnection conn { get; set; }
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public DatabaseManager()
{
string ifxString = System.Configuration.ConfigurationManager.ConnectionStrings["ifx"].ConnectionString;
conn = new IfxConnection(ifxString);
db = new PatientCheckinEntities();
}
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
handle.Dispose();
IfxClose();
conn.Dispose();
db.Dispose();
}
disposed = true;
}
private void IfxClose()
{
if (conn.State == System.Data.ConnectionState.Open)
{
conn.Close();
}
}
private void IfxOpen()
{
if (conn.State == System.Data.ConnectionState.Closed)
{
conn.Open();
}
}
public ProviderModel GetProviderByResourceID(string id)
{
ProviderModel provider = new ProviderModel();
using (IfxDataAdapter ida = new IfxDataAdapter())
{
ida.SelectCommand = new IfxCommand("SELECT description FROM sch_resource WHERE resource_id = ? FOR READ ONLY", conn);
IfxParameter ifp1 = new IfxParameter("resource_id", IfxType.Char, 4);
ifp1.Value = id;
ida.SelectCommand.Parameters.Add(ifp1);
IfxOpen();
object obj = ida.SelectCommand.ExecuteScalar();
IfxClose();
if (obj != null)
{
string name = obj.ToString();
provider.ResourceID = id.ToString();
string[] split = name.Split(',');
if (split.Count() >= 2)
{
provider.LastName = split[0].Trim();
provider.FirstName = split[1].Trim();
}
else
{
provider.LastName = name.Trim();
}
ProviderPreference pp = db.ProviderPreferences.Where(x => x.ProviderID.Equals(id, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
int globalWait = Convert.ToInt32(GetConfigurationValue(ConfigurationSetting.WaitThreshold));
if (pp != null)
{
provider.Preferences.DisplayName = pp.DisplayName;
provider.Preferences.WaitThreshold = pp.WaitThreshold.HasValue ? pp.WaitThreshold.Value : globalWait;
}
else
{
provider.Preferences.WaitThreshold = globalWait;
}
}
}
return provider;
}
public List<PatientModel> GetCheckedInPatients(List<string> providers)
{
List<PatientModel> patients = new List<PatientModel>();
foreach (string provider in providers)
{
List<PatientModel> pats = db.PatientAppointments
.Where(x => provider.Contains(x.ProviderResourceID)
&& DbFunctions.TruncateTime(x.SelfCheckInDateTime) == DbFunctions.TruncateTime(DateTime.Now))
.Select(x => new PatientModel()
{
Appointment = new AppointmentModel()
{
ID = x.AppointmentID,
DateTime = x.AppointmentDateTime,
ArrivalTime = x.ExternalArrivedDateTime
},
FirstName = x.FirstName,
LastName = x.LastName,
SelfCheckIn = x.SelfCheckInDateTime,
Provider = new ProviderModel()
{
ResourceID = x.ProviderResourceID
}
}).ToList();
patients.AddRange(pats.Select(x => { x.Provider = GetProviderByResourceID(x.Provider.ResourceID); return x; }));
}
using (IfxDataAdapter ida = new IfxDataAdapter())
{
ida.SelectCommand = new IfxCommand("SELECT arrival_time::char(5) as arrival_time FROM sch_app_slot WHERE appointment_key = ? FOR READ ONLY", conn);
IfxOpen();
foreach (PatientModel patient in patients)
{
ida.SelectCommand.Parameters.Clear();
IfxParameter ifx1 = new IfxParameter("appointment_key", IfxType.Serial);
ifx1.Value = patient.Appointment.ID;
ida.SelectCommand.Parameters.Add(ifx1);
using (IfxDataReader dr = ida.SelectCommand.ExecuteReader())
{
while (dr.Read())
{
if (dr.HasRows)
{
string arrival = dr["arrival_time"].ToString();
if (!string.IsNullOrWhiteSpace(arrival) && !patient.Appointment.ArrivalTime.HasValue)
{
PatientAppointment pa = new PatientAppointment();
pa.AppointmentID = patient.Appointment.ID;
pa.AppointmentDateTime = patient.Appointment.DateTime;
pa.FirstName = patient.FirstName;
pa.LastName = patient.LastName;
string dt = string.Format("{0} {1}", patient.Appointment.DateTime.ToString("yyyy-MM-dd"), arrival);
pa.ExternalArrivedDateTime = DateTime.ParseExact(dt, "yyyy-MM-dd HH:mm", CultureInfo.InvariantCulture);
patient.Appointment.ArrivalTime = pa.ExternalArrivedDateTime;
pa.ProviderResourceID = patient.Provider.ResourceID;
pa.SelfCheckInDateTime = patient.SelfCheckIn;
db.PatientAppointments.Attach(pa);
db.Entry(pa).State = EntityState.Modified;
db.SaveChanges();
}
}
}
}
}
IfxClose();
}
patients = patients.Select(x => { x.Appointment.WaitedMinutes = (int)Math.Round(((TimeSpan)x.Appointment.ArrivalTime.Value.Trim(TimeSpan.TicksPerMinute).Subtract(x.SelfCheckIn.Trim(TimeSpan.TicksPerMinute))).TotalMinutes); return x; }).ToList();
List<PatientModel> sorted = patients.Where(x => !x.Appointment.ArrivalTime.HasValue).OrderBy(x => x.SelfCheckIn).ThenBy(x => x.Provider.ResourceID).ToList();
sorted.AddRange(patients.Where(x => x.Appointment.ArrivalTime.HasValue).OrderBy(x => x.Appointment.DateTime).ThenBy(x => x.Provider.ResourceID));
return sorted;
}
private string GetConfigurationValue(string id)
{
return db.Configurations.Where(x => x.ID.Equals(id)).Select(x => x.Value).FirstOrDefault();
}
}
Controller
[HttpPost]
[Authorize]
public ActionResult GetCheckedIn(List<string> provider)
{
DashboardViewModel vm = new DashboardViewModel();
try
{
if (provider.Count > 0)
{
using (DatabaseManager db = new DatabaseManager())
{
vm.Patients = db.GetCheckedInPatients(provider);
}
}
}
catch (Exception ex)
{
//todo
}
return PartialView("~/Views/Dashboard/_InnerTable.cshtml", vm);
}
Your app consumes a lot of native memory, not .NET memory. Look at .NET memory consumption it's about 12Mb and does not grow intensively. It seems that you do not call Dispose method on some objects which uses native memory, e.g. database connection objects or something like that. Check a number of such objects if you does not release them the number will grow constantly.
I see you are using System.xml.Schema.... depending on the version, will create an unmanaged memory leak for each and every instance of about 80K. So, every 12 uses and you have a one meg memory leak in un-managed memory.
Instead of creating a new one each time, cache it if possible.
Related
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.
I'm struggling to find an answer to a problem I've been experiencing with C#'s popular Postgres library, Npgsql. I'm not sure if it's wholly a problem with Npgsql or not, though I suspect it is because my code is very straight forward. The issue I'm seeing is: when I call an async method on Npgsql sometimes, not all the time, the thread becomes locked. This issue occurs randomly from what I can tell. The result, being that I'm running in a Microsoft Orleans environment (which may be relevant to finding the solution), is that the thread locks indefinitely, thus making one of Orleans' worker threads unable to process work. As I make more Npgsql calls, these locked threads stack up and eventually the Orleans system is choked by thread exhaustion.
So I'm really at a loss for what the problem could be, but because the locking always happens in the same method, and because it appears to be happening in some subroutine of Npgsql, I think it's fair to investigate Npgsql further.
Here's my code which is used in an Orleans storage provider (special class which handles the system's persistence layer.)
var sql = $"SELECT * FROM objects WHERE id = #id";
using (var connection = new NpgsqlConnection(connectionString))
using (var cmd = new NpgsqlCommand(sql, connection))
{
try
{
await connection.OpenAsync();
cmd.Parameters.AddWithValue("id", id);
using(var reader = await connection.ExecuteReaderAsync(cmd))
{
if (reader.HasRows)
{
var objects = await ProtobufSQL.DataReaderToType(modelType, reader);
var data = objects[0];
state.Data = data;
}
}
catch (Exception e)
{
Log.Error(1, e.Message, e);
}
}
This is the source for the ProtobufSQL class:
public class ProtobufSQL
{
public static List<Tuple<string, object>> FlattenToSQLColumns(IMessage message, MessageDescriptor descriptor, string prefix = null)
{
var fields = descriptor.Fields.InDeclarationOrder();
var columns = new List<Tuple<string, object>>();
for (var i = 0; i < fields.Count; i++)
{
var field = fields[i];
var columnName = field.Name.ToLower();
if (field.Name == "id")
{
ByteString bytes = (ByteString)field.Accessor.GetValue(message);
var uuid = new Guid(bytes.ToByteArray());
columns.Add(new Tuple<string, object>("id", uuid));
}
else if (field.FieldType == FieldType.Message)
{
var embeddedDescriptor = field.MessageType;
var embeddedMessage = field.Accessor.GetValue(message);
if (field.IsRepeated)
{
throw new Exception("Repeated complex types are not supported, create a foreign key reference in a new object instead.");
}
else
{
columns.AddRange(FlattenToSQLColumns((IMessage)embeddedMessage, embeddedDescriptor, $"{columnName}."));
}
}
else if (field.FieldType == FieldType.Group)
{
throw new Exception("Groups are not supported by ProtobufSQL.");
}
else
{
var columnValue = field.Accessor.GetValue(message);
var key = prefix + columnName;
if (field.IsRepeated)
{
var enumerableColumnValue = columnValue as IEnumerable;
Type listTypeOf = enumerableColumnValue.GetType().GetGenericArguments()[0];
Type listType = typeof(List<>).MakeGenericType(listTypeOf);
dynamic valueList = Activator.CreateInstance(listType);
foreach (var item in enumerableColumnValue)
{
valueList.Add((dynamic)item);
}
columns.Add(new Tuple<string, object>(key, valueList.ToArray()));
}
else
{
columns.Add(new Tuple<string, object>(key, columnValue));
}
}
}
return columns;
}
public static async Task<IMessage[]> DataReaderToType(Type type, DbDataReader reader)
{
var descriptor = (MessageDescriptor)type.GetProperty("Descriptor").GetValue(null);
IList<IMessage> objects = new List<IMessage>();
while (await reader.ReadAsync())
{
var obj = Activator.CreateInstance(type);
TraverseDbRow(reader, descriptor, obj);
objects.Add((IMessage)obj);
}
return objects.ToArray();
}
private static void TraverseDbRow(DbDataReader reader, MessageDescriptor descriptor, object obj, string prefix = null)
{
var fields = descriptor.Fields.InFieldNumberOrder();
for (var i = 0; i < fields.Count; i++)
{
var field = fields[i];
if (field.FieldType == FieldType.Message)
{
if (field.IsRepeated)
{
// Repeated embedded types will be broken out into a separate table,
// so there's no need to handle them here.
}
else if (field.IsMap)
{
throw new Exception("Maps are not yet supported by ProtobufSQL.");
}
else
{
var embeddedDescriptor = field.MessageType;
var embeddedObj = Activator.CreateInstance(embeddedDescriptor.ClrType);
TraverseDbRow(reader, embeddedDescriptor, embeddedObj, $"{prefix}{field.Name}.");
}
}
else if (field.FieldType == FieldType.Group)
{
throw new Exception("Groups are not supported by ProtobufSQL.");
}
else
{
var columnName = prefix + field.Name;
try
{
var columnValue = reader[columnName];
var propertyInfo = obj.GetType().GetProperty(field.Name, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
if (field.Name == "id")
{
var guid = (Guid)columnValue;
ByteString bytes = ByteString.CopyFrom(guid.ToByteArray());
propertyInfo.SetValue(obj, bytes);
}
else if (field.IsRepeated)
{
var repeated = propertyInfo.GetValue(obj);
var addRange = repeated.GetType().GetMethod("AddRange");
addRange.Invoke(repeated, new object[] { columnValue });
}
else if (field.IsMap)
{
throw new Exception("Maps are not yet supported by ProtobufSQL.");
}
else
{
propertyInfo.SetValue(obj, Convert.ChangeType(columnValue, propertyInfo.PropertyType));
}
}
catch (IndexOutOfRangeException e)
{
// columnName was not present in the response
}
}
}
}
}
I also have this screenshot of the thread's stack when it got locked:
I'm really unsure what to make of all this. Hopefully someone is out there with a bit of knowledge that can help me proceed! Thanks.
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.
Dears
Please help me with restoring delayed (and persisted) workflows.
I'm trying to check on self-hosted workflow store is there any instance that was delayed and can be resumed. For testing purposes I've created dummy activity that is delayed and it persists on delay.
generally resume process looks like:
get WF definition
configure sql instance store
call WaitForEvents
is there event with HasRunnableWorkflowEvent.Value name and if it is
create WorkflowApplication object and execute LoadRunnableInstance method
it works fine if store is created|initialized, WaitForEvents is called, store is closed. In such case store reads all available workflows from persisted DB and throws timeout exception if there is no workflows available to resume.
The problem happens if store is created and loop is started only for WaitForEvents (the same thing happens with BeginWaitForEvents). In such case it reads all available workflows from DB (with proper IDs) but then instead of timeout exception it is going to read one more instance (I know exactly how many workflows is there ready to be resumed because using separate test database). But fails to read and throws InstanceNotReadyException. In catch I'm checking workflowApplication.Id, but it was not saved with my test before.
I've tried to run on new (empty) persistent database and result is the same :(
This code fails:
using (var storeWrapper = new StoreWrapper(wf, connStr))
for (int q = 0; q < 5; q++)
{
var id = Resume(storeWrapper); // InstanceNotReadyException here when all activities is resumed
But this one works as expected:
for (int q = 0; q < 5; q++)
using (var storeWrapper = new StoreWrapper(wf, connStr))
{
var id = Resume(storeWrapper); // timeout exception here or beginWaitForEvents continues to wait
What is a best solution in such case? Add empty catch for InstanceNotReadyException and ignore it?
Here are my tests
const int delayTime = 15;
string connStr = "Server=db;Database=AppFabricDb_Test;Integrated Security=True;";
[TestMethod]
public void PersistOneOnIdleAndResume()
{
var wf = GetDelayActivity();
using (var storeWrapper = new StoreWrapper(wf, connStr))
{
var id = CreateAndRun(storeWrapper);
Trace.WriteLine(string.Format("done {0}", id));
}
using (var storeWrapper = new StoreWrapper(wf, connStr))
for (int q = 0; q < 5; q++)
{
var id = Resume(storeWrapper);
Trace.WriteLine(string.Format("resumed {0}", id));
}
}
Activity GetDelayActivity(string addName = "")
{
var name = new Variable<string>(string.Format("incr{0}", addName));
Activity wf = new Sequence
{
DisplayName = "testDelayActivity",
Variables = { name, new Variable<string>("CustomDataContext") },
Activities =
{
new WriteLine
{
Text = string.Format("before delay {0}", delayTime)
},
new Delay
{
Duration = new InArgument<TimeSpan>(new TimeSpan(0, 0, delayTime))
},
new WriteLine
{
Text = "after delay"
}
}
};
return wf;
}
Guid CreateAndRun(StoreWrapper sw)
{
var idleEvent = new AutoResetEvent(false);
var wfApp = sw.GetApplication();
wfApp.Idle = e => idleEvent.Set();
wfApp.Aborted = e => idleEvent.Set();
wfApp.Completed = e => idleEvent.Set();
wfApp.Run();
idleEvent.WaitOne(40 * 1000);
var res = wfApp.Id;
wfApp.Unload();
return res;
}
Guid Resume(StoreWrapper sw)
{
var res = Guid.Empty;
var events = sw.GetStore().WaitForEvents(sw.Handle, new TimeSpan(0, 0, delayTime));
if (events.Any(e => e.Equals(HasRunnableWorkflowEvent.Value)))
{
var idleEvent = new AutoResetEvent(false);
var obj = sw.GetApplication();
try
{
obj.LoadRunnableInstance(); //instancenotready here if the same store has read all instances from DB and no delayed left
obj.Idle = e => idleEvent.Set();
obj.Completed = e => idleEvent.Set();
obj.Run();
idleEvent.WaitOne(40 * 1000);
res = obj.Id;
obj.Unload();
}
catch (InstanceNotReadyException)
{
Trace.TraceError("failed to resume {0} {1} {2}", obj.Id
, obj.DefinitionIdentity == null ? null : obj.DefinitionIdentity.Name
, obj.DefinitionIdentity == null ? null : obj.DefinitionIdentity.Version);
foreach (var e in events)
{
Trace.TraceWarning("event {0}", e.Name);
}
throw;
}
}
return res;
}
Here is store wrapper definition I'm using for test:
public class StoreWrapper : IDisposable
{
Activity WfDefinition { get; set; }
public static readonly XName WorkflowHostTypePropertyName = XNamespace.Get("urn:schemas-microsoft-com:System.Activities/4.0/properties").GetName("WorkflowHostType");
public StoreWrapper(Activity wfDefinition, string connectionStr)
{
_store = new SqlWorkflowInstanceStore(connectionStr);
HostTypeName = XName.Get(wfDefinition.DisplayName, "ttt.workflow");
WfDefinition = wfDefinition;
}
SqlWorkflowInstanceStore _store;
public SqlWorkflowInstanceStore GetStore()
{
if (Handle == null)
{
InitStore(_store, WfDefinition);
Handle = _store.CreateInstanceHandle();
var view = _store.Execute(Handle, new CreateWorkflowOwnerCommand
{
InstanceOwnerMetadata = { { WorkflowHostTypePropertyName, new InstanceValue(HostTypeName) } }
}, TimeSpan.FromSeconds(30));
_store.DefaultInstanceOwner = view.InstanceOwner;
//Trace.WriteLine(string.Format("{0} owns {1}", view.InstanceOwner.InstanceOwnerId, HostTypeName));
}
return _store;
}
protected virtual void InitStore(SqlWorkflowInstanceStore store, Activity wfDefinition)
{
}
public InstanceHandle Handle { get; protected set; }
XName HostTypeName { get; set; }
public void Dispose()
{
if (Handle != null)
{
var deleteOwner = new DeleteWorkflowOwnerCommand();
//Trace.WriteLine(string.Format("{0} frees {1}", Store.DefaultInstanceOwner.InstanceOwnerId, HostTypeName));
_store.Execute(Handle, deleteOwner, TimeSpan.FromSeconds(30));
Handle.Free();
Handle = null;
_store = null;
}
}
public WorkflowApplication GetApplication()
{
var wfApp = new WorkflowApplication(WfDefinition);
wfApp.InstanceStore = GetStore();
wfApp.PersistableIdle = e => PersistableIdleAction.Persist;
Dictionary<XName, object> wfScope = new Dictionary<XName, object> { { WorkflowHostTypePropertyName, HostTypeName } };
wfApp.AddInitialInstanceValues(wfScope);
return wfApp;
}
}
I'm not workflow foundation expert so my answer is based on the official examples from Microsoft. The first one is WF4 host resumes delayed workflow (CSWF4LongRunningHost) and the second is Microsoft.Samples.AbsoluteDelay. In both samples you will find a code similar to yours i.e.:
try
{
wfApp.LoadRunnableInstance();
...
}
catch (InstanceNotReadyException)
{
//Some logging
}
Taking this into account the answer is that you are right and the empty catch for InstanceNotReadyException is a good solution.
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;
}