MarimerLLC / cslaforum

Discussion forum for CSLA .NET
https://cslanet.com
Other
31 stars 6 forks source link

Unable to make async calls from Android #862

Open Abenzio opened 4 years ago

Abenzio commented 4 years ago

I have a simple client application in Xamarin Forms that is talking to an App Server (Azure). When I run the UWP version of my application everything works fine. I can create new objects, fetch a list, fetch an individual object, etc. However, if I run the Android version, I cannot do any sort of dataportal fetch or create asynchronously. In Android I get the error: "Csla.DataPortalException: 'Unable to read beyond the end of the stream.'"

As an example, I have the simplest object I could come up with:

    [Serializable]
    public class FooInfo : ReadOnlyBase<FooInfo> {
        // Properties
        public static readonly PropertyInfo<int> IdProperty = RegisterProperty<int>(x => x.Id);
        public int Id {
            get => GetProperty(IdProperty);
            private set => LoadProperty(IdProperty, value);
        }

        public static readonly PropertyInfo<string> NameProperty = RegisterProperty<string>(x => x.Name);
        public string Name {
            get => GetProperty(NameProperty);
            private set => LoadProperty(NameProperty, value);
        }

        // Factory Methods
        public static async Task<FooInfo> GetFooInfoAsync() => await DataPortal.FetchAsync<FooInfo>();

        public static async Task<FooInfo> NewFooInfoAsync() => await DataPortal.CreateAsync<FooInfo>();

        // (Works)
        public static FooInfo NewFooInfo() => DataPortal.Create<FooInfo>();

        // (Works)
        public static FooInfo GetFooInfo() => DataPortal.Fetch<FooInfo>();

        // DataPortal
        private void DataPortal_Fetch() {
            Id = 1;
            Name = "Test Name";
        }

        private void DataPortal_Create()
        {
            Id = 2;
            Name = "New Name";
        }
    }

To call these factory methods I'm using this in my view model:

        private async Task FooTest()
        {
            var newFoo = await FooInfo.NewFooInfoAsync();

            //var getFoo = await FooInfo.GetFooInfoAsync();
        }

My app server has a basic setup with the HttpPortalController:

    [Route("api/[controller]")]
    [ApiController]
    public class DataPortalController : HttpPortalController {
        [HttpGet]
        public string Get() {
            return "Running";
        }
    }

Whenever I call an async method in Android it gives me the error:

Csla.DataPortalException: 'Unable to read beyond the end of the stream.'

And the stack trace from the error info from the data portal exception is:

at System.IO.BinaryReader.FillBuffer(Int32 numBytes) at System.IO.BinaryReader.ReadInt32() at Csla.Serialization.Mobile.CslaBinaryReader.Read(Stream serializationStream) at Csla.Server.Hosts.HttpPortalController.InvokePortal(String operation, Stream requestStream, Stream responseStream)

I've had this issue with 4.11.2, 5.0.0, and 5.0.1.

I hope it's just a misunderstanding on my part and I'm just missing some obvious piece of the CSLA setup, but I haven't been able to find it.

Any help or nudge in the right direction would be greatly appreciated.

Version and Platform CSLA version: 4.11.2, 5.0.0, 5.0.1 OS: Windows, Android Platform: Xamarin, Azure

rockfordlhotka commented 4 years ago

Where are you hosting the app server? You can not host it on localhost when using the Android emulator, because the emulator won't have access to your local machine.

I typically host my app server in Azure when building mobile apps, because that puts the app server into the cloud where it can be reached by my dev workstation, my emulators, and my real phone.

Abenzio commented 4 years ago

I'm hosting my app server on Azure. The DataPortalProxy and the DataPortalUrlString are both being set in the OnStart() of the App.cs.

I'm able to call DataPortal_Create and Get methods synchronously, and when I make a conventional web api call to a regular API controller using Android that works too.

It's only when I try to make async calls through CSLA to my Azure app server that android has an issue.

rockfordlhotka commented 4 years ago

Thank you for bringing this to my attention. There does appear to be a bug in (I think) the configuration subsystem, where ApplicationContext is being lost in Xamarin. That might not be the only issue - but at least in my initial attempt to replicate your issue I'm finding that the data portal proxy is being reset to "Local" before the data portal call.

This might be different from your issue - but I can't get to your issue because I can't get the data portal to actually make a remote call just at the moment.

rockfordlhotka commented 4 years ago

Whew! No problem with the configuration subsystem - just me being an idiot 😄

In ProjectTracker I wasn't configuring the data portal until the OnStart event, but that's too late, because I'm loading some data earlier in the app lifecycle. No wonder the configuration seemed wrong - it hadn't even occurred yet!!

Abenzio commented 4 years ago

Thank you for attempting to look into it.

I feel like this is going to end up being an issue on my end, but so far I haven't found exactly what I'm missing to bring it all together.

rockfordlhotka commented 4 years ago

I'm not sure what to suggest. ProjectTracker is now working fine, once I configured the data portal before trying to use the data portal, so I don't think there are any specific issues with the HttpProxy.

Are you trying to retrieve a particularly large object graph, or an object with a large amount of data?

Abenzio commented 4 years ago

No, it's a pretty small amount of data. Even that Foo example I showed in my post fails, and that's not doing anything but returning hardcoded values.

Maybe I have some sort of dependency that needs to be updated. I've looked over that, but I suppose I could have missed something.

Abenzio commented 4 years ago

I was able to find some time to look at this issue again.

It was just the HttpClient implementation settings for the iOS and Android projects. I set it to Default in Android and Managed in iOS to match the ProjectTracker project and that fixed the issue.

Well, full disclosure, I can't be 100% certain if that's the only thing I changed in iOS. I had a few issues getting it working after that, and I lost track of what I changed in the moment. The main point, however, is that the original error was fixed by adjusting the settings in my Android and iOS projects.

Right-Click the Android project > Properties > Android Options > Advanced > HttpClient Implementation > Default

Right-Click the iOS project > Properties > iOS Build > HttpClient Implementation > Managed

So, while this wasn't a CSLA issue, if someone else runs into this issue, this is how I fixed it, because the defaults for the Android and iOS projects in Xamarin.Forms will give you this issue.