How to make a request asynchronously in c# (xamarin)? - c#

I used to work with browser-based applications. for example Angular simple repository.
function getSomeData(params) {
...
return $http({
url: conf.urlDev + 'some/rest-url',
method: "GET",
params: params,
cache: true
}).then(getDataComplete);
function getDataComplete(response) {
return response.data;
}
}
How it will look the same in c# (XAMARIN for example)?
i try :
public class BaseClient
{
protected Http _client = null;
protected string _urlObj;
protected string _basePath;
public BaseClient ()
{
_client = new Http(new HttpClientHandler());
}
public string Path
{
set
{
_urlObj = value;
}
}
public async Task<Result<IList<T>>>getList<T>(Dictionary<string,object> parametrs = null)
{
if (parametrs != null)
{
foreach(KeyValuePair<string, object> keyValue in parametrs)
{
_urlObj = _urlObj.SetQueryParam(keyValue.Key, keyValue.Value);
}
}
var response = await _client.GetAsync(_urlObj.ToString());
if (response.IsSuccessStatusCode)
{
return new Result<IList<T>>()
{
Success = true,
Value = JsonConvert.DeserializeObject<IList<T>>(await response.Content.ReadAsStringAsync())
};
}
else
{
var error = new Result<IList<T>>()
{
Error = response.StatusCode.ToString(),
Message = response.ReasonPhrase,
Success = false
};
return error;
}
}
in my service:
public async Task<IList<News>> GetAllNewsByParams(DateTime from,
string orderBy = "-published",
DateTime to = new DateTime(),
int page = 1, int category = 0)
{
_client.Path = _config.NewsPath;
var dict = new Dictionary<string, object> {
{"from", from.ToString("s")},
{"order_by", orderBy.ToString()},
{"to", to.ToString("s")},
{"page", page.ToString()}
};
if (category != 0)
{
dict.Add("category", category.ToString());
}
var res = await _client.getList<News>(dict);
return res.Value;
}
and im ny viewmodel
foreach (var item in await _newsService.GetAllNewsByParams(
_To,
_OrderBy,
_From, _Page,
selectedTag == null ? _SeletedNewsTagId : selectedTag.Id))
{
NewsList.Add(item);
}
Is his query executed synchronously ?
How do I make it an asynchronous?

First of all I would really encourage you to use RestSharp, it really simplifies making HTTP requests and deserialise them. Add a RestSharp nuget package to your project. Here is how your code will look like using RestSharp.
public class BaseClient
{
protected IRestClient _client = null;
protected string _urlObj;
protected string _basePath;
public BaseClient()
{
_client = new RestClient();
}
public async Task<Result<IList<T>>> GetList<T>(string path, Dictionary<string, object> parametrs = null)
{
var request = new RestRequest(path, Method.GET);
if (parametrs != null)
{
foreach (var keyValue in parametrs)
{
request.AddQueryParameter(keyValue.Key, keyValue.Value);
}
}
var response = await _client.Execute<List<T>>(request);
if (response.IsSuccess)
{
return new Result<IList<T>>()
{
Success = true,
Value = response.Data
};
}
else
{
var error = new Result<IList<T>>()
{
Error = response.StatusCode.ToString(),
Message = response.StatusDescription,
Success = false
};
return error;
}
}
}
In your service
public async Task<IList<News>> GetAllNewsByParams(DateTime from,
string orderBy = "-published",
DateTime to = new DateTime(),
int page = 1, int category = 0)
{
var dict = new Dictionary<string, object> {
{"from", from.ToString("s")},
{"order_by", orderBy.ToString()},
{"to", to.ToString("s")},
{"page", page.ToString()}
};
if (category != 0)
{
dict.Add("category", category.ToString());
}
var res = await _client.GetList<News>(_config.NewsPath, dict);
return res.Value;
}
And in your viewmodel
var news = await _newsService.GetAllNewsByParams(
_To,
_OrderBy,
_From, _Page,
selectedTag == null ? _SeletedNewsTagId : selectedTag.Id);
foreach (var item in news)
{
NewsList.Add(item);
}
This will be 100% asynchronous.

Related

Producer terminating with 1 message (39bytes) still in queue or transit. Kafka

How do I solve this terminating with kafka? I have the following error: https://i.stack.imgur.com/bY2j3.png
Code:
public class KafkaHelper
{
public static async Task<bool> SendMessage(KafkaSettings settings, string topic, string key, string val)
{
var succeed = false;
var config = new ProducerConfig
{
BootstrapServers = settings.Server,
ClientId = Dns.GetHostName(),
};
using (var adminClient = new AdminClientBuilder(config).Build())
{
try
{
await adminClient.CreateTopicsAsync(new List<TopicSpecification> {
new TopicSpecification {
Name = topic,
NumPartitions = settings.NumPartitions,
ReplicationFactor = settings.ReplicationFactor } });
}
catch (CreateTopicsException e)
{
if (e.Results[0].Error.Code != ErrorCode.TopicAlreadyExists)
{
Console.WriteLine($"An error occured creating topic {topic}: {e.Results[0].Error.Reason}");
}
else
{
Console.WriteLine("Topic already exists");
}
}
}
using (var producer = new ProducerBuilder<string, string>(config).Build())
{
producer.Produce(topic, new Message<string, string>
{
Key = key,
Value = val
}, (deliveryReport) =>
{
if (deliveryReport.Error.Code != ErrorCode.NoError)
{
Console.WriteLine($"Failed to deliver message: {deliveryReport.Error.Reason}");
}
else
{
Console.WriteLine($"Produced message to: {deliveryReport.TopicPartitionOffset}");
succeed = true;
}
});
producer.Flush(TimeSpan.FromSeconds(10));
}
return await Task.FromResult(succeed);
}
}

IOS 14 request limited photo access

I'm trying to use PHPickerController and access PHAsset to get file name and file size but the PHAsset are null
var config = new PHPickerConfiguration(PHPhotoLibrary.SharedPhotoLibrary) {
Filter = PHPickerFilter.ImagesFilter,
SelectionLimit = 1
};
var picker= new PHPickerViewController(config) {
ModalPresentationStyle = UIModalPresentationStyle.Popover,
Delegate = new ImagePickerDelegate((fileSize, fileName, url) => {
})
};
ViewController.PresentViewController(picker, true, null);
public class ImagePickerDelegate : PHPickerViewControllerDelegate
{
public ImagePickerDelegate(Action<int, string, string> action)
{
Action = action;
}
public Action<int, string, string> Action { get; }
public override void DidFinishPicking(PHPickerViewController picker, PHPickerResult[] results)
{
picker.DismissViewController(true, null);
foreach (var result in results)
{
var asset = PHAsset.FetchAssets(result.AssetIdentifier, null)?.firstObject as PHAsset;
// The asset are null
var fileSize = asset.ValueForKey((NSString)"fileSize");
}
}
}
As you can see in the image the request dialog show and code are not pause on following line
var asset = PHAsset.FetchAssets(result.AssetIdentifier, null)?.firstObject as PHAsset;
and return null
You could use FetchAssetsUsingLocalIdentifiers method to get PHAsset object, then it will return value.
Sample code as follows:
public override void DidFinishPicking(PHPickerViewController picker, PHPickerResult[] results)
{
picker.DismissViewController(true, null);
foreach (var result in results)
{
var refID = result.AssetIdentifier;
string[] refIDs = new string[] { refID };
var asset = PHAsset.FetchAssetsUsingLocalIdentifiers(refIDs, null)?.firstObject as PHAsset;
// var fileSize = asset.ValueForKey((NSString)"fileSize");
}
}
Also could have a look at this native code link.

How to send a broadcast or notification from a .NET Standard 2.0 view model?

I have a view model where multiple APIs that support pagination are called as separate Tasks. The view model reside in a .NET Standard 2.0 module which is shared by Xamarin.Android and Xamarin.iOS projects. Whenever I receive a response from any API, I need to send a broadcast if it is Xamarin.Android or notification if it is being called within the iOS project. Corresponding screens will have registered for notification/broadcast so that on receiving them updated data will be fetched from DB and UI will be updated/appended with new data.
public class SyncViewModel : BaseViewModel
{
private int _totalProductPages = 1;
private int _totalCategoryPages = 1;
private int _totalInstProductPages = 1;
private int _totalUserAssignmentPages = 1;
private readonly int _pageSize = 25;
private SyncCommand _command;
private JsonSerializerSettings _settings;
public override void Execute(ICommands commands)
{
_command = (SyncCommand)commands;
_settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
MissingMemberHandling = MissingMemberHandling.Ignore
};
FetchCategories();
FetchProducts();
FetchInstitutionSubscriptionProducts();
}
private void FetchProducts()
{
Task.Run(async () =>
{
ResponseType responseType = ResponseType.PRODUCTS;
int pageNumber = 1;
string updatedTime = DBService.GetDB().FetchSyncTime(responseType);
APIResponseStatus status = APIResponseStatus.ERROR;
while (pageNumber <= _totalProductPages)
{
Response response = await CloudService.GetCloud().FetchAllProducts(pageNumber, _pageSize, updatedTime);
status = ProcessResponse(response, responseType);
pageNumber++;
//Send notification/broadcast here
}
if (status == APIResponseStatus.SUCCESS)
DBService.GetDB().InsertOrUpdateSyncTime(responseType);
});
}
private void FetchCategories()
{
Task.Run(async () =>
{
ResponseType responseType = ResponseType.CATEGORIES;
int pageNumber = 1;
string updatedTime = DBService.GetDB().FetchSyncTime(responseType);
APIResponseStatus status = APIResponseStatus.ERROR;
while(pageNumber <= _totalCategoryPages)
{
Response response = await CloudService.GetCloud().FetchAllCategories(pageNumber, _pageSize, updatedTime);
status = ProcessResponse(response, responseType);
pageNumber++;
//Send notification/broadcast here
}
if (status == APIResponseStatus.SUCCESS)
DBService.GetDB().InsertOrUpdateSyncTime(responseType);
});
}
private void FetchInstitutionSubscriptionProducts()
{
if (!App.isLoggedIn)
return;
Task.Run(async () =>
{
ResponseType responseType = ResponseType.INSTITUTION_PRODUCTS;
int pageNumber = 1;
string updatedTime = DBService.GetDB().FetchSyncTime(responseType);
APIResponseStatus status = APIResponseStatus.ERROR;
while (pageNumber <= _totalInstProductPages)
{
Response response = await CloudService.GetCloud().FetchInstitutionSubscriptionProducts(pageNumber, _pageSize, updatedTime);
status = ProcessResponse(response, responseType);
pageNumber++;
//Send notification/broadcast here
}
if (status == APIResponseStatus.SUCCESS)
DBService.GetDB().InsertOrUpdateSyncTime(responseType);
});
}
[MethodImpl(MethodImplOptions.Synchronized)]
public APIResponseStatus ProcessResponse(Response response, ResponseType type)
{
string data = "";
if (response.status == "error")
{
Error error = JsonConvert.DeserializeObject<Error>(response.data);
data = error.Message;
return APIResponseStatus.ERROR;
}
else if (response.status == "internalError")
{
data = response.data;
return APIResponseStatus.INTERNAL_ERROR;
}
else
{
data = response.data;
Pagination paginationDetails = JsonConvert.DeserializeObject<Pagination>(JObject.Parse(data)["_pagination"].ToString(), _settings);
Console.WriteLine("\n");
Console.WriteLine("SYNC_RESPONSE_LOG");
Console.WriteLine("Response Type: " + type.ToString());
Console.WriteLine("Pagination Details: " + paginationDetails);
Console.WriteLine("\n");
switch (type)
{
case ResponseType.PRODUCTS:
List<Product> products = JsonConvert.DeserializeObject<List<Product>>(JObject.Parse(data)["products"].ToString(), _settings);
DBService.GetDB().InsertOrUpdateProducts(products);
if(paginationDetails != null)
_totalProductPages = paginationDetails.TotalPages;
break;
case ResponseType.CATEGORIES:
SubCategoryList subCategoryList = JsonConvert.DeserializeObject<SubCategoryList>(data, _settings);
List<Category> category = subCategoryList.Categories.ToList();
DBService.GetDB().InsertOrUpdateCategories(category);
if (paginationDetails != null)
_totalCategoryPages = paginationDetails.TotalPages;
break;
case ResponseType.INSTITUTION_PRODUCTS:
List<Product> instProducts = JsonConvert.DeserializeObject<List<Product>>(JObject.Parse(data)["products"].ToString(), _settings);
DBService.GetDB().InsertOrUpdateProducts(instProducts);
if (paginationDetails != null)
_totalInstProductPages = paginationDetails.TotalPages;
break;
}
return APIResponseStatus.SUCCESS;
}
}
}
How to send notification in native iOS and Android platform
You can implement it by using DependencyService
in Forms , Creating the Interface
public interface ISendNotifi
{
void SendNotifi(string content); //you can set the params as you want
}
iOS Implementation
[assembly: Dependency(typeof(SendNotifiImplementation))]
namespace xxx.iOS
{
public class SendNotifiImplementation: ISendNotifi
{
public SendNotifiImplementation() { }
void SendNotifi(string content)
{
// send notification
}
}
}
Android Implementation
[assembly: Dependency(typeof(SendNotifiImplementation))]
namespace xxx.Droid
{
public class SendNotifiImplementation: ISendNotifi
{
public SendNotifiImplementation(Context context):base(context) { }
void SendNotifi(string content)
{
// send notification
}
}
}
Don't forgrt to register with the Apple and Android Push Notification Service first. And you can call the method in viewmodel and implement it in iOS and Android platform.Such as
...
while (pageNumber <= _totalInstProductPages)
{
Response response = await CloudService.GetCloud().FetchInstitutionSubscriptionProducts(pageNumber, _pageSize, updatedTime);
status = ProcessResponse(response, responseType);
pageNumber++;
//Send notification/broadcast here
DependencyService.Get<ISendNotifi>().SendNotifi("pageNumber++");
}
...

Try to call Clients.Caller but it's not working

Hello I am trying to catch messages from database and transfer to caller but when I used Clients.Caller method then it pass me error like
Using a Hub instance not created by the HubPipeline is unsupported
If I am using
var context = GlobalHost.ConnectionManager.GetHubContext<ChatHub>();
context.Clients.All.receiver(respose);
then it replying nothing. So please help me to solve out this question.
and I am using below code:
public async Task GetAllMessages(int roomid, int fromuserId, int touserId, int index, int pageSize, int broadcastid = 0)
{
ResponseModel respose = new ResponseModel();
try
{
var model = new MessageDetailModel
{
Index = index,
PageSize = pageSize,
FromUserId = fromuserId,
ToUserId = touserId,
ChatRoomId = roomid,
BroadCastId = broadcastid
};
MesaageModel totalmessages = new MesaageModel();
List<RecentMessage> messages = new List<RecentMessage>();
var usermessages = await _userService.GetAllMessages(model);
messages.AddRange(usermessages);
foreach (var message in messages)
{
model.BroadCastMessageId = model.BroadCastId == 0 ? 0 : message.BroadCastMessageId;
model.ChatRoomMessageId = model.BroadCastId == 0 ? message.ChatRoomMessageId : 0;
List<UserDetail> userofseenby = new List<UserDetail>();
var users = await _userService.GetMessageSeenBy(model);
userofseenby.AddRange(users);
message.SeenBy = userofseenby;
}
//totalmessages.Messages.AddRange(messages);
totalmessages.Messages = messages;
totalmessages.UnseenMessageCount = await _userService.GetUnSeenCount(messages.FirstOrDefault().ChatRoomId, fromuserId);
if (messages.FirstOrDefault().IsBroadCast == true)
totalmessages.Users = (List<UserDetail>)await _userService.GetUsersByBroadCastId(messages.FirstOrDefault().BroadCastId);
else if (messages.FirstOrDefault().IsGroup == true)
totalmessages.Users = (List<UserDetail>)await _userService.GetUsersByChatRoomId((int)messages.FirstOrDefault().ChatRoomId);
respose = ResponseHelper.SuccessResponse("onAllMessagesReceived", totalmessages);
Clients.Caller.receiver(respose); // Error catch here
}
catch (Exception ex)
{
respose.ActionCommand = "onAllMessagesReceived";
respose = ResponseHelper.ErrorResponse(respose.ActionCommand, ex.ToString());
Clients.Caller.receiver(respose);
}
}
And Constructors of class is below :
public ChatHub()
{
_userService = new UserBusinessLogic();
_chatterService = new ChatterBusinessLogic();
}
public ChatHub(IChatterBusinessLogic chatterService, IUserBusinessLogic userService)
{
_chatterService = chatterService;
_userService = userService;
}
And Controller constructor are like this :
public ChatterController()
{
_userService = new UserBusinessLogic();
_chatterService = new ChatterBusinessLogic();
}
public ChatterController(IChatterBusinessLogic chatterService, IUserBusinessLogic userService)
{
_chatterService = chatterService;
_userService = userService;
}

Async => avoiding deadlock and writing slow performing async code

I have this class with two public async methods, which also calls some private methods that are async.
Here's the class:
public class AggregatedDataService
{
public async Task<OrganizationAggregatedInfo> GetOrganizationAggregatedInfo(int organizationId)
{
var organization = await GetOrganization(organizationId);
var organizationContact = await GetOrganizationContact(organization.ID);
var associations = await GetOrganizationAssociations(organization.ID);
//await Task.WhenAll(organizationContact, associations);
return new OrganizationAggregatedInfo(organization.Name, organization.Address, organizationContact,
associations);
}
public async Task<SchoolAggregatedInfo> GetSchoolAggreagtedInfo(int schoolId)
{
if(schoolId < 1)
return null;
var school = await GetSchool(schoolId);
if (school == null)
{
return null;
}
var getSchoolAddressTask = GetSchoolAddress(schoolId);
var getMemberSchoolsTask = GetMemberSchools(schoolId);
var getSchoolCurriculumTask = GetSchoolCurriculum(schoolId);
var getSchoolFacilitiesTask = GetSchoolFacilities(schoolId);
var getSchoolAssociationsTask = GetSchoolAssociations(schoolId);
var getSchoolAcreditationsTask = GetSchoolAcreditations(schoolId);
var getGovernanceStructureTask = GetGovernanceStructure(schoolId);
await Task.WhenAll(getSchoolAddressTask, getMemberSchoolsTask, getSchoolCurriculumTask,
getSchoolFacilitiesTask, getSchoolAssociationsTask, getSchoolAcreditationsTask,
getGovernanceStructureTask);
var schoolAddress = getSchoolAddressTask.Result;
var memberSchools = getMemberSchoolsTask.Result;
var teacherInfo = await GetTeacherInformation(schoolAddress.SchoolID);
var curriculum = getSchoolCurriculumTask.Result;
var facilities = getSchoolFacilitiesTask.Result;
var associations = getSchoolAssociationsTask.Result;
var accreditations = getSchoolAcreditationsTask.Result;
var studentInfo = await GetStudentInformation(schoolAddress.SchoolID);
var governanceStructure = getGovernanceStructureTask.Result;
SchoolContactReadView contact = null;
if (schoolAddress != null)//TODO: consider using null propagation
{
if (schoolAddress.SchoolContact != null)
contact = SchoolContactReadView.ShowSchoolContactView(schoolAddress.SchoolContact);
}
var schoolAggregateInfo = new SchoolAggregatedInfo
{
Name = school.Name,
Address = school.Address,
MemberSchoolsCount = memberSchools.Count(),
GovernanceStructure = governanceStructure,
Accreditations = accreditations.ToList(),
Associations = associations.ToList(),
Contact = contact,
Curriculum = curriculum,
Facilities = facilities.ToList()
};
return schoolAggregateInfo;
}
private async Task<List<OrganizationAssociationReadView>> GetOrganizationAssociations(int organizationId)
{
HttpResponseMessage response = await client.GetAsync("api/organization/associations/" + organizationId);
if (response.StatusCode == HttpStatusCode.OK)
{
var contentResult = await response.Content.ReadAsAsync<List<OrganizationAssociation>>();
associations = contentResult.Select(OrganizationAssociationReadView.MapFrom).ToList();
}
else
{
_log.Error("API Error Reason: " + response.ReasonPhrase);
}
}
private async Task<OrganizationContactReadView> GetOrganizationContact(int organizationId)
{
HttpResponseMessage response = await client.GetAsync("api/organization/contact/" + organizationId);
if (response.StatusCode == HttpStatusCode.OK)
{
var contentResult = await response.Content.ReadAsAsync<OrganizationContact>();
var organizationContactReadView = OrganizationContactReadView.MapFrom(contentResult);
organizationContact = organizationContactReadView;
}
else
{
_log.Error("API Error Reason: " + response.ReasonPhrase);
}
}
private async Task<OrganizationReadView> GetOrganization(int organizationId)
{
same as the other other methods....
}
private async Task<GovernanceStructureReadView> GetGovernanceStructure(int schoolId)
{
....
}
private async Task<IEnumerable<AccreditationReadView>> GetSchoolAcreditations(int schoolId)
{
....
}
.... all other method ....
}
and I also consume this method e.g
var obj = await GetSchoolAggreagtedInfo(21).ConfigureAwait(false);
I feel like I have too much async methods and which I'm afraid may cause more harm than good.
Is there a better way of doing all this async stuff (maybe a pattern), or is the code OK?

Categories

Resources