I've been trying to convert our service stack app host and client to use MsgPack serialization. I kept getting the exception
MsgPack.InvalidMessagePackStreamException Stream Unexpectedly Ends
After some investigation I've tracked it down to DateTimeOffset field in the response object.
Client Code
using System;
namespace Client
{
using Server;
using ServiceStack.MsgPack;
class Program
{
static void Main(string[] args)
{
var client = new MsgPackServiceClient("http://localhost:1337");
var response = client.Get(new GetRequest());
Console.WriteLine("Response: [{0}] {1}", response.Timestamp, response.Result);
Console.ReadKey();
}
}
}
Server Code
using System;
namespace Server
{
using System.Reflection;
using ServiceStack;
using ServiceStack.MsgPack;
class Program
{
static void Main(string[] args)
{
var listeningOn = args.Length == 0 ? "http://localhost:1337/" : args[0];
using (var appHost = new AppHost("Test", Assembly.GetAssembly(typeof(GetService))))
{
appHost.Init()
.Start(listeningOn);
Console.WriteLine("AppHost Created at {0}, listening on {1}", DateTime.Now, listeningOn);
Console.ReadKey();
}
}
}
public class AppHost : AppHostHttpListenerBase
{
public AppHost(string serviceName, params Assembly[] assembliesWithServices)
: base(serviceName, assembliesWithServices)
{
}
public override void Configure(Funq.Container container)
{
Plugins.Add(new MsgPackFormat());
}
}
[Route("/GetRequest", Verbs = "GET")]
public class GetRequest : IReturn<GetResponse> { }
public class GetResponse
{
public string Result { get; set; }
public string Timestamp { get; set; }
//public DateTimeOffset Timestamp { get; set; }
}
public class GetService : Service
{
public GetResponse Get(GetRequest request)
{
return new GetResponse { Result = "Success", Timestamp = DateTimeOffset.UtcNow.ToString() };
//return new GetResponse { Result = "Success", Timestamp = DateTimeOffset.UtcNow };
}
}
}
The example works without DateTimeOffset but fails with the exception when its included.
The MessagePackSerializer class appears to work as expected as demonstrated by the following test.
public static void SerializeTest()
{
var response = new GetResponse { Result = "Success", Timestamp = DateTime.UtcNow };
var serializer = MessagePackSerializer.Get<GetResponse>();
var asSingleObject = serializer.PackSingleObject(response);
var fromSingleObject = serializer.UnpackSingleObject(asSingleObject);
var packStream = new MemoryStream();
serializer.Pack(packStream, response);
packStream.Position = 0;
var fromPackStream = serializer.Unpack(packStream);
packStream.Position = 0;
serializer.PackTo(Packer.Create(packStream), response);
packStream.Position = 0;
var fromPacker = serializer.Unpack(packStream);
}
In each case the response is packed\unpacked correctly with DateTimeOffset property.
I've tried custom serializer for DateTimeOffset but that fails the same way.
MsgPack.Cli 0.5.7
ServiceStack 4.0.35
Related
I need to deserialize my json data. I am using Newtonsoft.Json for json operations. I tried a lot of method to deserialize this data but i failed. Btw, I need to summarize my system for better understanding. I am posting data every minute to an API. And its response to me. So I am trying to deserialize the response.
What need I do to deserialize this json and use it like a normal c# object? I want to deserialize res variable. Thank you for your interest.
Here is the main code
var data = new SendData
{
Readtime = time,
Stationid = new Guid(_stationid),
SoftwareVersion = softwareVersion,
Period = period,
AkisHizi = akisHizi,
AkisHizi_Status = status,
AKM = akm,
AKM_Status = status,
CozunmusOksijen = cozunmusOksijen,
CozunmusOksijen_Status = status,
Debi = debi,
Debi_Status = status,
DesarjDebi = desarjDebi,
DesarjDebi_Status = status,
KOi = koi,
KOi_Status = status,
pH = ph,
pH_Status = status,
Sicaklik = sicaklik,
Sicaklik_Status = status,
Iletkenlik = iletkenlik,
Iletkenlik_Status = status
};
var res = Services.sendData(data);
MessageBox.Show(res.objects.ToString());
Here is the services model PostData method
private ResultStatus<T> PostData<T>(string url, string data) where T : new()
{
try
{
using (var webClient = new WebClient())
{
webClient.Encoding = Encoding.UTF8;
webClient.Headers.Add("AToken", JsonConvert.SerializeObject(new AToken { TicketId = this.TicketId.ToString() }));
var resp = webClient.UploadString(this.Url + url, data);
return JsonConvert.DeserializeObject<ResultStatus<T>>(resp);
}
}
catch (Exception ex)
{
return new ResultStatus<T>
{
message = ex.Message + System.Environment.NewLine + url
};
}
}
Here is the sendData method
public ResultStatus<object> sendData(SendData data)
{
var res = PostData<object>(this.stationType.ToString() + "/SendData", JsonConvert.SerializeObject(data));
return res;
}
Here is the MessageBox result (json data)
{
'Period': 1,
'ReadTime':
'2022-08-22714:01:00',
'AKM': 65.73,
'AKM_Status': 1,
'CozunmusOksijen': 0.2,
'CozunmusOksijen_Status': 1,
'Debi': 1.0,
'Debi_Status': 1,
'KOi': 25.1,
'KOi_Status': 1
}
Your JSON is probably,
{
"Period": 1,
"ReadTime": "2022-08-22T14:01:00",
"AKM": 65.73,
"AKM_Status": 1,
"CozunmusOksijen": 0.2,
"CozunmusOksijen_Status": 1,
"Debi2": 1.0,
"Debi_Status": 1,
"KOi": 25.1,
"KOi_Status": 1
}
From https://app.quicktype.io/, a C# model would be,
// <auto-generated />
//
// To parse this JSON data, add NuGet 'Newtonsoft.Json' then do:
//
// using QuickType;
//
// var thing = Thing.FromJson(jsonString);
namespace QuickType
{
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public partial class Thing
{
[JsonProperty("Period")]
public long Period { get; set; }
[JsonProperty("ReadTime")]
public DateTimeOffset ReadTime { get; set; }
[JsonProperty("AKM")]
public double Akm { get; set; }
[JsonProperty("AKM_Status")]
public long AkmStatus { get; set; }
[JsonProperty("CozunmusOksijen")]
public double CozunmusOksijen { get; set; }
[JsonProperty("CozunmusOksijen_Status")]
public long CozunmusOksijenStatus { get; set; }
[JsonProperty("Debi2")]
public long Debi2 { get; set; }
[JsonProperty("Debi_Status")]
public long DebiStatus { get; set; }
[JsonProperty("KOi")]
public double KOi { get; set; }
[JsonProperty("KOi_Status")]
public long KOiStatus { get; set; }
}
public partial class Thing
{
public static Thing FromJson(string json) => JsonConvert.DeserializeObject<Thing>(json, QuickType.Converter.Settings);
}
public static class Serialize
{
public static string ToJson(this Thing self) => JsonConvert.SerializeObject(self, QuickType.Converter.Settings);
}
internal static class Converter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters =
{
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
}
}
As per https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-how-to?pivots=dotnet-6-0#how-to-read-json-as-net-objects-deserialize
Thing? thing = JsonSerializer.Deserialize<Thing>(res);
I'm working with a REST API, that returns 2 different kinds of XML responses for the same request.
For example if I ask for a ticket using some ticket number, say 12345 to this API, it either returns:
The ticket:
Or says that it doesn't have the ticket:
(I couldn't format my XML for some reason so just pasted the screenshot.)
Note that the status code comes to be Ok in both the cases. I'm aware that it's a bad api design but we can't change anything about it.
With some help from this JSON2Csharp website, I came up with these classes to represent the response:
The Ticket class:
[XmlRoot(ElementName = "Tickets")]
public class TicketsResponse
{
public List<Ticket> Tickets { get; set; } = new List<Ticket>();
public bool HasTickets() => Tickets.Any();
}
[XmlRoot(ElementName = "Ticket")]
public class Ticket
{
[XmlElement(ElementName = "Field1", IsNullable = true)]
public string Field1 { get; set; }
public bool ShouldSerializeField1() { return Field1 != null; }
[XmlElement(ElementName = "TicketNumber")]
public int TicketNumber { get; set; }
[XmlElement(ElementName = "SomeOtherDetails")]
public SomeOtherDetails SomeOtherDetails { get; set; }
[XmlElement(ElementName = "Accessorials")]
public object Accessorials { get; set; }
}
[XmlRoot(ElementName = "SomeOtherDetails")]
public class SomeOtherDetails
{
[XmlElement(ElementName = "SomeOtherField1", IsNullable = true)]
public string SomeOtherField1 { get; set; }
public bool ShouldSerializeSomeOtherField1() { return SomeOtherField1 != null; }
}
The Error class:
[XmlRoot(ElementName = "response")]
public class ErrorResponse
{
public byte requestId { get; set; }
public byte errorCode { get; set; }
public string errorDesc { get; set; }
public ErrorResponseBody body { get; set; }
public bool HasErrors()
{
var hasTopLevelError = errorCode != 0;
var hasErrorBody = body?.errors?.Any() ?? false;
if (hasTopLevelError || hasErrorBody)
{
return true;
}
return false;
}
public string ErrorMessage()
{
var hasTopLevelError = errorCode != 0;
var hasErrorBody = body?.errors?.Any() ?? false;
if (hasTopLevelError)
{
return errorDesc;
}
else if (hasErrorBody)
{
return string.Join(", ", body.errors.Select(e => e.errorDescription));
}
return null;
}
}
[XmlRoot(ElementName = "body")]
public class ErrorResponseBody
{
[XmlElement("errors")]
public List<Error> errors { get; set; }
}
[XmlRoot(ElementName = "Error")]
public class Error
{
public byte errorId { get; set; }
public string errorDescription { get; set; }
public string errorObjectId { get; set; }
}
I then call the API using a TicketNumber that exists.
I'm using RestSharp for calling the api:
public async void SendRequestAndReceiveResponse()
{
var restClient = new RestClient("https://someapiaddress.net");
var requestXMLBody = "<request><request_id>1</request_id><operation>retrieve</operation><method /><entity>ticket</entity><user>someuser</user><password>somepassword</password><body><ticket><TicketNumber>12345</TicketNumber></ticket></body></request>";
var request = new RestRequest("somexmlwebservice!process.action", Method.POST);
request.AddParameter("xmlRequest", requestXMLBody, "text/xml", ParameterType.QueryString);
var response = await restClient.ExecuteAsync<TicketsResponse>(request);
// Do other stuffs with this response...
}
Now this works very well. Because I know my response will have the ticket and that will correctly deserialize to TicketsResponse object.
But if I call the API using a TicketNumber that doesn't exist, I simply get TicketsResponse object that has an empty list of Tickets because this time I'm getting error response. The status code comes to be OK in this case too.
What I want to do here is that I want to capture the error message from the error response. (Response of either Ticket or Error applies to bunch of other processes as well, so it's important to grab this information in a single call.)
And if I knew this ticket doesn't exist, I could simply call the API this way and capture the errors. But that's not ideal nor even a good idea:
var response = await restClient.ExecuteAsync<ErrorResponse>(request);
So I thought of combining TicketsResponse and ErrorResponse, like this:
[XmlRoot]
public class CombinedResponse
{
[XmlElement(ElementName = "Tickets")]
public TicketsResponse Data { get; set; }
[XmlElement(ElementName = "response")]
public ErrorResponse NonData { get; set; }
}
And get the response using that class:
var response = await restClient.ExecuteAsync<CombinedResponse>(request);
The Status code comes OK (when it returns either data or error message) and I get my correct response in response.Content, but the deserialization doesn't work, so my response.Data will show 2 fields Data and NonData both as null. Ideally I should have gotten either my Ticket data or Error data in response.Data.
So my question is:
Is it possible to make this work using a single class for deserialization?
I have spent too much time on this so any help is appreciated.
Also please look at my model classes and suggest if there's better way of doing things.
This is how I solved this issue.
I'm posting here so others may find it helpful.
If there's a better way of doing this, please advise.
I created a method to call the API and deserialize the response to multiple types:
public async Task<(T1, T2)> SendRequestAndReceiveResponse<T1, T2>(RestRequest request)
{
// This can be done in the constructor so we don't instantiate new client for every request.
var restClient = new RestClient("https://someapiaddress.net");
// Get response:
var response = await restClient.ExecuteAsync(request);
// Log request and response here if you want.
if (response.ErrorException != null)
{
var message = $"An error occured during this request. Check request response entries for more details.";
var newException = new Exception(message, response.ErrorException);
throw newException;
}
else
{
var xmlDeserilizer = new RestSharp.Deserializers.XmlDeserializer();
var data = xmlDeserilizer.Deserialize<T1>(response);
var nonData = xmlDeserilizer.Deserialize<T2>(response);
return (data, nonData);
}
}
And used it, by sending the types I need:
public async Task<IEnumerable<Ticket>> FetchTickets()
{
var xmlRequestBody = "<request><request_id>1</request_id><operation>retrieve</operation><method /><entity>ticket</entity><user>someuser</user><password>somepassword</password><body><ticket><TicketNumber>12345</TicketNumber></ticket></body></request>";
var request = new RestRequest("somexmlwebservice!process.action", Method.POST);
request.AddParameter("xmlRequest", xmlRequestBody, "text/xml", ParameterType.QueryString);
var apiCallResult = await SendRequestAndReceiveResponse<TicketsResponse, ErrorResponse>(request);
if (apiCallResult.Item1 != null && apiCallResult.Item1.HasTickets())
{
// Do something with the tickets...
}
else if (apiCallResult.Item2 != null && apiCallResult.Item2.HasErrors())
{
// Do something with the errors...
}
// And so on...
}
My complete solution. If you just need the answer, please take a look at the accepted answer.
This is more like a documentation of the whole process for anyone who deals with RestSharp and XML.
Request:
To form a request body that looks like this, we need few classes like below:
[XmlRoot(ElementName = "request")]
[XmlInclude(typeof(RequestBodyWithTicketNumberOnly))] // To make sure 'body' can be serialized to RequestBodyWithTicketNumberOnly
public class TicketRequestBase
{
public byte request_id { get; set; }
public string operation { get; set; }
public string method { get; set; }
public string entity { get; set; }
public string user { get; set; }
public string password { get; set; }
// body can have different shapes, so not giving it any specific class name.
public object body { get; set; }
}
[XmlRoot(ElementName = "body")]
public class RequestBodyWithTicketNumberOnly
{
public TicketWithTicketNumberOnly ticket { get; set; }
}
[XmlRoot(ElementName = "ticket")]
public class TicketWithTicketNumberOnly
{
public string TicketNumber { get; set; }
}
A method to convert C# objects into XML strings, like so:
public static string ToXml<T>(T obj)
{
var settings = new XmlWriterSettings
{
Indent = false,
OmitXmlDeclaration = true,
NewLineHandling = NewLineHandling.None,
NewLineOnAttributes = false
};
var objType = obj.GetType();
var serializer = new XmlSerializer(objType);
var emptyNamespaces = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty });
using (var stream = new StringWriter())
using (var writer = XmlWriter.Create(stream, settings))
{
serializer.Serialize(writer, obj, emptyNamespaces);
return stream.ToString();
}
}
A method to return request body as XML string:
public static string GetTicketFetchRequestBody(string ticketNumber)
{
if (ticketNumber== null) throw new ArgumentNullException(nameof(ticketNumber));
var singleTicketRequest = new TicketRequestBase()
{
request_id = 1,
operation = "retrieve",
method = string.Empty,
entity = "ticket",
user = "sauser",
password = "sapassword",
body = new RequestBodyWithTicketNumberOnly() { ticket = new TicketWithTicketNumberOnly { TicketNumber = ticketNumber} }
};
return ToXml(singleTicketRequest);
}
Response:
All the classes for the response are already documented in this question. Please take a look at them.
The top level method that gets the Ticket:
public static async Task<IEnumerable<Ticket>> FetchTickets()
{
var xmlRequestBody = GetTicketFetchRequestBody("12345");
var request = new RestRequest("somexmlwebservice!process.action", Method.POST);
request.AddParameter("xmlRequest", xmlRequestBody, "text/xml", ParameterType.QueryString);
var apiCallResult = await SendRequestAndReceiveResponse<TicketsResponse, ErrorResponse>(request);
if (apiCallResult.Item1 != null && apiCallResult.Item1.HasTickets())
{
// Do something with the tickets...
}
else if (apiCallResult.Item2 != null && apiCallResult.Item2.HasErrors())
{
// Do something with the errors...
}
// And so on...
}
The method that actually calls the API uses Proxy, logs requests and responses, and executes request using Polly retry policy:
public async Task<(T1, T2)> SendRequestAndReceiveResponse<T1, T2>(RestRequest request, bool useProxy = true)
{
// This can be done in the constructor so we don't instantiate new client for every request.
var restClient = new RestClient("https://someapiaddress.net");
if (useProxy) // This variable can even be initialized in the constructor of this RestClient
{
var proxy = GetWebProxy();
restClient.Proxy = proxy;
}
// Request Logging Part:
var requestAsJSONString = GetRequestForLogging(request, restClient);
// Log it using your logging provider.
// Response Part:
var response = await ExecuteAsyncWithPolicy(request, restClient);
// Response Logging Part:
var responseAsString = response.Content;
// Log it using your logging provider.
if (response.ErrorException != null)
{
var message = $"An error occured during this request. Check request response entries for more details.";
var newException = new Exception(message, response.ErrorException);
throw newException;
}
else
{
var xmlDeserilizer = new RestSharp.Deserializers.XmlDeserializer();
var data = xmlDeserilizer.Deserialize<T1>(response);
var nonData = xmlDeserilizer.Deserialize<T2>(response);
return (data, nonData);
}
}
Create Web proxy like so:
private static WebProxy GetWebProxy()
{
var proxyUrl = "http://proxy.companyname.com:9090/";
return new WebProxy()
{
Address = new Uri(proxyUrl),
BypassProxyOnLocal = false,
//UseDefaultCredentials = true, // This uses: Credentials = CredentialCache.DefaultCredentials
//*** These creds are given to the proxy server, not the web server ***
Credentials = CredentialCache.DefaultNetworkCredentials
//Credentials = new NetworkCredential("proxyUserName", "proxyPassword")
};
}
Create the request string with all the parameters like so:
private string GetRequestForLogging(IRestRequest request, IRestClient client)
{
var serializer = new JsonSerializer();
var requestToLog = new
{
// This will generate the actual Uri used in the request
RequestUri = client.BuildUri(request),
// Parameters are custom anonymous objects in order to have the parameter type as a nice string
// otherwise it will just show the enum value
parameters = request.Parameters.Select(parameter => new
{
name = parameter.Name,
value = parameter.Value,
type = parameter.Type.ToString()
}),
// ToString() here to have the method as a nice string otherwise it will just show the enum value
method = request.Method.ToString()
};
return serializer.Serialize(requestToLog);
}
Polly retry policy:
private AsyncPolicy<IRestResponse> GetRetryPolicy()
{
var policy = Polly.Policy.HandleResult<IRestResponse>((response) =>
{
return response.ResponseStatus != ResponseStatus.Completed;
})
//.Or<SomeKindOfCustomException>()
.RetryAsync();
return policy;
}
Call the API using the retry policy:
private async Task<IRestResponse> ExecuteAsyncWithPolicy(IRestRequest request, IRestClient restClient)
{
var policy = GetRetryPolicy();
var policyResult = await policy.ExecuteAndCaptureAsync(async () => await restClient.ExecuteAsync(request));
return (policyResult.Outcome == OutcomeType.Successful) ? policyResult.Result : new RestResponse
{
Request = request,
ErrorException = policyResult.FinalException
};
}
Hope this was helpful.
I need to get subscribe to Uniswap pair contract Sync event and get pair reserves. So here what I tried to do:
[Event("Sync")]
class PairSyncEventDTO : IEventDTO
{
[Parameter("uint112", "reserve0")]
public virtual BigInteger Reserve0 { get; set; }
[Parameter("uint112", "reserve1", 2)]
public virtual BigInteger Reserve1 { get; set; }
}
public async Task Start()
{
readonly string uniSwapFactoryAddress = "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f";
string uniSwapFactoryAbi = GetAbi(Resources.IUniswapV2Factory);
string uniSwapPairAbi = GetAbi(Resources.IUniswapV2Pair);
var web3 = new Web3("https://mainnet.infura.io/v3/fff");
Contract uniSwapFactoryContract = web3.Eth.GetContract(uniSwapFactoryAbi, uniSwapFactoryAddress);
Function uniSwapGetPairFunction = uniSwapFactoryContract.GetFunction("getPair");
string daiAddress = "0x6b175474e89094c44da98b954eedeac495271d0f";
string wethAddress = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2";
string pairContractAddress = await uniSwapGetPairFunction.CallAsync<string>(wethAddress, daiAddress);
Contract pairContract = web3.Eth.GetContract(uniSwapPairAbi, pairContractAddress);
Event pairSyncEvent = pairContract.GetEvent("Sync");
NewFilterInput pairSyncFilter = pairSyncEvent.EventABI.CreateFilterInput();
using (var client = new StreamingWebSocketClient("wss://mainnet.infura.io/ws/v3/fff"))
{
var subscription = new EthLogsObservableSubscription(client);
subscription.GetSubscriptionDataResponsesAsObservable().
Subscribe(log =>
{
try
{
EventLog<PairSyncEventDTO> decoded = Event<PairSyncEventDTO>.DecodeEvent(log);
if (decoded != null)
{
decimal reserve0 = Web3.Convert.FromWei(decoded.Event.Reserve0);
decimal reserve1 = Web3.Convert.FromWei(decoded.Event.Reserve1);
Console.WriteLine($#"Price={reserve0 / reserve1}");
}
else Console.WriteLine(#"Found not standard transfer log");
}
catch (Exception ex)
{
Console.WriteLine(#"Log Address: " + log.Address + #" is not a standard transfer log:", ex.Message);
}
});
await client.StartAsync();
await subscription.SubscribeAsync(pairSyncFilter);
}
}
string GetAbi(byte[] storedContractJson)
{
string json = Encoding.UTF8.GetString(storedContractJson);
JObject contractObject = JObject.Parse(json);
if (!contractObject.TryGetValue("abi", out JToken abiJson)) throw new KeyNotFoundException("abi object was not found in stored contract json");
return abiJson.ToString();
}
And it seems to subscribe, but never enters Subscribe lambda.
Also if I try to await subscription.SubscribeAsync(); without any filter, it also doesn't enter Subscribe lambda.
But after executing SubscribeAsync CPU is significantly loaded by the process.
What am I doing wrong? Why isn't Subscribe lambda called?
Why does it load CPU?
I don't see a major issue with your code, but as I don't have the abis, this is an example without them. The "Sync" event does not fire all the time, so that might have been the issue.
using Nethereum.ABI.FunctionEncoding.Attributes;
using Nethereum.Contracts;
using Nethereum.JsonRpc.WebSocketStreamingClient;
using Nethereum.RPC.Reactive.Eth.Subscriptions;
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using Nethereum.RPC.Eth.DTOs;
using Nethereum.RPC.Web3;
using Newtonsoft.Json.Linq;
namespace Nethereum.WSLogStreamingUniswapSample
{
class Program
{
[Event("Sync")]
class PairSyncEventDTO : IEventDTO
{
[Parameter("uint112", "reserve0")]
public virtual BigInteger Reserve0 { get; set; }
[Parameter("uint112", "reserve1", 2)]
public virtual BigInteger Reserve1 { get; set; }
}
public partial class GetPairFunction : GetPairFunctionBase { }
[Function("getPair", "address")]
public class GetPairFunctionBase : FunctionMessage
{
[Parameter("address", "tokenA", 1)]
public virtual string TokenA { get; set; }
[Parameter("address", "tokenB", 2)]
public virtual string TokenB { get; set; }
}
public static async Task Main()
{
string uniSwapFactoryAddress = "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f";
var web3 = new Web3.Web3("https://mainnet.infura.io/v3/7238211010344719ad14a89db874158c");
string daiAddress = "0x6b175474e89094c44da98b954eedeac495271d0f";
string wethAddress = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2";
var pairContractAddress = await web3.Eth.GetContractQueryHandler<GetPairFunction>()
.QueryAsync<string>(uniSwapFactoryAddress,
new GetPairFunction() {TokenA = daiAddress, TokenB = wethAddress});
var filter = web3.Eth.GetEvent<PairSyncEventDTO>(pairContractAddress).CreateFilterInput();
using (var client = new StreamingWebSocketClient("wss://mainnet.infura.io/ws/v3/7238211010344719ad14a89db874158c"))
{
var subscription = new EthLogsObservableSubscription(client);
subscription.GetSubscriptionDataResponsesAsObservable().
Subscribe(log =>
{
try
{
EventLog<PairSyncEventDTO> decoded = Event<PairSyncEventDTO>.DecodeEvent(log);
if (decoded != null)
{
decimal reserve0 = Web3.Web3.Convert.FromWei(decoded.Event.Reserve0);
decimal reserve1 = Web3.Web3.Convert.FromWei(decoded.Event.Reserve1);
Console.WriteLine($#"Price={reserve0 / reserve1}");
}
else Console.WriteLine(#"Found not standard transfer log");
}
catch (Exception ex)
{
Console.WriteLine(#"Log Address: " + log.Address + #" is not a standard transfer log:", ex.Message);
}
});
await client.StartAsync();
subscription.GetSubscribeResponseAsObservable().Subscribe(id => Console.WriteLine($"Subscribed with id: {id}"));
await subscription.SubscribeAsync(filter);
Console.ReadLine();
await subscription.UnsubscribeAsync();
}
}
}
To keep it alive in infura, you might want to ping it every so often
Example
while (true)
{
var handler = new EthBlockNumberObservableHandler(client);
handler.GetResponseAsObservable().Subscribe(x => Console.WriteLine(x.Value));
await handler.SendRequestAsync();
Thread.Sleep(30000);
}
I have cut down the code from my project significantly so it's copy and pastable but if you want to debug in a console project it'll need the nuget package: Install-Package MsgPack.Cli.
Ok, below I have commented on the line that is the issue, all I want to know is why the list is forcing duplicates within the _outgoingMessageQueue queue. Is this some kind of captured variable conundrum? Please give as much detail as possible
using MsgPack.Serialization;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Collections;
namespace QueueTest
{
public class Message
{
public string Data { get; set; }
}
public class InternalFactoryMsg<T>
{
public T Data { get; set; }
public string Group { get; set; }
public List<byte[]> ReturnIds { get; set; }
}
public class FactoryHelpers
{
public static List<byte[]> GetReturnIdentities(List<byte[]> messageBytes, byte[] identity)
{
var response = new List<byte[]>();
foreach (byte[] part in messageBytes)
{
if (part != null && part.Length > 0)
response.Add(part);
else
break;
}
// may not need this with good routing, but can avoid errors
if (messageBytes.Count > 0 && messageBytes[0] == identity)
{
messageBytes.RemoveAt(0);
Console.WriteLine("[GetReturnIdentities]: Removed identity from start, check your routing!");
}
// no return identities, send empty list as these bytes will be the
// app message and command identity couple
if (response.Count == messageBytes.Count)
return new List<byte[]>();
return response;
}
public static byte[] SerializeData<T>(T appMsg)
{
var serializer = MessagePackSerializer.Get<T>();
using (var byteStream = new MemoryStream())
{
serializer.Pack(byteStream, appMsg);
return byteStream.ToArray();
}
}
public static T DeserializeData<T>(byte[] bytes)
{
try
{
var serializer = MessagePackSerializer.Get<T>();
using (var byteStream = new MemoryStream(bytes))
{
return serializer.Unpack(byteStream);
}
}
catch (Exception ex)
{
return default(T);
}
}
}
public class Factory: FactoryHelpers
{
protected ConcurrentQueue<KeyValuePair<string, List<byte[]>>> _outgoingMessageQueue { get; set; }
public ConcurrentQueue<KeyValuePair<string, List<byte[]>>> IncomingMessageQueue { get; set; }
public Factory()
{
_outgoingMessageQueue = new ConcurrentQueue<KeyValuePair<string, List<byte[]>>>();
IncomingMessageQueue = new ConcurrentQueue<KeyValuePair<string, List<byte[]>>>();
// add fake incoming message
var byteMsg = new List<byte[]>()
{
Encoding.Unicode.GetBytes("socket1"),
Encoding.Unicode.GetBytes(""),
Encoding.Unicode.GetBytes("data")
};
var msg = new KeyValuePair<string, List<byte[]>>("socket1", byteMsg);
IncomingMessageQueue.Enqueue(msg);
}
public void AddMessage<T>(InternalFactoryMsg<T> msg)
{
var msgBytes = msg.ReturnIds ?? new List<byte[]>();
msgBytes.Add(new byte[0]);
msgBytes.Add(Factory.SerializeData<T>(msg.Data));
_outgoingMessageQueue.Enqueue(new KeyValuePair<string, List<byte[]>>("socket2", msgBytes));
}
public List<KeyValuePair<string, List<byte[]>>> GetQueue()
{
return _outgoingMessageQueue.ToList();
}
public static T GetDataFromBytes<T>(List<byte[]> msgBytes)
{
// ignoring null checks etc
return DeserializeData<T>(msgBytes.Last());
}
}
public static class MessageLayer
{
public static Factory Factory = new Factory();
public static void Init()
{
Task.Factory.StartNew(u =>
{
while(true)
{
KeyValuePair<string, List<byte[]>> msg;
if(Factory.IncomingMessageQueue.TryDequeue(out msg))
{
var data = msg.Value.Last();
var returnIds = Factory.GetReturnIdentities(msg.Value, Encoding.Unicode.GetBytes(msg.Key));
IncomingCommands.HandleDataCommand(data, "test grp", returnIds);
}
// nice and slow for simulation
Thread.Sleep(400);
}
}, TaskCreationOptions.LongRunning);
}
public static void SendMessage(Message msg, string group, List<byte[]> returnIds)
{
var intMsg = new InternalFactoryMsg<Message>();
intMsg.Data = msg;
intMsg.Group = group;
intMsg.ReturnIds = returnIds;
Factory.AddMessage<Message>(intMsg);
}
}
public static class DataAccessor
{
public static List<Message> GetData(byte[] data)
{
return new List<Message>()
{
new Message() { Data = "magic" },
new Message() { Data = "data!" }
};
}
}
public static class IncomingCommands
{
public static void HandleDataCommand(byte[] data, string group, List<byte[]> returnIds)
{
List<Message> result;
// does big switch, gets data response
result = DataAccessor.GetData(data);
foreach (Message msg in result)
{
var local = msg;
var fix = new List<byte[]>(returnIds);
// THIS IS THE ISSUE
// comment out the following line and uncomment the one below to fix it
// but... why??? :O !!!
MessageLayer.SendMessage(local, group, returnIds);
//MessageLayer.SendMessage(local, group, fix);
}
// check the queue
Console.WriteLine("---------------------------");
Console.WriteLine("::Checking queue contents::");
var msgs = MessageLayer.Factory.GetQueue();
foreach(var m in msgs)
{
var check = Factory.GetDataFromBytes<Message>(m.Value);
Console.WriteLine("data -> " + check.Data);
}
}
}
public class Program
{
static void Main(string[] args)
{
MessageLayer.Init();
while(true)
{
Thread.Sleep(400);
}
}
}
}
If you can't work it out, please up vote so it gets attention. Thanks
The reason was
var msgBytes = msg.ReturnIds ?? new List<byte[]>();
was causing a variable capture, meaning subsequent use forced duplicate references to the same object
HI I am getting this error when compiling in the beginning at SMS.Rest.Client. We are trying to use the Celltrust SMS server to facilitate messaging in our app. -------------------------END____________-
using Lda.Sms.Client.Events;
using Lda.Sms.Models;
using Caliburn.Micro;
using Newtonsoft.Json;
using RestSharp;
using RestSharp.Deserializers;
using RestSharp.Serializers;
using System;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
------------------------------
namespace Lda.Sms.Client.Services {
public class SmsRestClient : ISmsRestClient, IHandle<SuccessfulLoginEvent> {
private RestClient client = new RestClient();
string userId = null;
public SmsRestClient(IEventAggregator eventAggregator) {
eventAggregator.Subscribe(this);
var address = ConfigurationManager.AppSettings["serverAddress"];
client.BaseUrl = address + "api";
client.AddHandler("application/json", new JsonNetSerializer());
}
public void Handle(SuccessfulLoginEvent message) {
if (client.CookieContainer == null) {
client.CookieContainer = new System.Net.CookieContainer();
}
client.CookieContainer.Add(message.AuthenticationCookie);
userId = message.UserId;
}
---------------------------------------------
#region Asynchronous Api
public async Task<Settings> GetSettingsAsync() {
return (await client.ExecuteTaskAsync<Settings>(CreateGetSettingsRequest())).Data;
}
public async Task<User> GetUserAsync() {
return (await client.ExecuteTaskAsync<User>(CreateGetUserRequest())).Data;
}
public async Task UpdateSettingsAsync(Settings settings) {
var response = await client.ExecuteTaskAsync(CreateUpdateSettingsRequest(settings));
return;
}
public async Task SendMessageAsync(int contactId, string body) {
var response = await client.ExecuteTaskAsync(CreateSendMessageRequest(contactId, body));
return;
}
#endregion Asynchronous Api
#region Synchronous Api
public Settings GetSettings() {
return client.Execute<Settings>(CreateGetSettingsRequest()).Data;
}
public User GetUser() {
return client.Execute<User>(CreateGetUserRequest()).Data;
}
public void SendMessage(int contactId, string body) {
client.Execute(CreateSendMessageRequest(contactId, body));
}
public void UpdateSettings(Settings settings) {
client.Execute(CreateUpdateSettingsRequest(settings));
}
#endregion Synchronous Api
#region Rest Requests
private RestRequest CreateGetSettingsRequest() {
return new RestRequest {
Resource = "Settings",
Method = Method.GET
};
}
private RestRequest CreateGetUserRequest() {
var request = new RestRequest {
Resource = "User/{userId}",
Method = Method.GET
};
request.AddUrlSegment("userId", userId);
return request;
}
private RestRequest CreateUpdateSettingsRequest(Settings settings) {
var request = new RestRequest {
JsonSerializer = new JsonNetSerializer(),
Resource = "Settings",
Method = Method.POST,
RequestFormat = DataFormat.Json
};
request.AddBody(settings);
return request;
}
private RestRequest CreateSendMessageRequest(int contactId, string body) {
var request = new RestRequest {
Resource = "Sms",
Method = Method.POST,
};
request.AddParameter("contactId", contactId, ParameterType.QueryString);
request.AddParameter("body", body, ParameterType.QueryString);
return request;
}
#endregion Rest Requests
}
internal class JsonNetSerializer : IDeserializer, ISerializer {
private Newtonsoft.Json.JsonSerializer _serializer;
public JsonNetSerializer() {
ContentType = "application/json";
_serializer = new Newtonsoft.Json.JsonSerializer {
MissingMemberHandling = MissingMemberHandling.Ignore,
NullValueHandling = NullValueHandling.Include,
DefaultValueHandling = DefaultValueHandling.Include,
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
};
}
}
// adding more details here
// adding more and more and more details more and details
// Asher barah Sasone viSimcha sasone vi Simchas Chason Vi Kalah
// mnnm
public string ContentType { get; set; }
public string DateFormat { get; set; }
public string Namespace { get; set; }
public string RootElement { get; set; }
public T Deserialize<T>(IRestResponse response) {
using (var textReader = new StreamReader(new MemoryStream(response.RawBytes)))
using (var jsonTextReader = new JsonTextReader(textReader))
return _serializer.Deserialize<T>(jsonTextReader);
}
// adding more details here
// adding more and more and more details more and details
// Asher barah Sasone viSimcha sasone vi Simchas Chason Vi Kalah
// mnnm
// details detail;s and detail;s
public string Serialize(object obj) {
using (var textWriter = new StringWriter())
using (var jsonTextWriter = new JsonTextWriter(textWriter)) {
jsonTextWriter.Formatting = Formatting.Indented;
jsonTextWriter.QuoteChar = '"';
_serializer.Serialize(jsonTextWriter, obj);
return textWriter.ToString();
}
}
}
}
lplp
this code is not successfully building...
*** this issue is that this code is not building. *****
You know you can do this right : ?
Uri uri = new Uri(whateverstring);