Showing posts with label azure-mobile-services. Show all posts
Showing posts with label azure-mobile-services. Show all posts

Thursday, August 3, 2017

Why is Entity Framework generating the following nested SQL for Azure Mobile Services Table Controllers

Leave a Comment

I'm trying to get to the bottom of an entity Framework issue when using it with a TableController

I've created the following setup.

  1. The basic TodoItem example provided with a new Mobile Web Api which leverages EntityFramework, TableController & the default EntityDomainManager

    public class TodoItemController : TableController<TodoItem> {     protected override void Initialize(HttpControllerContext controllerContext)     {         base.Initialize(controllerContext);         context = new MobileServiceContext();         context.Database.Log += LogToDebug;         DomainManager = new EntityDomainManager<TodoItem>(context, Request);     }      public IQueryable<TodoItem> GetAllTodoItems()     {         var q = Query();         return q;     } 
  2. A vanilla Web API 2 controller.

    public class TodoItemsWebController : ApiController {      private MobileServiceContext db = new MobileServiceContext();     public TodoItemsWebController()     {         db.Database.Log += LogToDebug;     }      public IQueryable<TodoItem> GetTodoItems()     {         return db.TodoItems;     } 

I've gone through the tablecontroller code with a fine tooth comb, digging into the Query method, which is just proxying the call via the DomainManager to add in the Where(_ => !_.IsDeleted) modfiication to the IQueryable

Yet the two queries produce VERY different SQL.

For the regular Web API Controller you get the following SQL.

SELECT      [Extent1].[Id] AS [Id],      [Extent1].[Version] AS [Version],      [Extent1].[CreatedAt] AS [CreatedAt],      [Extent1].[UpdatedAt] AS [UpdatedAt],      [Extent1].[Deleted] AS [Deleted],      [Extent1].[Text] AS [Text],      [Extent1].[Complete] AS [Complete]     FROM [dbo].[TodoItems] AS [Extent1] 

But for the TableController, you get the following chunk of SQL which has a *Magic* Guid in the middle of it, and results in a Nested SQL statement. The performance of this goes to complete garbage when you start dealing with any of the ODATAv3 queries like $top, $skip, $filter and $expand.

SELECT TOP (51)      [Project1].[C1] AS [C1],      [Project1].[C2] AS [C2],      [Project1].[C3] AS [C3],      [Project1].[Complete] AS [Complete],      [Project1].[C4] AS [C4],      [Project1].[Text] AS [Text],      [Project1].[C5] AS [C5],      [Project1].[Deleted] AS [Deleted],      [Project1].[C6] AS [C6],      [Project1].[UpdatedAt] AS [UpdatedAt],      [Project1].[C7] AS [C7],      [Project1].[CreatedAt] AS [CreatedAt],      [Project1].[C8] AS [C8],      [Project1].[Version] AS [Version],      [Project1].[C9] AS [C9],      [Project1].[Id] AS [Id]     FROM ( SELECT          [Extent1].[Id] AS [Id],          [Extent1].[Version] AS [Version],          [Extent1].[CreatedAt] AS [CreatedAt],          [Extent1].[UpdatedAt] AS [UpdatedAt],          [Extent1].[Deleted] AS [Deleted],          [Extent1].[Text] AS [Text],          [Extent1].[Complete] AS [Complete],          1 AS [C1],          N'804f84c6-7576-488a-af10-d7a6402da3bb' AS [C2],          N'Complete' AS [C3],          N'Text' AS [C4],          N'Deleted' AS [C5],          N'UpdatedAt' AS [C6],          N'CreatedAt' AS [C7],          N'Version' AS [C8],          N'Id' AS [C9]         FROM [dbo].[TodoItems] AS [Extent1]     )  AS [Project1]     ORDER BY [Project1].[Id] ASC 

You can see the results of both queries here. https://pastebin.com/tSACq6eg

So my questions are:

  • Why is the TableController generating the SQL in this way?

  • What is the *magic* guid in the middle of the query? (it will stay the same until I stop and restart the app so I don't know if it's session, client or DB context specific)

  • Where exactly in the pipeline is the TableController making these Modifications to the IQueryable? I assume it's done through some middleware step, or an on executed attribute later in the request after the Query() method is called, but I cannot for the life of me find it.

2 Answers

Answers 1

According to your description, I did some research and found that Azure Mobile Server SDK uses the following code line under TableControllerConfigProvider.cs for adding additional query related filters for the same actions with the QueryableAttribute for enabling a controller action to support OData query parameters.

controllerSettings.Services.Add(typeof(IFilterProvider), new TableFilterProvider()); 

Note: the additional filters would be executed after your action has been executed and return the IQueryable.

You could check EnableQueryAttribute.cs and found that OnActionExecuted would call the ExecuteQuery method and eventually call ODataQueryOptions.ApplyTo for applying OData query options ($filter, $orderby, $top, $skip, and $inlinecount, etc.) to the given IQueryable.

Per my understanding, the Nested SQL statement is generated by OData component. After invoked ODataQueryOptions.ApplyTo, your IQueryable has been modified and the related sql statement has been modified too. I did some test in my regular Web API Controller as follows, you could refer to it:

Request:

Get http://localhost:58971/api/todoitem?$top=2&$select=Text,Id,Version 

Before applying OData query options:

enter image description here

After applied OData query options:

enter image description here

Answers 2

Your one of the tables being synced between the backend and client because if that your are getting 2nd sql.

Read more here : https://documentation.devexpress.com/wpf/17927/Common-Concepts/Scaffolding-Wizard/Tutorials/Building-Outlook-Inspired-and-Hybrid-UI-Applications/Lesson-3-Customize-Layout-of-the-Collection-Views

Read More

Friday, June 9, 2017

Why does Navigation.PushAsync crash after Azure MobileServiceClient LoginAsync()?

Leave a Comment

Edit: Sample project that demonstrates crash can be found here: https://github.com/rringham/brokenazurexamforms - you need to set your own Azure App Service URL in:

  • src/BrokenAzureForms/Droid/Services/User/DroidUserService.cs
  • src/BrokenAzureForms/iOS/Services/User/IosUserService.cs

I'm seeing Xamarin Forms's Navigation.PushAsync() crash on Android when I attempt to use it after authenticating with Azure MobileServiceClient. This crash is isolated to Android - it does not happen on iOS.

Here's the setup - I've got a basic NavigationPage as my main app page:

MainPage = new NavigationPage(new LoginPage()); 

On my LoginPage, I authenticate using a DependencyService-injected class that performs authentication in my Android project:

private async void OnMicrosoftAccountTapped(object sender, EventArgs args) {     IUserService userService = DependencyService.Get<IUserService>();     bool authenticated = await userService.LoginWithAzureAD();     if (authenticated)     {         await Navigation.PushAsync(new HomePage(), false);     }  } 

In my Android implementation of IUserService, I do this (pretty much exactly what the Azure / Xamarin Forms tutorials show):

public async Task<bool> LoginWithAzureAD() {     try     {         _user = await _client.LoginAsync(Xamarin.Forms.Forms.Context, MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory);     }     catch (Exception)     {         return false;     }      return true; } 

Here's where things fall apart. When LoginWithAzureAD() is done, control resumes in OnMicrosoftAccountTapped(); we then go to call Navigation.PushAsync(), and boom - the app crashes, with very little detail to go on:

MobileServiceClient / Navigation crash

All I can think is that Azure MobileServiceClient is doing something pretty funky with Xamarin.Forms.Forms.Context internally, because if I remove the call to await userService.LoginWithAzureAD(), the call to Navigation.PushAsync() works with no issues. Something in MobileServiceClient is either broken, or is breaking something in Xamarin Forms.

Anyone see anything like this?

2 Answers

Answers 1

When I do this, I use the following:

In MainActivity.cs:

// Initialize for Azure Mobile Apps Microsoft.WindowsAzure.MobileServices.CurrentPlatform.Init();  // Initialize for Xamarin Forms global::Xamarin.Forms.Forms.Init(this, savedInstanceState);  // Initialize for Login Provider var lp = (DroidLoginProvider)DependencyService.Get<ILoginProvider>(); lp.Initialize(this); 

Then, in my DroidLoginProvider class, I do the following:

Context _context;  public void Initialize(Context context) {     this._context = context; }  public async Task LoginAsync(MobileServiceClient client) {     await client.LoginAsync(this._context, MobileServiceAuthenticationProvider.whatever); } 

I call the LoginAsync from a Singleton wrapper in my shared project. It's important that it is a Singleton because there should only be one MobileServiceClient in a project - the authentication is stored in the MobileServiceClient.CurrentUser property and is only set on the current client.

You can see a working project with this logic here: https://github.com/adrianhall/30-days-of-zumo-v2/tree/master/file-upload

Answers 2

I think the login issue is orthogonal, and you're calling PushAsync from a background thread. You should just await the call to your dependency service method from your main thread, and then do PushAsync there.

Here's a sample:

Read More

Saturday, July 2, 2016

Cannot load System.Net.Http.Primitives in Xamarin iOS

Leave a Comment

I have an older Xamarin forms project that I've updated, that uses Microsoft Azure's Mobile App Services (previously used Azure Mobile Services).

Was working fine, but after a clean and rebuild I now get this error whenever attempting to run it (I'm using Xamarin Studio)

Could not load file or assembly 'System.Net.Http.Primitives, Version=1.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. 

Microsoft.Azure.Mobile.Client and SQLiteStore are at version 2.0.1. I've tried removing and readding the assembly via NuGet, without any luck.

Has anyone seen this issue or resolved it?

2 Answers

Answers 1

Xamarin iOS (monotouch) depends on System.Net.Http.Primitives version 4.2.29.0, while version 1.5.0.0 is for platform portable-net40+sl4+win8+wp71+wpa81. As Eric Hedstrom commented, have you tried the steps from this answer?

Answers 2

Solution here wound up being recreating the Xamarin projects, and relinking the portable class library of the old project to it.

Read More

Wednesday, March 30, 2016

Azure MobileServicePushFailedException on Android

Leave a Comment

I am trying to build an Application with offline sync and followed the tutorial on Azure

Now the offline storage works well, but no items are added to on the server with the following exception:

       Error syncAsync com.microsoft.windowsazure.mobileservices.table.sync.push.MobileServicePushFailedException  java.util.concurrent.ExecutionException: com.microsoft.windowsazure.mobileservices.table.sync.push.MobileServicePushFailedException      at com.google.common.util.concurrent.AbstractFuture$Sync.getValue(AbstractFuture.java:299)      at com.google.common.util.concurrent.AbstractFuture$Sync.get(AbstractFuture.java:286)      at com.google.common.util.concurrent.AbstractFuture.get(AbstractFuture.java:116)      at irisrecognition.example.com.irisrecognition.util.ItemManager$5.doInBackground(ItemManager.java:237)      at irisrecognition.example.com.irisrecognition.util.ItemManager$5.doInBackground(ItemManager.java:232)      at android.os.AsyncTask$2.call(AsyncTask.java:288)      at java.util.concurrent.FutureTask.run(FutureTask.java:237)      at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)      at java.lang.Thread.run(Thread.java:818)  Caused by: com.microsoft.windowsazure.mobileservices.table.sync.push.MobileServicePushFailedException      at com.microsoft.windowsazure.mobileservices.table.sync.MobileServiceSyncContext.pushOperations(MobileServiceSyncContext.java:939)      at com.microsoft.windowsazure.mobileservices.table.sync.MobileServiceSyncContext.consumePushSR(MobileServiceSyncContext.java:834)      at com.microsoft.windowsazure.mobileservices.table.sync.MobileServiceSyncContext.access$1100(MobileServiceSyncContext.java:85)      at com.microsoft.windowsazure.mobileservices.table.sync.MobileServiceSyncContext$PushSyncRequestConsumer.run(MobileServiceSyncContext.java:1127) 

Here is the code for onCreate()

  try {         mClient = new MobileServiceClient(                 Constants.ROOT_URL,                 "kfogvaexzeDLYyPbRmBiHxQEBUYpku30",                 this).withFilter(new ProgressFilter());          initLocalStore().get();         syncAsync();         refreshItemsFromTable();      } catch (MalformedURLException e) {         e.printStackTrace();     } catch (Exception e) {         e.printStackTrace();     } 

and some more...

private AsyncTask<Void, Void, Void> initLocalStore() throws MobileServiceLocalStoreException, ExecutionException, InterruptedException {          AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {             @Override             protected Void doInBackground(Void... params) {                 try {                     mPullQuery = mClient.getTable(IrisEntry.class).where().orderBy("__createdAt", QueryOrder.Descending);                      MobileServiceSyncContext syncContext = mClient.getSyncContext();                     SQLiteLocalStore localStore = new SQLiteLocalStore(mClient.getContext(), "OfflineStore", null, 1);                     SimpleSyncHandler handler = new SimpleSyncHandler();                      Map<String, ColumnDataType> tableDefinition = new HashMap<String, ColumnDataType>();                     tableDefinition.put("id", ColumnDataType.String);                     tableDefinition.put("text", ColumnDataType.String);                     tableDefinition.put("device", ColumnDataType.String);                     tableDefinition.put("segmentationAlgo", ColumnDataType.String);                     tableDefinition.put("imageUri", ColumnDataType.String);                     tableDefinition.put("containerName", ColumnDataType.String);                     tableDefinition.put("resourceName", ColumnDataType.String);                     tableDefinition.put("sasQueryString", ColumnDataType.String);                     tableDefinition.put("userId", ColumnDataType.String);                     tableDefinition.put("complete", ColumnDataType.Boolean);                      localStore.defineTable(Constants.TABLE_IRIS, tableDefinition);                     syncContext.initialize(localStore, handler).get();                      mIrisTable = mClient.getSyncTable(IrisEntry.class);                 } catch (final Exception e) {                     e.printStackTrace();                 }                  return null;             }         };          return runAsyncTask(task);     }    public void syncAsync() {         if (isNetworkAvailable()) {             new AsyncTask<Void, Void, Void>() {                  @Override                 protected Void doInBackground(Void... params) {                     try {                         mClient.getSyncContext().push().get();                         mIrisTable.pull(mPullQuery).get();                         Log.e(LOGTAG, "Success syncAsync");                      } catch (Exception e) {                         Log.e(LOGTAG, "Error syncAsync " + e.getMessage());                         e.printStackTrace();                      }                     return null;                 }             }.execute();         } else {             Log.e(LOGTAG, "You are not online, re-sync later!");         }     }      private boolean isNetworkAvailable() {         ConnectivityManager connectivityManager                 = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);         NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();         return activeNetworkInfo != null && activeNetworkInfo.isConnected();     } 

Permissions for my table are set to allow with Application Key. Any ideas?

EDIT:

This appears in the log file on the server:

ERROR { [Error: getaddrinfo ENOTFOUND] code: 'ENOTFOUND', errno: 'ENOTFOUND', syscall: 'getaddrinfo' } 

1 Answers

Answers 1

I am not sure if this is the answer but does "Constants.ROOT_URL" include the protocol header string like "http://myurl.com" ? The ENOTFOUND error means the address of the getaddrinfo call was not able to be resolved. Youmay want to try "myurl.com", without the protocol portion of the URI. Also the method requires a class type Uri for the argument. It looks like you might be getting the stack trace from the MalformedURIException handler. (The call is probably using https even if you specified something else).

Read More