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.
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?
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:
```C#
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
.Build();
webHost.Run();
}
}
}
Startup.cs:
```C#
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.
@JeffMarqMetrix, can you also share the exception details you're getting? Stack trace?
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
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.
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.
@glennc, do you have any contacts who can help us with this?
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 https://github.com/aspnet/Home/issues/2116 and we will update the issue once we have more concrete plans.
@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 :)
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
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.
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();
}
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.
If anyone can help me understand the flow of things from the request hitting IServer through to Mvc for routing that would be helpful
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.
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.
@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
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
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?
I'm stuck so posting my repo, hopefully someone can pick this up and help finish it https://github.com/los93sol/UwpAspNetCore/
I got it working, check my repo for a demo
Wow. That's great news. I will be attempting this myself in the near future.
Most helpful comment
I got it working, check my repo for a demo