I'm trying to consume a Rest API to display a listView with MVVM so I created this this service to consume the Rest API:
public class MachineService : IMachineService
{
public string Base_url = "https://169.254.178.79:45455/api/";
public async Task<ObservableCollection<Machine>> GetMachines()
{
string url = Base_url + "machines";
HttpClient client = new HttpClient();
HttpResponseMessage responseMessage = await client.GetAsync(url);
if (responseMessage.StatusCode == System.Net.HttpStatusCode.OK)
{
var result = await responseMessage.Content.ReadAsStringAsync();
var json = JsonConvert.DeserializeObject<ObservableCollection<Machine>>(result);
return json;
}
return null;
}
And this is the ViewModel:
public class MachineListeVM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
IMachineService _rest = DependencyService.Get<IMachineService>();
public MachineListeVM ()
{
GetMachines();
}
public async void GetMachines()
{
var result = await _rest.GetMachines();
if (result != null)
{
Machines = result;
}
}
public ObservableCollection<Machine> machines;
public ObservableCollection<Machine> Machines
{
get { return machines; }
set
{
machines = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Machines"));
}
}
}
}
the exception details while debugging:
>[monodroid-assembly] open_from_bundles: failed to load assembly Xamarin.Forms.Platform.Android.AppLinks.dll
[SurfaceFactory] [static] sSurfaceFactory = com.mediatek.view.impl.SurfaceFactoryImpl#e0b84b8
[ViewRootImpl[MainActivity]] hardware acceleration = true , fakeHwAccelerated = false, sRendererDisabled = false, forceHwAccelerated = false, sSystemRendererDisabled = false
[libMEOW] applied 1 plugins for [com.companyname.app5]:
[libMEOW] plugin 1: [libMEOW_gift.so]:
[InputTransport] Create ARC handle: 0x7a825386a0
[libMEOW_gift] etEventThreadFunc()::thread Begin. eventRefCnt: 1
[PhoneWindow] DecorView setVisiblity: visibility = 0, Parent = android.view.ViewRootImpl#8503464, this = DecorView#374f6cd[MainActivity]
[ViewRootImpl[Syncfusion License]] hardware acceleration = true , fakeHwAccelerated = false, sRendererDisabled = false, forceHwAccelerated = false, sSystemRendererDisabled = false
[InputTransport] Create ARC handle: 0x7a825ecf60
Thread started: #5
**System.NullReferenceException:** 'Object reference not set to an instance of an object.'
Note: I tried the same code in another project and it's worked fine and the list view is apeared
Related
I wrote two views in my application, and when I am changin from "Home" to "Details" it causes lag for about 0.5 - 1 sec. I understand, that the reason are my API calls in the constructor, but I added threading, and first filling in an empty properties, and after threads finish it should raise a OnPropertyChange. My "Details" viewmodel is here
public class DetailsViewModel : ObservableObject
{
private ObservableCollection<Market> _markets;
private Asset _asset;
private readonly Uri _baseUri = new Uri("https://cryptingup.com/api/assets/");
public RestClient Client { get; set; }
public ObservableCollection<Market> Markets
{
get
{
return _markets;
}
set
{
_markets = value;
OnPropertyChanged();
}
}
public Asset Asset
{
get
{
return _asset;
}
set
{
_asset = value;
OnPropertyChanged();
}
}
public DetailsViewModel(string id)
{
Client = new RestClient();
Markets = new ObservableCollection<Market>();
new Thread(() =>
{
RequestCurrencyDetails(id);
})
{
IsBackground = true
}.Start();
RequestAllMarkets(id);
new Thread(() =>
{
RequestAllMarkets(id);
})
{
IsBackground = true
}.Start();
}
private void RequestCurrencyDetails(string id)
{
Uri uri = new Uri(_baseUri, id);
var request = new RestRequest(uri.ToString());
request.AddHeader("Accept", "application/json");
request.AddHeader("Content-Type", "application/json");
var response = Client.GetAsync(request).GetAwaiter().GetResult();
var curemodel = JsonConvert.DeserializeObject<CurrencyModelCU>(response.Content);
Asset = curemodel.Asset;
}
private void RequestAllMarkets(string id)
{
string marketPath = id + "/markets";
Uri uri = new Uri(_baseUri, marketPath);
var request = new RestRequest(uri.ToString());
request.AddHeader("Accept", "application/json");
request.AddHeader("Content-Type", "application/json");
var response = Client.GetAsync(request).GetAwaiter().GetResult();
var marmodel = JsonConvert.DeserializeObject<MarketsModel>(response.Content);
ObservableCollection<Market> temp = new ObservableCollection<Market>();
for (int i = 0; i < 10; i++)
{
temp.Add(marmodel.Markets[i]);
}
Markets = temp;
}
}
I know, that I did something wrong with threading here, but I can't get the reason by myself. Thanks for answer
I highly recommend using await async instead of Thread. Also RequestAllMarkets(id); is called synchronously. this is most likley causing the "lag".
Here is a sample how to use Task.Run to "fire and forget the loading operation.
https://dotnetfiddle.net/sK3ivq
using System;
using System.Threading;
using System.Diagnostics;
using System.Threading.Tasks;
public class Program
{
public static void Main()
{
Console.WriteLine("start");
Stopwatch sw = new Stopwatch();
sw.Start();
DetailsViewModel vm = new DetailsViewModel();
Console.WriteLine($"already back from ctor -> no lag. prove: {sw.ElapsedMilliseconds}ms foo:{vm.Foo}");
// we use thread sleep -> in wpf app you don't have to wait because property change took place in VM.
Thread.Sleep(1000);
Console.WriteLine($"we waited long enough...: {sw.ElapsedMilliseconds}ms foo:{vm.Foo}");
}
public class DetailsViewModel
{
public DetailsViewModel()
{
Foo= "not initialized";
Task.Run(()=> Init());
}
public async Task Init()
{
await Task.Delay(500);
Foo = "bar";
}
public string Foo {get;set;}
}
}
I have an API call using client.GetAsync(url) within a SSIS script task but for some reason its not waiting for response from API and jumping back to the entry point for the script task which is public void Main(). Done some reading and found out that it might result in a deadlock for some reason but tried all the variations I could find to get it to work but with no luck. Something else that I don't understand is the exact same code is running on a webpage and that works perfect and waits for response from the api and continuing the flow.
Script Task entry point
The response here for payload is: ID =5, Status = WaitingForActivation, Method = "{null}", Result = "{Not yet computed}"
Here if in debug mode and moving the process back to go through the process again I noticed there 2 threads one current executing and the old one with the response I was expecting on the first call but not too sure what this means.
public void Main() {
// TODO: Add your code here
try {
PackageDetails packageInfo = new PackageDetails {
PackageNumber = 1234567891, Env = "Development", UserName = "USER"
};
var payload = API.ListHeadByPackAsync(packageInfo);
//var test = GetResponse();
Dts.TaskResult = (int) ScriptResults.Success;
} catch (Exception ex) {
System.Console.Write(ex.Message);
Dts.TaskResult = (int) ScriptResults.Failure;
}
}
API Call
public static class API {
public static async Task<PackageDetails> ListHeadByPackAsync(PackageDetails package) {
PackageDetails packageInfo = new PackageDetails();
try {
using(var client = new ApiClient(requestUrl, authToken)) {
var response = await client.GetAsync(); //-> not waiting for response
}
} catch (Exception err) {
switch (err.Message) {
//TODO:
}
}
return packageInfo;
}
}
Client
public class ApiClient: IDisposable {
private readonly TimeSpan _timeout;
private HttpClient _httpClient;
private HttpClientHandler _httpClientHandler;
private readonly string _baseUrl;
private readonly string _credentials;
//private const string MediaTypeXml = "application/csv";
public ApiClient(string baseUrl, string authToken, TimeSpan ? timeout = null) {
_baseUrl = baseUrl;
_credentials = Base64Encode(authToken);
_timeout = timeout ?? TimeSpan.FromSeconds(90);
}
public async Task < string > GetAsync() {
EnsureHttpClientCreated();
using(var response = await _httpClient.GetAsync(_baseUrl).ConfigureAwait(continueOnCapturedContext: false))
//-> after executing above line it will go straight to public void Main(), dose not wait for response
{
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
public void Dispose() {
_httpClientHandler?.Dispose();
_httpClient?.Dispose();
}
private void CreateHttpClient() {
_httpClientHandler = new HttpClientHandler {
AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip
};
_httpClient = new HttpClient(_httpClientHandler, false) {
Timeout = _timeout
};
if (!string.IsNullOrWhiteSpace(_baseUrl)) {
_httpClient.BaseAddress = new Uri(_baseUrl);
}
_httpClient.DefaultRequestHeaders.Add("Authorization", "Basic" + " " + _credentials);
}
private void EnsureHttpClientCreated() {
if (_httpClient == null) {
//ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
CreateHttpClient();
}
}
public static string Base64Encode(string token) {
var tokenBytes = System.Text.Encoding.UTF8.GetBytes(token);
return System.Convert.ToBase64String(tokenBytes);
}
}
Periodically, the application begins to update itself. There is a constant call in the logs:
[ForceDarkHelper] updateByCheckExcludeList: pkg: com.companyname.manimobile activity: crc64d14753dcc52b83b4.MainActivity#a894c70
[ForceDarkHelper] updateByCheckExcludeList: pkg: com.companyname.manimobile activity: crc64d14753dcc52b83b4.MainActivity#a894c70
[ForceDarkHelper] updateByCheckExcludeList: pkg: com.companyname.manimobile activity: crc64d14753dcc52b83b4.MainActivity#a894c70
[ForceDarkHelper] updateByCheckExcludeList: pkg: com.companyname.manimobile activity: crc64d14753dcc52b83b4.MainActivity#a894c70
When this happens, if, for example, you open the menu , it closes itself, if something is filled in, it is cleared, the page is updated. There are no timers in the code. I'm testing the app on Xiaomi Redmi. I repeat sometimes it happens sometimes it doesn't. What is it?
I do not know what the problem is, but occasionally, it happens that the application throws the fingerprint to the page. It is intermittent. Sometimes everything works fine. That is, I go through the fingerprint, the next page opens, everything is normal and a second after 5 I am again thrown to the page where you need to enter the fingerprint.
Code for the authorization page:
public authentification()
{
try
{
InitializeComponent();
bool auth = CrossSettings.Current.GetValueOrDefault("authorized", false);
if (auth == false) { CheckAuth(); }
else
{
Application.Current.MainPage = new MasterLk();
}
}
catch { }
}
async void CheckAuth()
{
try
{
var avail = await CrossFingerprint.Current.IsAvailableAsync();
if (!avail)
{
CrossSettings.Current.GetValueOrDefault("authorized", true);
Application.Current.MainPage = new MasterLk();
}
else
{
var request = new AuthenticationRequestConfiguration("NeedAuth", "-");
var result = await CrossFingerprint.Current.AuthenticateAsync(request);
if (result.Authenticated)
{
CrossSettings.Current.GetValueOrDefault("authorized", true);
Application.Current.MainPage = new MasterLk();
}
else
{
CheckAuth();
}
}
}
catch { }
}
On the page where it throws it there is a ListView with a binding:
public class OrdersViewModel : BaseViewModel
{
private Table oldLoan;
private bool isRefreshing;
private readonly string clientId;
public bool IsRefreshing
{
get
{
return isRefreshing;
}
set
{
isRefreshing = value;
OnPropertyChanged("IsRefreshing");
}
}
public ICommand RefreshCommand { get; set; }
public ObservableCollection<Table> Loans { get; set; }
public void ShowOrHideLoan(Table loan)
{
if (oldLoan == loan)
{
loan.IsExpanded = !loan.IsExpanded;
Reload(loan);
}
else
{
if (oldLoan != null)
{
oldLoan.IsExpanded = false;
Reload(oldLoan);
}
loan.IsExpanded = true;
Reload(loan);
}
oldLoan = loan;
}
private void Reload(Table loan)
{
var index = Loans.IndexOf(loan);
Loans.Remove(loan);
Loans.Insert(index, loan);
}
public async Task LoadDataAsync()
{
IsRefreshing = true;
Loans.Clear();
try
{
var loans = await ConnectAPI.GetOrdersAsync(clientId);
await Task.Delay(1000);
foreach (var item in loans)
{
Loans.Add(item);
}
}
catch (Exception exc)
{
Console.WriteLine(exc.Message);
}
finally
{
oldLoan = null;
IsRefreshing = false;
}
}
public OrdersViewModel(string clientId)
{
IsRefreshing = false;
this.clientId = clientId;
Loans = new ObservableCollection<Table>();
RefreshCommand = new Command(async () =>
{
await LoadDataAsync();
});
Task.Run(async () => await LoadDataAsync());
}
}
That is, whenever the [ForceDarkHelper] updateByCheckExcludeList: pkg: com.companyname.manimobile activity: crc64d14753dcc52b83b4 event appears.MainActivity#a894c70
Throws it to the print page...
and if you stay on this page, it is updated after a while.
MIUI 12 has made an intelligent dark theme. The system itself repaints the applications if they do not support the dark theme. Apparently this service is ForceDarkHelper. And ExcludeList is in the settings a list of applications that cannot be repainted
I am trying to write an unit test at the moment and i am having difficult with the mocking the RestSharp. The test i am trying to write is for the GetAll method.
This is the code that i am trying to test.
public class Client: IClient
{
public IRestClient RestClient { get; set; }
public IOptions<ClientSettings>Settings { get; set; }
public Client(IOptions<ClientSettings>options)
{
Settings = options;
RestClient = new RestClient(options.Value.BaseUrl);
}
public async Task<List<EventDTO>> GetAll()
{
var allEvents = await RetrieveAllEvents();
var data = TransformData(allEvents);
return data;
}
private static List<EventDTO> TransformData(IEnumerable<Event> allEvents)
{
var data = allEvents.SelectMany(con =>
con.Geometries.Select(geo =>
new EventDTO
{
Title = con.Title,
Id = con.Sources.FirstOrDefault()?.Id,
CategoriesTitle = con.Categories.FirstOrDefault()?.Title,
Closed = con.Closed,
DateTime = geo.Date
})
).ToList();
return data;
}
private async Task<IEnumerable<Event>> RetrieveAllEvents()
{
var openEvents = await RetrieveEvent(Settings.Value.GetAllOpen);
var closedEvents = await RetrieveEvent(Settings.Value.GetAllClosed);
var allEvents = openEvents.Events.Concat(closedEvents.Events);
return allEvents;
}
private async Task<RootObject> RetrieveEvent(string request)
{
var responseData = new RestRequest(request, Method.GET);
var content = await RestClient.GetAsync<RootObject>(responseData);
return content;
}
}
When the code gets to this line, it just stops working. I tried putting in a try and catch around it see what the error is but it just blows the stack.
var data = await RestClient.GetAsync<RootObject>(responseData);
I saw an example online and i tried mocking the RestSharp
restClient.Setup(c => c.ExecuteAsync<EventDTO>(
Moq.It.IsAny<IRestRequest>(),
Moq.It.IsAny<Action<IRestResponse<EventDTO>, RestRequestAsyncHandle>>()))
.Callback<IRestRequest, Action<IRestResponse<EventDTO>, RestRequestAsyncHandle>>((request, callback) =>
{
var responseMock = new Mock<IRestResponse<EventDTO>>();
responseMock.Setup(r => r.Data).Returns(new EventDTO() { });
callback(responseMock.Object, null);
});
I'm trying to create a quite simple notifications system (don't want to use SignalIR or something else). I have the following testing code:
Client side:
var source = new EventSource('/notifications.axd');
source.onopen = function () {
Console.log("Connection open");
};
source.onerror = function () {
Console.log("Connection error");
};
source.onmessage = function (event) {
Console.log("Message: " + event.data);
};
Server side:
public class NotificationMessage {
public NotificationMessage() {
Id = Guid.NewGuid().ToString();
}
public string Id { get; private set; }
}
public class NotificationsHandler : HttpTaskAsyncHandler {
private const string CONTENT_TYPE = "text/event-stream";
private sealed class NotificationItem {
public ConcurrentQueue<NotificationMessage> Messages;
public CancellationTokenSource CancellationTokenSource;
}
private static readonly ConcurrentDictionary<string, NotificationItem> _tasks =
new ConcurrentDictionary<string, NotificationItem>();
public static void Notify(string hostId, string userId, NotificationMessage message) {
NotificationItem item;
if (!_tasks.TryGetValue(string.Format("{0}|{1}", hostId, userId), out item)) {
return;
}
var tokenSource = item.CancellationTokenSource;
item.Messages.Enqueue(message);
item.CancellationTokenSource = new CancellationTokenSource();
tokenSource.Cancel();
}
public override async Task ProcessRequestAsync(HttpContext context) {
HttpRequest request = context.Request;
NotificationItem item = _tasks.GetOrAdd(
string.Format("{0}|{1}", request.Url.Host, CsSession.Data.CurrentUser.Id),
k => new NotificationItem {
Messages = new ConcurrentQueue<NotificationMessage>(),
CancellationTokenSource = new CancellationTokenSource()
}
);
HttpResponse response = context.Response;
response.ContentType = CONTENT_TYPE;
response.CacheControl = "no-cache";
response.ContentEncoding = Encoding.UTF8;
response.AppendHeader("connection", "keep-alive");
response.BufferOutput = false;
bool supportsAsyncFlush = response.SupportsAsyncFlush;
bool shouldPing = true;
while (response.IsClientConnected) {
try {
NotificationMessage message = null;
if ((!item.Messages.IsEmpty && item.Messages.TryDequeue(out message)) || shouldPing) {
response.Write(string.Format("data:{0}\n\n", message == null ? "{}" : JsonMapper.Serialize(message)));
if (supportsAsyncFlush) {
await Task.Factory.FromAsync(response.BeginFlush, response.EndFlush, null);
} else {
response.Flush();
}
}
} catch (Exception) {
break;
}
var delay = Task.Delay(15000, item.CancellationTokenSource.Token);
await delay;
shouldPing = delay.Status == TaskStatus.RanToCompletion;
}
}
}
The problem is: the above doesn't works. I have two issues:
1) When the client connects, I receive an empty packet (that's ok). Then, if I don't enqueue any messages, after awaiting the Task.Delay, the loop tries to write an empty message again, but I don't know where. The response.Write line never returns (and nothing is being received on the client).
2) If I write to the queue, for some reason the connection is dropped. If I put a breakpoint on the line after the await delay, that line is never executed (while my logic says otherwise :) ). If I cancel the token, the delay task should quit, but it seems it is aborting the whole handler??