This repository has been archived by the owner. It is now read-only.

UWP for IoT Core Not Working #7128

Closed
JeffMarqMetrix opened this Issue Dec 13, 2017 · 22 comments

Comments

Projects
None yet
5 participants
@JeffMarqMetrix
Copy link

JeffMarqMetrix commented Dec 13, 2017

I've created a Windows 10 IoT Core Background Application using the Visual Studio Template for IoT Core. I'm trying to create an Asp.Net Core web Api using Mvc and I'm getting an exception before the service is even started.

When initializing MVC in my configureservices method (services.AddMvc or services.AddMvcCore) I get a FileNotFoundException in System.Private.CoreLib.

@mkArtakMSFT

This comment has been minimized.

Copy link
Contributor

mkArtakMSFT commented Dec 13, 2017

Hi @JeffMarqMetrix, can you please provide more details about how exactly are you trying to configure ASP.NET Core ? Can you send us a repro?

@JeffMarqMetrix

This comment has been minimized.

Copy link

JeffMarqMetrix commented Dec 14, 2017

Yes. So the end goal is to run an Asp.Net Core web application with Web Api functionality and several pages. I'm attempting to run this on Windows 10 IoT Core. The examples for Raspberry Pi that I've seen are all running Asp.Net Core as a .Net Core Exe running on the device. I'm attempting to run the app as a Windows IoT Core Background Application via the templates available here. This is necessary for me to access the serial ports, since I cannot access them from a .Net Core application.

Also, the device I'm running IoT Core on is not an ARM device. It's x64 Windows IoT Core, running an Intel processor.

Side note: I did have to create my own server based on HttpListener. But aside from that, Mvc crashes the application before the server can ever get started.

When using the Visual Studio templates, the only code that needs to be added to the project to repro the issue, is the following:

StartupTask.cs:

using Windows.ApplicationModel.Background;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Builder;

namespace TestBackgroundApp
{
    public sealed class StartupTask : IBackgroundTask
    {
        public void Run(IBackgroundTaskInstance taskInstance)
        {
            IWebHost webHost = new WebHostBuilder()
                .UseKestrel()
                .UseUrls("http://*:80/")
                .UseStartup<Startup>()
                .Build();
            webHost.Run();
        }
    }
}

Startup.cs:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using System;

namespace TestBackgroundApp
{
    class Startup
    {

        public void ConfigureServices(IServiceCollection services)
        {
            services
                .AddMvc();
        }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app
                .UseMvcWithDefaultRoute();

            app.Run(async context =>
            {
                await context.Response.WriteAsync($"Hello world! {DateTime.Now}");
            });
        }

    }
}

Again, I had to create an HttpListenerServer in order to successfully receive and process requests from my IoT Background App. And that works fine if I remove the Mvc-related code. But since Kestrel doesn't work in IoT Background Apps, you will need to use the HttpListener I've created in order to fully test.

Also, I think you're only able to test this remotely on a device that's running Windows 10 IoT Core. These types of applications cannot be started by Visual Studio on a normal Windows machine.

TestIoTCoreAspNetCore.zip

@mkArtakMSFT

This comment has been minimized.

Copy link
Contributor

mkArtakMSFT commented Dec 14, 2017

@JeffMarqMetrix, can you also share the exception details you're getting? Stack trace?

@JeffMarqMetrix

This comment has been minimized.

Copy link

JeffMarqMetrix commented Dec 14, 2017

System.IO.FileNotFoundException
  HResult=0x80070002
  Message=Could not load file or assembly 'TestBackgroundApp, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified.
  Source=System.Private.CoreLib
  StackTrace:
   at System.Reflection.RuntimeAssembly.nLoad(AssemblyName fileName, String codeBase, RuntimeAssembly locationHint, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, IntPtr ptrLoadContextBinder)
   at System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef, RuntimeAssembly reqAssembly, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, IntPtr ptrLoadContextBinder)
   at System.Reflection.Assembly.Load(AssemblyName assemblyRef)
   at Microsoft.AspNetCore.Mvc.Internal.DefaultAssemblyPartDiscoveryProvider.DiscoverAssemblyParts(String entryPointAssemblyName)
   at Microsoft.Extensions.DependencyInjection.MvcCoreServiceCollectionExtensions.GetApplicationPartManager(IServiceCollection services)
   at Microsoft.Extensions.DependencyInjection.MvcCoreServiceCollectionExtensions.AddMvcCore(IServiceCollection services)
   at Microsoft.Extensions.DependencyInjection.MvcServiceCollectionExtensions.AddMvc(IServiceCollection services)
   at TestBackgroundApp.Startup.ConfigureServices(IServiceCollection services) in C:\Users\jscherrer\Documents\Visual Studio 2017\Projects\TestIoTCoreAspNetCore\TestBackgroundApp\Startup.cs:line 14
@JeffMarqMetrix

This comment has been minimized.

Copy link

JeffMarqMetrix commented Dec 14, 2017

Here are the exceptions from my output window:

Exception thrown: 'System.IO.FileNotFoundException' in System.Private.CoreLib.dll
An exception of type 'System.IO.FileNotFoundException' occurred in System.Private.CoreLib.dll but was not handled in user code
Could not load file or assembly 'TestBackgroundApp, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified.
@JeffMarqMetrix

This comment has been minimized.

Copy link

JeffMarqMetrix commented Dec 14, 2017

Maybe it's because the IoT Core Background Application underlying project type is actually a Windows Runtime Component? It just seems to me like it has something to do with reflection.

@mkArtakMSFT

This comment has been minimized.

Copy link
Contributor

mkArtakMSFT commented Dec 27, 2017

@glennc, do you have any contacts who can help us with this?

@danroth27

This comment has been minimized.

Copy link
Member

danroth27 commented Jan 8, 2018

Hi @JeffMarqMetrix,

We don't yet support running ASP.NET Core in a Windows 10 IoT Core Background Application. It's not something we currently test or support. It is on our radar to add this support, but we don't have a roadmap to share at this time. The issue tracking adding this support is aspnet/AspNetCore#2116 and we will update the issue once we have more concrete plans.

@danroth27 danroth27 closed this Jan 8, 2018

@los93sol

This comment has been minimized.

Copy link

los93sol commented Apr 9, 2018

@JeffMarqMetrix Even though this is unsupported it is something I also need for one of my projects and I'm also not satisfied with the workaround of splitting the project and deploying two separate binaries with some type of service bus to bridge the gap. FWIW, I started debugging this and found the cause of the FileNotFound exception is in ApplicationPartManager.PopulateDefaultParts. It tries to Load the assembly from the name of the application which doesn't appear it would work in the case of UWP since we don't end up with an exe or dll of the applications name, instead it appears there's a wrapper around it. I don't know much about UWP at this point though, learning as I dig through this issue and attempt to hack it to make it work :)

@los93sol

This comment has been minimized.

Copy link

los93sol commented Apr 9, 2018

So as it turns out, after hacking the whole assembly loading it just ends up in a PlatformNotSupported exception now, time to work through that one

@los93sol

This comment has been minimized.

Copy link

los93sol commented Apr 9, 2018

Looks like the last hurdle is with the host builder, it does some reflection to get the assembly when you call Build() on it, internally that calls BuildCommonServices() and it looks like that's where the last issue lies. Hacking around the AddMvc issue wasn't so bad, just had to manually initialize some things.

For anyone else trying to get this going, here is what I did in startup....

    public void ConfigureServices(IServiceCollection services)
    {
        var manager = new ApplicationPartManager();
        var entryAssembly = Assembly.GetExecutingAssembly();
        var assembliesProvider = new ApplicationAssembliesProvider();
        var applicationAssemblies = assembliesProvider.ResolveAssemblies(entryAssembly);

        foreach (var assembly in applicationAssemblies)
        {
            var partFactory = ApplicationPartFactory.GetApplicationPartFactory(assembly);
            foreach (var part in partFactory.GetApplicationParts(assembly))
            {
                manager.ApplicationParts.Add(part);
            }
        }

        services.AddSingleton(manager);
        services.AddMvc();
    }

I need to doublecheck, but there was one method that had to be publicly exposed, though I assume you could hack down to it with reflection to get at it just the same without modifying the actual code for Mvc the way I did. I'll do some more work on this and update with a workaround that doesn't require a code change.

@los93sol

This comment has been minimized.

Copy link

los93sol commented Apr 10, 2018

Here is a reflection version of the little startup hack so AddMvc() will work...

    public void ConfigureServices(IServiceCollection services)
    {
        var manager = new ApplicationPartManager();

        // Do some reflection to get at the internal stuff we need to override
        var assembliesProviderType = Assembly.Load("Microsoft.AspNetCore.Mvc.Core").GetType("Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationAssembliesProvider");
        var resolveAssembliesMethod = assembliesProviderType.GetMethod("ResolveAssemblies");

        // Now implement enough of the internal functionality for things to pass
        var assembliesProvider = Activator.CreateInstance(assembliesProviderType);
        var applicationAssemblies = (IEnumerable<Assembly>)resolveAssembliesMethod.Invoke(assembliesProvider, new object[] { Assembly.GetExecutingAssembly() });

        foreach (var assembly in applicationAssemblies)
        {
            var partFactory = ApplicationPartFactory.GetApplicationPartFactory(assembly);
            foreach (var part in partFactory.GetApplicationParts(assembly))
            {
                manager.ApplicationParts.Add(part);
            }
        }

        services.AddSingleton(manager);
        services.AddMvc();
    }
@los93sol

This comment has been minimized.

Copy link

los93sol commented Apr 11, 2018

Still trying to sort this out and was able to use the built in server working with the following....

public sealed class StartupTask : IBackgroundTask
{
    BackgroundTaskDeferral _deferral;

    public void Run(IBackgroundTaskInstance taskInstance)
    {
        _deferral = taskInstance.GetDeferral();

        var host = WebHost
            .CreateDefaultBuilder()
            .UseSetting("preventHostingStartup", "true")
            .UseStartup<Startup>()
            .UseUrls("http://*:80")
            .Build();

        host.Run();
    }
}

At this point things are pretty close to working, I can see 404's coming back from the UWP app from both localhost and remote requests. I think it's just some configuration missing at this point.

@los93sol

This comment has been minimized.

Copy link

los93sol commented Apr 11, 2018

If anyone can help me understand the flow of things from the request hitting IServer through to Mvc for routing that would be helpful

@los93sol

This comment has been minimized.

Copy link

los93sol commented Apr 11, 2018

Good news and bad news, the good news is I tracked down why routing is failing, the bad news is I am not sure how to work around it yet. UWP is not happy about a declaration like public class HomeController : Controller and complains about not deriving from System.Object. I had ignored this previously and just removed public from the declaration, but that breaks routing's ability to locate the controller in my assembly since it does not add the class unless it is public. I suppose a workaround here may be to implement my own ControllerFeatureProvider that can locate the controller class, but hoping someone else may have another idea.

@JeffMushmo

This comment has been minimized.

Copy link

JeffMushmo commented Apr 11, 2018

Hey @los93sol
I really appreciate all of the effort you've put into trying to solve this.

One idea might be to try defining your controllers in a class library, and referencing that class library from your main UWP project. The issue you're seeing is because UWP wants to make everything a Windows Runtime Component when it's at the app level like that.

@los93sol

This comment has been minimized.

Copy link

los93sol commented Apr 11, 2018

@JeffMushmo Thank you, I had to get a little hacky with it but that did the trick! Routing is now working and my breakpoint in the controller is being hit as well. Looks like there's some stuff in Razor now that I need to deal with

@los93sol

This comment has been minimized.

Copy link

los93sol commented Apr 11, 2018

Stuck again, this time it's Razor, it tries to reach back into my assembly to locate the views after it compiles them, and again, since we get a .winmd file it fails there. I was thinking I could just disable view compilation in the solution file, but that doesn't appear to be doing the trick. I also wasn't able to just drop the views in the class lib either, hmmmm

@los93sol

This comment has been minimized.

Copy link

los93sol commented Apr 11, 2018

Bit of an update, I'm having trouble getting view compilation to work, I think views will have to be precompiled, but the normal way of doing this via csproj doesn't seem to be working for UWP, any thoughts?

@los93sol

This comment has been minimized.

Copy link

los93sol commented Apr 12, 2018

I'm stuck so posting my repo, hopefully someone can pick this up and help finish it https://github.com/los93sol/UwpAspNetCore/

@los93sol

This comment has been minimized.

Copy link

los93sol commented Apr 23, 2018

I got it working, check my repo for a demo

@JeffMushmo

This comment has been minimized.

Copy link

JeffMushmo commented Apr 23, 2018

Wow. That's great news. I will be attempting this myself in the near future.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.