I am trying to make offline sync to table from azure mobile service. My Xamarin Form version is 1.4.2.6359.
I try to test my code in OnCreate method of MainActivity. All preparation steps such as MobileServiceClient initialization, MobileServiceSQLiteStore initialization, SyncTable creation, etc are ok.
When I try to call PullAsync, I am getting NullReferenceException. I capture the package using Package Capture App from mobile. The request goes to Azure Mobile service and it returns the correct json data successfully.
When I try the same code in Xamarin Android project (not Xamarin Form), it is working fine.
To reproduce the issue.
Just create Xamarin Form (Portable) project and use my code.
My Code
private async Task Test() {
const string applicationURL = #"https://xxxx.azure-mobile.net/";
const string applicationKey = #"xxxx";
CurrentPlatform.Init();
var client = new MobileServiceClient(applicationURL, applicationKey);
string path = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal), "store.db");
if (!File.Exists(path)) {
File.Create(path).Dispose();
}
var store = new MobileServiceSQLiteStore(path);
store.DefineTable<Product>();
await client.SyncContext.InitializeAsync(store);
var productTable = client.GetSyncTable<Product>();
try {
await client.SyncContext.PushAsync();
await productTable.PullAsync("allProducts", productTable.CreateQuery());
var t = await productTable.ToListAsync();
Console.WriteLine("Product Count : " + t.Count);
}
catch (Java.Net.MalformedURLException ex) {
Console.WriteLine(ex.Message);
}
catch (Exception e) {
Console.WriteLine(e.Message);
}
}
References:
http://azure.microsoft.com/en-us/documentation/articles/mobile-services-xamarin-android-get-started-offline-data/
http://blogs.msdn.com/b/carlosfigueira/archive/2014/04/07/deep-dive-on-the-offline-support-in-the-azure-mobile-service-managed-client-sdk.aspx
I got the solution for this case.
As far as my understanding, this is what is happening. During the application is loading, I call PullAsync. It is async call and during this call, application keeps loading other components. The actual NullReferenceException is coming from OnPrepareOptionsMenu function (Xamarin.Forms.Platform.Android.AndroidActivity.OnPrepareOptionsMenu). The exception is happening on other thread and the thread simply dies. That's why I cannot get stack trace from my main thread.
This NullReferenceException issue is totally not related to Azure Mobile Service.
I override OnPrepareOptionsMenu in MainActivity and add try-catch block to base class function call. The problem is solved. Here is my code in MainActivity class.
public override bool OnPrepareOptionsMenu(IMenu menu) {
try {
// I am always getting menu.HasVisibleItems = false in my app
if (menu != null && menu.HasVisibleItems) {
// Exception is happening when the following code is executed
var result = base.OnPrepareOptionsMenu(menu);
return result;
}
}
catch{
}
return true;
}
I don't really understand why it is happening. Please point me out if you have better understanding of this case and better solution.
I think my issue is same as this : http://forums.xamarin.com/discussion/23579/exception-whilte-trying-to-open-activity-from-xamarin-forms-page
Related
I have a webservice .Net core2 that has certain methods that send an email. I have it working fine using smtpclient.sendemailasync.
public async Task<bool> SendEmailAsync(MailMessage email)
{
try
{
if (string.IsNullOrWhiteSpace(emailFrom)) email.From = new MailAddress(emailFrom);
using (SmtpClient client = getSMTPClientInstance())
{
await client.SendMailAsync(email);
}
return true;
}
catch (Exception ex)
{
Log.Error(ex, "Error sending email in EmailService.SendEmailAsync");
return false;
}
}
The only issue is that some SMTP servers take a little too long to respond. I want to set up the email, queue it and return without waiting for the result.
Just using an unawaited async is out for 2 reasons;
It is not reliable to continue a method outside a request context in asp
I need access to the database context of my entity framework to write a log
I have to allow for external or internal SMTP (my client specifies), so a collection folder is not a possibility - at least not without a service that manages it.
How could I achieve this? Do I need to write a service that manages this? If so, how would I do that inside my .Net Core App, keeping in mind that the service also needs to access the EF context to write a log
UPDATE
There is plumbing available in .NetCore DI especially for this. Refer to my additional answer below. Use IServiceScopeFactory
You can call the RegisterAsyncTask method on the Page object. That will signal the ASP.NET runtime you want to make sure these are finished before terminating the request context:
Example:
public void Page_Load(object sender, EventArgs e)
{
RegisterAsyncTask(new PageAsyncTask(LoadSomeData));
}
public async Task LoadSomeData()
{
var clientcontacts = Client.DownloadStringTaskAsync("api/contacts");
var clienttemperature = Client.DownloadStringTaskAsync("api/temperature");
var clientlocation = Client.DownloadStringTaskAsync("api/location");
await Task.WhenAll(clientcontacts, clienttemperature, clientlocation);
var contacts = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Contact>>(await clientcontacts);
var location = Newtonsoft.Json.JsonConvert.DeserializeObject<string>(await clientlocation);
var temperature = Newtonsoft.Json.JsonConvert.DeserializeObject<string>(await clienttemperature);
listcontacts.DataSource = contacts;
listcontacts.DataBind();
Temparature.Text = temperature;
Location.Text = location;
}
https://www.hanselman.com/blog/TheMagicOfUsingAsynchronousMethodsInASPNET45PlusAnImportantGotcha.aspx
So, while I have marked an answer, there are a couple of options that are better solutions for my specific example. First is the option to use a library like hangfire to schedule tasks - although that is not technically an answer to the question.
The better solution in .net core is to use IServiceScopeFactory
With IServiceScopeFactory you can rescope a task so it doesnt go out of scope when the request is complete. I did the following directly in a controller (I later moved to using the hangfire approach, but this works). As you can see, the async task is fired off in a new unawaited thread while the controller code continues.
var task = Task.Run(async () =>
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var service = scope.ServiceProvider.GetRequiredService<ApprovalService>();
await service.sendResponseEmailAsync(approvalInfo.ApprovalId, userID, approvalInfo.emailTo, approvalInfo.ccTo);
}
});
I have an ASP.NET Core website, using EFCore.
I would like to do some work like logging to the database, but after having sent the response to the user in order to answer faster.
I could do it in a different thread, but due to async access of the DbContext I am not sure it is safe. Is there any recommended way to do that?
public async Task<IActionResult> Request([FromForm]RequestViewModel model, string returnUrl = null)
{
try
{
var newModel = new ResponseViewModel(model);
// Some work
return View("RequestView",newModel)
}
finally
{
// Some analysis on the request
// I would like to defer this part
await Log(model);
}
}
One of the reason is that I would like to call a web-service (geocoding), which is not needed to answer, but good to work on the log (I need the city/country of coordinates).
I see this has never been answered, but actually have a solution.
The simple solution:
public async Task<IActionResult> Request([FromForm]RequestViewModel model, string returnUrl = null)
{
try
{
var newModel = new ResponseViewModel(model);
// Some work
return View("RequestView",newModel)
}
finally
{
Response.OnCompleted(async () =>
{
// Do some work here
await Log(model);
});
}
}
The secure solution, as OnCompleted used to be called before the response being sent, so delaying the response:
public static void OnCompleted2(this HttpResponse resp, Func<Task> callback)
{
resp.OnCompleted(() =>
{
Task.Run(() => { try { callback.Invoke(); } catch {} });
return Task.CompletedTask;
});
}
and call Response.OnCompleted2(async () => { /* some async work */ })
Building on Jeans answer and a question and answer on the try - return - finally pattern, the try and finally blocks can be removed (if you don't really want to catch an exception).
This leads to the following code:
public async Task<IActionResult> Request([FromForm] RequestViewModel model, string returnUrl = null)
{
var newModel = new ResponseViewModel(model);
// Some work
Response.OnCompleted(async () =>
{
// Do some work here
await Log(model);
});
return View("RequestView", newModel);
}
There's no out of the box way to do what you want.
But, here's a possible approach:
Have a queue and a worker (thread or process)
Just before the request is sent back to the client, add a message in that queue
The worker will pick up that message at some point in the future, and process it.
Since the worked runs somewhere else and not on the request thread, the server can complete the request thread and the worker can do what's left.
Try using Hangfire. Hangfire is an easy way to perform background processing in .NET and .NET Core applications. No Windows Service or separate process required.
Backed by persistent storage. Open and free for commercial use.
You could do something like
var jobId = BackgroundJob.Enqueue(() => Log(model));
And here is my blog post on using HangFire in ASP.NET Core
Create a new class that inherits from ActionFilterAttribute, overwrite the OnResultExecuted method to perform the logging and then apply your attribute class to the controller actions you want to do logging.
I have problem getting a "CodeFluent.Runtime.CodeFluentDuplicateException" and i'm probably missing something fundamental.
However i first followed this blog about using Servicestack and codefluents and made my own template.
I have no problems to get entities but doing a put give me an exception mentioned.
Ok maybe i have done some wrong in my template so i took another approach looking for answers i found a "complete" project using Webapi and a template, ready to use. Generate ASP .NET Web API Controllers using Templates.
This generates all the controllers and seems to work. However i have the same exeption when using the "put".
This is an example of generated controller code for Put
public HttpResponseMessage Put([FromBody]Country value)
{
if (value == null || !value.Save())
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
return Request.CreateResponse(HttpStatusCode.OK, value);
}
This is how i use the controller above inside a Xamarin.Forms solution.
public async Task UpdateAsync(Country update, bool isNewItem=false)
{
HttpClient client = new HttpClient();
// RestUrl = http://developer.xamarin.com:8081/api/todoitems{0}
var uri = new Uri(string.Format(Constants.RestUrl2, update.Id));
try
{
var json = JsonConvert.SerializeObject(update);
var content = new StringContent(json, Encoding.UTF8, "application/json");
HttpResponseMessage response = null;
if (isNewItem)
{
response = await client.PostAsync(uri, content);
}
else
{
response = await client.PutAsync(uri, content);
}
if (response.IsSuccessStatusCode)
{
Debug.WriteLine(#" TodoItem successfully saved.");
}
}
catch (Exception ex)
{
Debug.WriteLine(#" ERROR {0}", ex.Message);
}
}
Any suggestions of what i'm missing?
Thanks for any help
//Greg
I got the answer from the softfluent support
"..
Another case of duplicate exception is when you enable optimistic concurrency and the RowVersion of the instance you are updating is null. In this case, the stored procedure will insert the row instead of updating it.
.."
Changeing the concurrencyMode=None did the trick
I am currently trying to convert the phone sample app from the official ADAL github repo to a caliburn.micro MVVM app. But there are so many moving parts backed into the code-behind to get along with WebAuthenticationBroker that I don't now how to push that into viewmodels and handle navigation correctly when the app is activated again after the broker did the sign-on. Since I am totally clueless at the moment there is no code to share yet.
I've been using MvvmLight along with ADAL in my Windows Phone 8.1 App. What you need to do is as soon as you get a token, you need to send the message using the Messenger pattern. All of those view models which require the token and have already subscribed to it will receive it. Here is something I've done in my app using MvvmLight. Remember that you need to have a ContinuationManager class and IWebContinuable interface to make the app work.
private async void Login()
{
AuthenticationResult result = null;
context = AuthenticationContext.CreateAsync("https://login.windows.net/<tenant-id>").GetResults();
try
{
//try to check if you can get the token without showing pop-up
result = await context.AcquireTokenSilentAsync("https://management.core.windows.net/", "<clientid>");
if(result.Status==AuthenticationStatus.ClientError)
{
bool exists = CheckInVault();
if(exists)
{
PasswordVault vault = new PasswordVault();
var tokenvault = vault.FindAllByResource("Token");
string RefreshToken = tokenvault[0].Password;
var refresh=await context.AcquireTokenByRefreshTokenAsync(RefreshToken, clientid);
vault.Remove(tokenvault[0]);
StoreToken(refresh);
}
else
{
context.AcquireTokenAndContinue("https://management.core.windows.net/", clientid, WebAuthenticationBroker.GetCurrentApplicationCallbackUri(), StoreToken);
}
}
else if(result != null && result.Status == AuthenticationStatus.Success)
{
// A token was successfully retrieved. Post the new To Do item
bool exists = CheckInVault();
if (exists)
{
PasswordVault vault = new PasswordVault();
var tokenvault = vault.FindAllByResource("Token");
vault.Remove(tokenvault[0]);
}
StoreToken(result);
}
//this method will be called when app is opened first time and pop-up appears
result=await context.AcquireTokenSilentAsync("https://management.core.windows.net/", "<client-id>");
}
catch(Exception e)
{
MessageDialog dialog = new MessageDialog("Error");
}
}
What I am doing here is - after acquiring the access token and reference token when the user signs up first, I store the refresh token in the PasswordVault, so as to get it in the future to enable single sign-on. ADAL actually does using its caching feature, but sometimes the single sign-on failed for me, hence using the PasswordVault to store the refresh token. After the authentication completes, I have a delegate to StoreToken function, where I actually store the new refresh token and send the access token to all the subscribers using the Messenger class in MvvmLight.
private void StoreToken(AuthenticationResult result)
{
try
{
var token = result.AccessToken;
Messenger.Default.Send<string>(token); //send the access token.
PasswordVault vault = new PasswordVault();
PasswordCredential credential = new PasswordCredential();
credential.UserName = result.AccessToken;
credential.Password = result.RefreshToken;
credential.Resource = "Token";
vault.Add(credential);
}
catch(Exception e)
{
}
}
I would recommend handling the navigation in the view models. Define a helper class like NavigationService:
public class NavigationService:INavigationService
{
private Frame _frame;
public Frame Frame
{
get
{
return _frame;
}
set
{
_frame = value;
_frame.Navigated+= OnFrameNavigated;
}
}
public void NavigateTo(string type)
{
Frame.Navigate(Type.GetType(type));
}
public void GoForward()
{
if (Frame.CanGoForward)
Frame.GoForward();
}
public void GoBack()
{
if (Frame.CanGoBack)
Frame.GoBack();
}
}
To navigate to a page from the view models, you use the NavigateTo(string) method as
NavigateTo("<fully qualified class name of the page you want to navigate to>,<assembly name>")
I would also suggest using a IoC container (MvvmLight gives you a ViewModelLocator class) so that you can maintain singleton instances of your view models and helpers like NavigationService. I haven't used the CaliburnMicro framework but I would assume there would be similar features for Messaging and Dependency Injection.
I need to do some connectivity simulations to see that my code handles various connectivity errors to Facebook. I want to be able to simulate 500s, timeouts etc.
The easiest way to do that is to use Fiddler, but it seems to not be working with HTTPS (I get 403s when I try).
Is ther a way to force the SDK to work with HTTP instead of HTTPS for debugging purposes?
Facebook C# SDK supports your scenario for mocking the entire HttpWebRequest and HttpWebResponse. In fact we actually use that internally in our unit tests so that every single line of the code in Facebook C# SDK actually gets executed and the result is always the same. https://github.com/facebook-csharp-sdk/facebook-csharp-sdk/blob/v5/Source/Facebook.Tests/TestExtensions.cs For now you will need to check these tests in v5 branch as we haven't yet migrated those tests to v6.
For v5, you will need to override the protected CreateHttpWebRequest method in FacebookClient.
Here is an example for v5 when there is no internet connection. There are three hidden classes HttpWebRequestWrapper, HttpWebResponseWrapper and WebExceptionWrapper that you will need to make use of.
public static void NoInternetConnection(this Mock<Facebook.FacebookClient> facebookClient, out Mock<HttpWebRequestWrapper> mockRequest, out Mock<WebExceptionWrapper> mockWebException)
{
mockRequest = new Mock<HttpWebRequestWrapper>();
mockWebException = new Mock<WebExceptionWrapper>();
var mockAsyncResult = new Mock<IAsyncResult>();
var request = mockRequest.Object;
var webException = mockWebException.Object;
var asyncResult = mockAsyncResult.Object;
mockRequest.SetupProperty(r => r.Method);
mockRequest.SetupProperty(r => r.ContentType);
mockRequest.SetupProperty(r => r.ContentLength);
mockAsyncResult
.Setup(ar => ar.AsyncWaitHandle)
.Returns((ManualResetEvent)null);
mockWebException
.Setup(e => e.GetResponse())
.Returns<HttpWebResponseWrapper>(null);
mockRequest
.Setup(r => r.GetResponse())
.Throws(webException);
mockRequest
.Setup(r => r.EndGetResponse(It.IsAny<IAsyncResult>()))
.Throws(webException);
AsyncCallback callback = null;
mockRequest
.Setup(r => r.BeginGetResponse(It.IsAny<AsyncCallback>(), It.IsAny<object>()))
.Callback<AsyncCallback, object>((c, s) =>
{
callback = c;
})
.Returns(() =>
{
callback(asyncResult);
return asyncResult;
});
var mockRequestCopy = mockRequest;
var mockWebExceptionCopy = mockWebException;
facebookClient.Protected()
.Setup<HttpWebRequestWrapper>("CreateHttpWebRequest", ItExpr.IsAny<Uri>())
.Callback<Uri>(uri =>
{
mockRequestCopy.Setup(r => r.RequestUri).Returns(uri);
mockWebExceptionCopy.Setup(e => e.Message).Returns(string.Format("The remote name could not be resolved: '{0}'", uri.Host));
})
.Returns(request);
}
You can then write your tests as below.
[Fact]
public void SyncWhenThereIsNotInternetConnectionAndFiddlerIsNotOpen_ThrowsWebExceptionWrapper()
{
var mockFb = new Mock<FacebookClient> { CallBase = true };
Mock<HttpWebRequestWrapper> mockRequest;
Mock<WebExceptionWrapper> mockWebException;
mockFb.NoInternetConnection(out mockRequest, out mockWebException);
Exception exception = null;
try
{
var fb = mockFb.Object;
fb.Get(_parameters);
}
catch (Exception ex)
{
exception = ex;
}
mockFb.VerifyCreateHttpWebRequest(Times.Once());
mockRequest.VerifyGetResponse();
mockWebException.VerifyGetReponse();
Assert.IsAssignableFrom<WebExceptionWrapper>(exception);
}
In v6 we have made mocking the HttpWebRequest and HttpWebResponse much easier.
Create your custom HttpWebRequest and HttpWebResponse by inheriting HttpWebRequestWrapper and HttpWebReponseWrapper.
Then change the default http web request factory for Facebook C# SDK. Here is the sample of the default factory.
FacebookClient.SetDefaultHttpWebRequestFactory(uri => new HttpWebRequestWrapper((HttpWebRequest)WebRequest.Create(uri)));
If you want to change the HttpWebRequestFactor per FacebookClient instance then use the following code.
var fb = new FacebookClient();
fb.HttpWebRequestFactory = uri=> new MyHttpWebRequestWrapper(uri);
Note: HttpWebRequestWrapper, HttpWebResponseWrapper, WebExceptionWrapper, FacebookClient.SetDefaultHttpWebRequestFactory and FacebookClient.HttpWebRequestFactory has the attribute [EditorBrowsable(EditorBrowsableState.Never)] so you might not see it in the intellisense.
Things like no internet connection that you mention should actually be a part of facebook c# sdk tests and not your app unit tests. The sdk should guarantee that when there is not internet conenction it always throws WebExceptionWrapper and your app unit tests should actually be handling the WebExceptionWrapper exception and not mocking the entire httpwebrequest and httpwebresponse.
I'd suggest you introduce another level of abstraction to your code and code to that abstraction rather than the implementation. Eg.
public interface IFacebookClient {
IEnumerable<Friend> GetFriends();
}
public class HttpsClient : IFacebookClient {
public IEnumerable<Friend> GetFriends() {
// Make a call out to the Facebook API, as per usual
};
}
In your consuming code you'd do something like;
public class ConsumingCode {
private IFacebookClient _client;
public ConsumingCode(IFacebookClient client) {
_client = client;
foreach (Friend friend in _client.GetFriends()) {
// Do something with each Friend
}
}
}
If you're using an IoC container this can all get wired up for you automatically. MVVM frameworks like Caliburn.Micro tend to support this as well.
Then when it comes to unit testing (or manual testing) you can change the implementation of your interface;
public class Http403Client : IFacebookClient {
public IEnumerable<Friend> GetFriends() {
throw new HttpException(403, "Forbidden");
}
}
Obviously this is just a mock up example but I think it demonstrates the concept that you want to implement.